<!DOCTYPE html><html lang="en"><head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1,viewport-fit=cover"><meta name="theme-color" content="#000000"><meta name="apple-mobile-web-app-status-bar-style" content="black-translucent"><title>Daniel Rodríguez [sadasant]</title><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=Space+Grotesk:wght@300;400;500;600;700&display=swap" rel="stylesheet"><style>:root{--bg:#000;--text:#fff;--accent:#ff2d3c;--dim:#777;--steel:#333;--card-bg:rgba(255, 255, 255, 0.03)}*{margin:0;padding:0;box-sizing:border-box;-webkit-tap-highlight-color:transparent}body,html{overflow-x:hidden;background:var(--bg)}@supports (height:100dvh){:root{--vh-full:100dvh}}@supports not (height:100dvh){:root{--vh-full:100vh}}html{scroll-behavior:smooth;font-size:clamp(14px, 1.5vw, 18px)}body{background:var(--bg);color:var(--text);font-family:'Space Grotesk',sans-serif;overflow-x:hidden;min-height:150vh;line-height:1.6}#canvas{position:fixed;top:0;left:0;width:100%;height:100%;z-index:1}.bio-screen{position:fixed;top:0;left:0;width:100%;height:var(--vh-full);z-index:10;display:flex;flex-direction:column;justify-content:center;align-items:center;pointer-events:none;opacity:1;transition:opacity .6s ease;padding:env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left)}.bio-screen.hidden{opacity:0}.bio-avatar{width:clamp(140px,20vw,200px);height:clamp(140px,20vw,200px);border-radius:50%;border:3px solid var(--accent);box-shadow:0 0 60px rgba(255,45,60,.25),0 0 120px rgba(255,45,60,.1);margin-bottom:2rem;object-fit:cover;opacity:0;transform:scale(.3);filter:blur(10px);animation:avatarEmerge 1.2s cubic-bezier(.34,1.56,.64,1) .3s forwards}@keyframes avatarEmerge{0%{opacity:0;transform:scale(.3);filter:blur(10px);box-shadow:0 0 60px rgba(255,45,60,0),0 0 120px rgba(255,45,60,0)}50%{opacity:1;filter:blur(0px)}70%{transform:scale(1.08);box-shadow:0 0 80px rgba(255,45,60,.5),0 0 160px rgba(255,45,60,.3)}100%{opacity:1;transform:scale(1);filter:blur(0px);box-shadow:0 0 60px rgba(255,45,60,.25),0 0 120px rgba(255,45,60,.1)}}.bio-name{opacity:0;transform:translateY(20px);animation:textReveal .8s ease-out .8s forwards}.bio-handle{opacity:0;transform:translateY(20px);animation:textReveal .8s ease-out 1s forwards}.bio-text{opacity:0;transform:translateY(20px);animation:textReveal .8s ease-out 1.2s forwards}.scroll-hint{opacity:0;animation:textReveal .8s ease-out 1.6s forwards}@keyframes textReveal{to{opacity:1;transform:translateY(0)}}.scroll-hint{animation:textReveal .8s ease-out 1.6s forwards,pulse 2s ease-in-out 2.4s infinite}.bio-name{font-size:clamp(2.5rem, 6vw, 4.5rem);font-weight:700;letter-spacing:-.03em;margin-bottom:.5rem}.bio-handle{color:var(--accent);font-size:clamp(1.1rem, 2.5vw, 1.5rem);font-weight:500;margin-bottom:2rem;letter-spacing:.05em}.bio-text{max-width:600px;text-align:center;color:var(--dim);font-size:clamp(1rem, 2vw, 1.25rem);font-weight:300;line-height:1.8;padding:0 2rem}.scroll-hint{position:absolute;bottom:3rem;font-size:.875rem;color:var(--dim);letter-spacing:.2em;text-transform:uppercase}.scroll-hint::after{content:'';display:block;width:1px;height:40px;background:linear-gradient(to bottom,var(--dim),transparent);margin:1rem auto 0}@keyframes pulse{0%,100%{opacity:.4}50%{opacity:1}}.triptych{position:fixed;top:0;left:0;width:100%;height:var(--vh-full);z-index:5;display:grid;grid-template-columns:1fr 1fr 1fr;gap:0;padding:0;padding:env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);pointer-events:none}.triptych.visible{pointer-events:auto}.triptych-column{opacity:0;filter:blur(8px)}.triptych-column:first-child{transform:translateX(-100px) scale(.95);transition:none}.triptych-column:nth-child(2){transform:translateY(60px) scale(.95);transition:none}.triptych-column:nth-child(3){transform:translateX(100px) scale(.95);transition:none}.triptych.visible .triptych-column:first-child{opacity:1;transform:translateX(0) scale(1);filter:blur(0px);transition:opacity .7s cubic-bezier(.16, 1, .3, 1) 0s,transform .7s cubic-bezier(.16, 1, .3, 1) 0s,filter .5s ease .1s,background 1.2s ease-out}.triptych.visible .triptych-column:nth-child(2){opacity:1;transform:translateY(0) scale(1);filter:blur(0px);transition:opacity .7s cubic-bezier(.16, 1, .3, 1) .15s,transform .7s cubic-bezier(.16, 1, .3, 1) .15s,filter .5s ease .25s,background 1.2s ease-out}.triptych.visible .triptych-column:nth-child(3){opacity:1;transform:translateX(0) scale(1);filter:blur(0px);transition:opacity .7s cubic-bezier(.16, 1, .3, 1) .3s,transform .7s cubic-bezier(.16, 1, .3, 1) .3s,filter .5s ease .4s,background 1.2s ease-out}.triptych-column{display:flex;flex-direction:column;justify-content:center;gap:clamp(2rem,4vw,3rem);padding:clamp(3rem,6vw,5rem) clamp(2rem,4vw,4rem);border-left:1px solid var(--steel);position:relative;background:0 0;height:100%}.triptych.visible .triptych-column:hover{background:rgba(0,0,0,.5);transition:opacity .7s cubic-bezier(.16, 1, .3, 1),transform .7s cubic-bezier(.16, 1, .3, 1),filter .5s ease,background .15s ease-out!important}.triptych.visible .triptych-column.struck{background:rgba(0,0,0,.6);transition:opacity .7s cubic-bezier(.16, 1, .3, 1),transform .7s cubic-bezier(.16, 1, .3, 1),filter .5s ease,background 80ms ease-out!important}.triptych-column:first-child{border-left:none}.section-title{font-size:clamp(.75rem, 2vw, .875rem);font-weight:600;color:var(--accent);letter-spacing:.3em;text-transform:uppercase;margin-bottom:1.5rem;position:relative;padding-bottom:1rem}.section-title::after{content:'';position:absolute;bottom:0;left:0;width:3rem;height:2px;background:var(--accent)}.links{display:flex;flex-wrap:wrap;gap:.75rem}.links a{color:var(--text);text-decoration:none;padding:.875rem 1.5rem;border:2px solid var(--steel);font-size:clamp(.875rem, 1.5vw, 1rem);font-weight:600;letter-spacing:.05em;transition:all .3s ease}.links a:hover{background:var(--accent);border-color:var(--accent);color:var(--bg);transform:translateY(-2px)}.triptych-bio{color:var(--dim);font-size:clamp(.9rem, 1.5vw, 1rem);font-weight:300;line-height:1.8;margin-top:1rem}.triptych-bio a{color:var(--accent);text-decoration:none;font-weight:400}.triptych-bio a:hover{text-decoration:underline}.cards{display:flex;flex-direction:column;gap:1rem}.card{padding:clamp(1.25rem,2.5vw,1.75rem);background:var(--card-bg);border-left:3px solid var(--steel);transition:all .3s ease}.card:hover{border-color:var(--accent);background:rgba(255,45,60,.05);transform:translateX(4px)}.card a{color:var(--text);text-decoration:none;font-size:clamp(1rem, 1.8vw, 1.2rem);font-weight:600;display:block;margin-bottom:.5rem;line-height:1.4}.card a:hover{color:var(--accent)}.card .meta{font-size:clamp(.75rem, 1.2vw, .875rem);color:var(--dim);font-weight:400}.card .card-excerpt,.card .repo-desc{display:block;margin-bottom:.25rem;line-height:1.5}.card .card-date,.card .repo-stats{opacity:.7}.book-card{display:flex;gap:1rem;align-items:flex-start}.book-cover{width:50px;height:auto;border-radius:2px;flex-shrink:0;filter:grayscale(100%) contrast(1.2) brightness(1.1);box-shadow:0 0 8px rgba(180,180,200,.3),0 0 20px rgba(140,140,160,.15),0 2px 8px rgba(0,0,0,.4);border:1px solid rgba(255,255,255,.1);transition:all .3s ease}.book-cover:hover{filter:grayscale(60%) contrast(1.3) brightness(1.2);box-shadow:0 0 12px rgba(200,200,220,.4),0 0 30px rgba(160,160,180,.2),0 4px 12px rgba(0,0,0,.5)}.book-info{display:flex;flex-direction:column;gap:.25rem}.book-title{font-size:clamp(.9rem, 1.5vw, 1.1rem);font-weight:500;color:var(--text);line-height:1.4}.book-author{font-size:clamp(.75rem, 1.2vw, .875rem);color:var(--dim);font-weight:400}.card span{font-size:clamp(1rem, 1.8vw, 1.2rem);color:var(--text);font-weight:500;line-height:1.4}.repo-grid{display:flex;flex-wrap:wrap;gap:.5rem}.repo-chip{display:flex;flex-direction:column;gap:.15rem;padding:.6rem .9rem;background:var(--card-bg);border-left:2px solid var(--steel);text-decoration:none;transition:all .2s ease;min-width:0;flex:1 1 calc(50% - 0.5rem);max-width:calc(50% - .25rem)}.repo-chip:hover{border-color:var(--accent);background:rgba(255,45,60,.05)}.repo-name{color:var(--text);font-size:clamp(.8rem, 1.3vw, .95rem);font-weight:500;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.repo-chip:hover .repo-name{color:var(--accent)}.repo-meta{color:var(--dim);font-size:clamp(.65rem, 1vw, .75rem);font-weight:400}@media (max-width:768px){.repo-chip{flex:1 1 100%;max-width:100%}}.misc-content{font-size:clamp(1rem, 1.5vw, 1.1rem);color:var(--dim);line-height:1.8}.misc-content a{color:var(--accent);text-decoration:none;font-weight:500}.misc-content a:hover{text-decoration:underline}.sticky-avatar{position:fixed;top:1.5rem;left:1.5rem;z-index:100;width:48px;height:48px;border-radius:50%;border:2px solid var(--accent);box-shadow:0 0 20px rgba(255,45,60,.3);opacity:0;transform:scale(.8);pointer-events:none;transition:opacity .4s ease,transform .4s ease}.sticky-avatar.visible{opacity:1;transform:scale(1);pointer-events:auto}.toggle-mode{position:fixed;top:1.5rem;right:1.5rem;z-index:100;background:rgba(0,0,0,.9);border:2px solid var(--steel);color:var(--dim);padding:.75rem 1.25rem;font-family:'Space Grotesk',sans-serif;font-size:.75rem;font-weight:600;letter-spacing:.15em;cursor:pointer;transition:all .3s ease;text-transform:uppercase}.toggle-mode:hover{border-color:var(--accent);color:var(--text)}body.default-mode #canvas{display:none}body.default-mode .bio-screen{position:relative;height:auto;min-height:var(--vh-full);padding:6rem 2rem;padding-top:calc(6rem + env(safe-area-inset-top));padding-bottom:calc(6rem + env(safe-area-inset-bottom))}body.default-mode .bio-screen.hidden{display:flex}body.default-mode .scroll-hint{display:none}body.default-mode .sticky-avatar{display:none}body.default-mode .bio-avatar,body.default-mode .bio-handle,body.default-mode .bio-name,body.default-mode .bio-text,body.default-mode .scroll-hint{opacity:1;transform:none;filter:none;animation:none}body.default-mode .triptych{position:relative;pointer-events:auto;min-height:var(--vh-full);height:auto;padding-bottom:env(safe-area-inset-bottom)}body.default-mode .triptych-column{opacity:1;transform:none;filter:none;transition:background .3s ease}body.default-mode{min-height:auto}@media (max-width:1024px){.triptych{grid-template-columns:1fr 1fr;height:auto;min-height:var(--vh-full);overflow-y:auto;-webkit-overflow-scrolling:touch}.triptych-column:nth-child(3){grid-column:span 2;border-left:none;border-top:1px solid var(--steel)}.triptych-column:first-child{transform:translateX(-60px) scale(.97)}.triptych-column:nth-child(3){transform:translateX(60px) scale(.97)}}@media (max-width:768px){html{font-size:clamp(15px, 4vw, 18px)}body{min-height:auto}.triptych{position:relative;grid-template-columns:1fr;height:auto;min-height:auto}.triptych-column{border-left:none;border-bottom:1px solid var(--steel);padding:clamp(2.5rem,6vw,4rem) clamp(1.5rem,5vw,2rem);height:auto}.triptych-column:last-child{border-bottom:none;padding-bottom:calc(2rem + env(safe-area-inset-bottom))}.triptych-column:nth-child(3){grid-column:auto;border-top:none}.triptych-column:first-child,.triptych-column:nth-child(3){transform:translateX(0) translateY(40px) scale(.98)}.triptych-column:nth-child(2){transform:translateY(40px) scale(.98)}.triptych-column{filter:blur(4px)}.triptych.visible .triptych-column:first-child,.triptych.visible .triptych-column:nth-child(2),.triptych.visible .triptych-column:nth-child(3){filter:blur(0px)}.links{gap:.5rem;display:grid;grid-template-columns:1fr 1fr}.links a{padding:.75rem .5rem;text-align:center;min-height:44px;font-size:clamp(.75rem, 3vw, .875rem)}.section-title{margin-bottom:1.25rem}.bio-avatar{width:120px;height:120px}.bio-name{font-size:2.25rem}.scroll-hint{bottom:calc(2rem + env(safe-area-inset-bottom))}.bio-screen{position:relative;min-height:var(--vh-full);height:auto;transition:opacity .3s ease}.triptych-column:first-child{padding-top:calc(5rem + env(safe-area-inset-top))}.bio-screen.hidden{opacity:0;pointer-events:none;transition:opacity .3s ease}.triptych{pointer-events:auto}.triptych .triptych-column{opacity:1;transform:none;filter:none;transition:none}.sticky-avatar{top:calc(1rem + env(safe-area-inset-top));left:1rem;width:40px;height:40px}.toggle-mode{top:calc(1rem + env(safe-area-inset-top));right:1rem;padding:.6rem 1rem;font-size:.65rem}}@media (max-width:480px){.links{grid-template-columns:1fr 1fr}.links a{min-height:48px;padding:.75rem .25rem;font-size:.7rem}.card{padding:1rem;min-height:44px}.bio-text{padding:0 1rem}}@media (hover:none) and (pointer:coarse){.triptych.visible .triptych-column:hover{background:0 0}.links a:hover{transform:none}.card:hover{transform:none}}</style></head><body><canvas id="canvas"></canvas><img src="https://www.gravatar.com/avatar/d4309fc28933672ade396db9f936aa07?s=96" alt="Daniel Rodriguez" class="sticky-avatar" id="stickyAvatar"><div class="bio-screen" id="bioScreen"><img src="https://www.gravatar.com/avatar/d4309fc28933672ade396db9f936aa07?s=400" alt="Daniel Rodriguez" class="bio-avatar"><h1 class="bio-name">Daniel Rodriguez</h1><div class="bio-handle">@sadasant</div><p class="bio-text">I'm a technical architect and writer based in Miami. My work spans distributed systems, AI infrastructure, and the organizational structures that surround them—I keep finding the places where technical problems turn out to be coordination problems in disguise.</p><div class="scroll-hint">Scroll</div></div><div class="triptych" id="triptych"><div class="triptych-column"><div><h2 class="section-title">Connect</h2><div class="links"><a href="https://X.com/sadasant">X</a> <a href="https://github.com/sadasant">GitHub</a> <a href="https://linkedin.com/in/sadasant">LinkedIn</a> <a href="https://medium.com/sadasant">Medium</a> <a href="https://soundcloud.com/sadasant">SoundCloud</a> <a href="https://berserk.tech/">Berserk</a> <a href="https://handle.me/dr">$dr</a> <a href="#" id="email-link" class="email-link">Email</a></div></div><div><h2 class="section-title">About</h2><p class="triptych-bio">I'm a technical architect and writer based in Miami. My work spans distributed systems, AI infrastructure, and the organizational structures that surround them—I keep finding the places where technical problems turn out to be coordination problems in disguise. I write about mathematics, economics, and the texture of complex systems at <a href="https://medium.com/sadasant">Medium</a>. Previously at Microsoft, Anaconda, and various startups in various states of becoming.</p></div></div><div class="triptych-column"><div><h2 class="section-title">Writing</h2><div class="cards" id="medium-posts"><div class="card"><span class="meta">Loading...</span></div></div></div><div><h2 class="section-title">Reading</h2><div class="cards" id="goodreads-updates"><div class="card"><span class="meta">Loading...</span></div></div></div></div><div class="triptych-column"><div><h2 class="section-title">Code</h2><div class="cards" id="github-activity"><div class="card"><span class="meta">Loading...</span></div></div></div><div><h2 class="section-title">Misc</h2><p class="misc-content">sadasant: A misspelling of <a href="https://www.learnsanskrit.cc/translate?search=sadasat">सदसत्</a> by 10-year-old me.</p></div></div></div><button class="toggle-mode" id="toggleMode">Default</button><script>const canvas=document.getElementById("canvas"),gl=canvas.getContext("webgl")||canvas.getContext("experimental-webgl");let webglEnabled=!!gl;gl||(console.warn("WebGL not supported, falling back to default mode"),document.body.classList.add("default-mode"));let width=window.innerWidth,height=window.innerHeight;const isMobile=/Android|webOS|iPhone|iPad|iPod/i.test(navigator.userAgent),scale=isMobile?.6:1;function resize(){width=window.innerWidth,height=window.innerHeight,gl&&(canvas.width=width*scale,canvas.height=height*scale,gl.viewport(0,0,canvas.width,canvas.height))}resize(),window.addEventListener("resize",resize);const vertexShaderSource="\n      attribute vec2 a_position;\n      void main() {\n        gl_Position = vec4(a_position, 0.0, 1.0);\n      }\n    ",fragmentShaderSource="\n      precision highp float;\n      uniform vec2 u_resolution;\n      uniform float u_time;\n      uniform vec2 u_mouse;\n      uniform float u_pulse;\n      uniform vec2 u_lightCenter;\n      uniform float u_lightIntensity;\n      uniform float u_pourProgress;\n      uniform vec2 u_orbCenter;\n      uniform float u_redLightActive;\n      uniform float u_calmFactor;\n      uniform float u_entityStir; // Entity stirring from below (0-1)\n\n      float hash(vec2 p) {\n        return fract(sin(dot(p, vec2(12.9898, 78.233))) * 43758.5453);\n      }\n\n      float noise(vec2 p) {\n        vec2 i = floor(p);\n        vec2 f = fract(p);\n        f = f * f * (3.0 - 2.0 * f);\n        float a = hash(i);\n        float b = hash(i + vec2(1.0, 0.0));\n        float c = hash(i + vec2(0.0, 1.0));\n        float d = hash(i + vec2(1.0, 1.0));\n        return mix(mix(a, b, f.x), mix(c, d, f.x), f.y);\n      }\n\n      float fbm(vec2 p) {\n        float value = 0.0;\n        float amplitude = 0.5;\n        float frequency = 1.0;\n        for(int i = 0; i < 5; i++) {\n          value += amplitude * noise(p * frequency);\n          frequency *= 2.0;\n          amplitude *= 0.5;\n        }\n        return value;\n      }\n\n      vec2 curl(vec2 p) {\n        float eps = 0.1;\n        float n1 = fbm(p + vec2(eps, 0.0));\n        float n2 = fbm(p - vec2(eps, 0.0));\n        float n3 = fbm(p + vec2(0.0, eps));\n        float n4 = fbm(p - vec2(0.0, eps));\n        return vec2(n3 - n4, n2 - n1) / (2.0 * eps);\n      }\n\n      void main() {\n        vec2 uv = gl_FragCoord.xy / u_resolution;\n\n        // ========================================\n        // FLUID TEXTURE - Always fullscreen, always flowing\n        // Calmer when all systems synced\n        // ========================================\n        float timeScale = mix(1.0, 0.4, u_calmFactor); // Slower when calm\n        float flowScale = mix(0.5, 0.25, u_calmFactor); // Less turbulent when calm\n\n        vec2 p = uv * 3.0;\n        vec2 flow = curl(p + u_time * 0.1 * timeScale);\n        vec2 flowRaw = curl(p + u_time * 0.1); // Uncalmed flow for the red light entity\n        p += flow * flowScale;\n\n        float n1 = fbm(p * 1.5 + vec2(sin(u_time * 0.12 * timeScale), cos(u_time * 0.15 * timeScale)) * 2.0);\n        float n2 = fbm(p * 3.0 + vec2(cos(u_time * 0.08 * timeScale), sin(u_time * 0.11 * timeScale)) * 1.5 + vec2(100.0));\n        float n3 = fbm(p * 0.8 + vec2(sin(u_time * 0.05 * timeScale), -cos(u_time * 0.06 * timeScale)) * 1.0 + vec2(200.0));\n\n        float combined = n1 * 0.5 + n2 * 0.3 + n3 * 0.2;\n\n        vec2 displacement = curl(p * 2.0 + vec2(cos(u_time * 0.06), sin(u_time * 0.07)) * 0.8) * 0.3;\n        float displaced = fbm(p * 1.2 + displacement);\n        combined = mix(combined, displaced, 0.6);\n\n        float sharpened = pow(combined, 1.2);\n        float boundary1 = smoothstep(0.38, 0.42, sharpened) * (1.0 - smoothstep(0.42, 0.46, sharpened));\n        float boundary2 = smoothstep(0.54, 0.58, sharpened) * (1.0 - smoothstep(0.58, 0.62, sharpened));\n        float boundary3 = smoothstep(0.28, 0.32, sharpened) * (1.0 - smoothstep(0.32, 0.36, sharpened));\n        sharpened += (boundary1 + boundary2 + boundary3) * 0.15;\n        combined = smoothstep(0.25, 0.75, sharpened);\n\n        // Surface shading\n        float greyVariation = fbm(p * 2.5 + u_time * 0.06 + vec2(1000.0));\n        greyVariation = greyVariation * 0.6 + 0.4;\n\n        float opacityNoise = fbm(p * 4.0 + u_time * 0.08);\n        float opacity = smoothstep(0.2, 0.8, combined * (0.85 + opacityNoise * 0.3));\n\n        float eps = 0.02;\n        float dx = fbm(p + vec2(eps, 0.0) + vec2(sin(u_time * 0.12), cos(u_time * 0.15)) * 2.0) -\n                  fbm(p - vec2(eps, 0.0) + vec2(sin(u_time * 0.12), cos(u_time * 0.15)) * 2.0);\n        float dy = fbm(p + vec2(0.0, eps) + vec2(sin(u_time * 0.12), cos(u_time * 0.15)) * 2.0) -\n                  fbm(p - vec2(0.0, eps) + vec2(sin(u_time * 0.12), cos(u_time * 0.15)) * 2.0);\n\n        vec3 normal = normalize(vec3(-dx * 3.0, -dy * 3.0, 1.0));\n        vec3 lightDir = normalize(vec3(0.4, 0.3, 1.0));\n        float surfaceLighting = max(0.0, dot(normal, lightDir));\n        surfaceLighting = pow(surfaceLighting, 2.0);\n\n        float heightGradient = smoothstep(0.0, 1.0, uv.y);\n        heightGradient = pow(heightGradient, 1.5);\n\n        float dissolve = fbm(vec2(uv.x * 3.0, uv.y * 1.5) + u_time * 0.05);\n        dissolve = smoothstep(0.3, 0.7, dissolve);\n        heightGradient *= (0.6 + dissolve * 0.4);\n        surfaceLighting *= heightGradient;\n\n        float lightVariation = fbm(p * 5.0 - u_time * 0.12 + vec2(600.0));\n        lightVariation = smoothstep(0.4, 0.8, lightVariation);\n        surfaceLighting *= lightVariation;\n\n        // ========================================\n        // TOPOGRAPHIC TO LIQUID TRANSITION\n        // Start: sharp contour lines (#999 on black)\n        // End: full liquid with all gradients\n        // ========================================\n\n        // Reveal progress\n        float topoToLiquid = smoothstep(0.0, 0.7, u_pourProgress);  // Main transition\n        float fullReveal = smoothstep(0.5, 1.0, u_pourProgress);    // Final richness\n\n        // ========================================\n        // INITIAL STATE: Dim, low-contrast liquid\n        // ========================================\n\n        // Very low contrast version of the liquid shape\n        // Just subtle undulations, not sharp lines\n        float dimLiquid = smoothstep(0.3, 0.7, combined);  // Soft gradient\n\n        // Initial color: very dim grey, low contrast\n        // Max brightness ~0.15 (#262626), fading to black\n        vec3 initialColor = vec3(dimLiquid * 0.15);\n\n        // ========================================\n        // FULL LIQUID COLORS\n        // ========================================\n\n        vec3 pureBlack = vec3(0.0);\n        vec3 surfaceBase = vec3(32.0, 36.0, 42.0) / 255.0;\n        vec3 surfaceHighlight = vec3(102.0, 102.0, 102.0) / 255.0;\n        vec3 surfaceDark = vec3(8.0, 8.0, 12.0) / 255.0;\n\n        float highlightIntensity = surfaceLighting * heightGradient;\n\n        vec3 liquidColor = mix(surfaceDark, surfaceBase, opacity * 0.7);\n        liquidColor = mix(liquidColor, liquidColor * (1.0 + heightGradient * 0.4), 0.5);\n        liquidColor = mix(liquidColor, surfaceHighlight, highlightIntensity * 0.5);\n        liquidColor *= greyVariation * 0.9;\n\n        // Highlights\n        float highlights = fbm(p * 5.0 - u_time * 0.12 + vec2(600.0));\n        highlights = smoothstep(0.85, 0.98, highlights) * highlightIntensity;\n        liquidColor += vec3(highlights * 0.25);\n\n        // ========================================\n        // BLEND: Dim initial → Full liquid\n        // ========================================\n\n        // Start with dim grey, transition to full liquid\n        vec3 surfaceColor = mix(initialColor, liquidColor, topoToLiquid);\n\n        // Add depth as full reveal completes\n        surfaceColor = mix(surfaceColor, surfaceColor * (1.0 + heightGradient * 0.2), fullReveal);\n\n        vec3 color = surfaceColor;\n\n        // ========================================\n        // ENTITY STIR (something vast notices from below)\n        // Not a spotlight - a massive presence barely felt\n        // ========================================\n        if (u_entityStir > 0.001) {\n          vec3 redLight = vec3(255.0, 45.0, 60.0) / 255.0;\n\n          // Very slow rise and sink - something huge moves slowly\n          // Stir 0→0.4: rising, 0.4→1.0: sinking back\n          float risePhase = smoothstep(0.0, 0.4, u_entityStir);\n          float sinkPhase = smoothstep(0.4, 1.0, u_entityStir);\n\n          // Extra smooth easing - glacial, inevitable movement\n          float easedRise = risePhase * risePhase * risePhase; // cubic ease-in\n          float easedSink = 1.0 - pow(1.0 - sinkPhase, 3.0); // cubic ease-out\n\n          // Position: barely crests the bottom of the screen\n          // Something huge - its \"top\" just barely becomes visible\n          float entityY = -0.6 + easedRise * 0.35 - easedSink * 0.35;\n\n          // Very subtle drift - it's so big it barely moves horizontally\n          float xDrift = sin(u_time * 0.1) * 0.03;\n          vec2 presenceCenter = vec2(0.5 + xDrift, entityY);\n\n          // Intensity: very faint, like sensing something in peripheral vision\n          // Peaks around 0.15-0.2, never bright\n          float stirIntensity = easedRise * (1.0 - easedSink) * 0.18;\n\n          // === VAST DIFFUSE PRESENCE (not a focused spotlight) ===\n\n          vec2 toPresence = uv - presenceCenter;\n\n          // Refraction through fluid - the presence distorts through the liquid\n          vec2 flowDisplacement = flowRaw * 0.12;\n          toPresence += flowDisplacement;\n\n          // VERY wide spread - this thing is enormous\n          // Horizontal spread much wider than vertical (like looking at the top of something huge)\n          vec2 scaledOffset = vec2(toPresence.x * 0.3, toPresence.y * 0.8);\n          float distToPresence = length(scaledOffset);\n\n          // Extremely soft falloff - no hard edges, just a vast gradient\n          float presenceRadius = 1.2;\n          float presenceFalloff = 1.0 - smoothstep(0.0, presenceRadius, distToPresence);\n          presenceFalloff = pow(presenceFalloff, 0.6); // Even softer falloff\n\n          // Modulate by fluid - the presence is felt THROUGH the liquid\n          float fluidDensity = combined;\n          float transmission = mix(0.3, 0.8, 1.0 - fluidDensity * 0.5);\n\n          // Very subtle turbulence - massive things don't flicker\n          float turbulence = fbm(uv * 1.5 + flowDisplacement * 0.3 + u_time * 0.01);\n\n          // Final presence intensity\n          float presenceMask = presenceFalloff * transmission * stirIntensity;\n          presenceMask *= (0.95 + turbulence * 0.05); // Barely any flicker\n\n          // The glow - deep, diffuse, eerie\n          // No hotspots, no bright center - just an even, unsettling warmth\n          vec3 presenceGlow = redLight * presenceMask * (1.0 - opacity * 0.4);\n\n          // Slight color shift at the edges - darker red, almost brown\n          float edgeDarkening = smoothstep(0.0, 0.8, distToPresence);\n          presenceGlow *= mix(vec3(1.0), vec3(0.7, 0.5, 0.5), edgeDarkening * 0.3);\n\n          color += presenceGlow;\n        }\n\n        // ========================================\n        // RED LIGHT (Phase D only)\n        // ========================================\n        if (u_redLightActive > 0.01) {\n          vec3 redLight = vec3(255.0, 45.0, 60.0) / 255.0;\n\n          vec2 spotCenter = u_lightCenter;\n          float pulseBoost = u_pulse;\n          float anglePhase = fbm(vec2(u_time * 0.05, 0.0));\n          float isPointingAtViewer = smoothstep(0.3, 0.7, anglePhase);\n\n          vec2 toSpot = uv - spotCenter;\n          float angle = u_time * 0.08;\n          mat2 rotation = mat2(cos(angle), -sin(angle), sin(angle), cos(angle));\n          vec2 rotatedOffset = rotation * toSpot;\n\n          vec2 flowDisplacement = flowRaw * 0.08; // Red light uses uncalmed flow - it's independent\n          rotatedOffset += flowDisplacement;\n\n          float ellipseStretch = mix(2.5, 1.2, isPointingAtViewer);\n          vec2 ellipticalOffset = vec2(rotatedOffset.x * ellipseStretch, rotatedOffset.y);\n          float distToSpot = length(ellipticalOffset);\n\n          float spotRadius = mix(0.7, 0.9, isPointingAtViewer);\n          spotRadius += pulseBoost * 0.08;\n\n          float spotFalloff = 1.0 - smoothstep(0.0, spotRadius, distToSpot);\n          spotFalloff = pow(spotFalloff, 1.2);\n\n          float hotspotSize = mix(0.25, 0.15, isPointingAtViewer);\n          hotspotSize += pulseBoost * 0.03;\n\n          float hotspot = 1.0 - smoothstep(0.0, hotspotSize, distToSpot);\n          hotspot = pow(hotspot, 2.5);\n          hotspot *= (1.0 + pulseBoost * 0.2);\n\n          float fluidDensity = combined;\n          float lightTransmission = mix(0.4, 1.0, 1.0 - fluidDensity * 0.4);\n          float intensityBoost = 1.0 + pulseBoost * 0.15;\n          float spatialMask = spotFalloff * lightTransmission * (0.6 + hotspot * 0.4) * intensityBoost;\n\n          float turbulence = fbm(uv * 2.0 + flowDisplacement * 0.5 + u_time * 0.02);\n          spatialMask *= (0.9 + turbulence * 0.1);\n          spatialMask *= u_lightIntensity * u_redLightActive;\n\n          float transmission = (1.0 - opacity) * smoothstep(0.1, 0.5, spatialMask);\n\n          float redHotSpot = fbm(p * 1.5 + u_time * 0.03 + vec2(700.0));\n          redHotSpot = pow(smoothstep(0.5, 0.9, redHotSpot), 2.0);\n\n          float backIntensity = transmission * (1.1 + redHotSpot * 1.5 * smoothstep(0.2, 0.6, spatialMask));\n          vec3 backlight = redLight * backIntensity;\n\n          if (backIntensity > 0.6) {\n            backlight.g += (backIntensity - 0.6) * 0.2;\n          }\n\n          color += backlight;\n\n          float glowRadius = spatialMask * 0.08;\n          vec3 atmosphericGlow = redLight * glowRadius * (1.0 - opacity * 0.6);\n          color += atmosphericGlow;\n        }\n\n        // Film grain - fades in with transition\n        float grain = hash(gl_FragCoord.xy + u_time * 0.1);\n        grain = (grain - 0.5) * 0.04;\n        float grainMask = 0.5 + opacity * 0.5;\n        color += grain * grainMask * 0.5 * topoToLiquid;\n\n        gl_FragColor = vec4(color, 1.0);\n      }\n    ";let program,resolutionLocation,timeLocation,mouseLocation,pulseLocation,lightCenterLocation,lightIntensityLocation,pourProgressLocation,orbCenterLocation,redLightActiveLocation,calmFactorLocation,entityStirLocation;if(gl){function createShader(e,t,n){const i=e.createShader(t);return e.shaderSource(i,n),e.compileShader(i),e.getShaderParameter(i,e.COMPILE_STATUS)?i:(console.error(e.getShaderInfoLog(i)),e.deleteShader(i),null)}const e=createShader(gl,gl.VERTEX_SHADER,vertexShaderSource),t=createShader(gl,gl.FRAGMENT_SHADER,fragmentShaderSource);program=gl.createProgram(),gl.attachShader(program,e),gl.attachShader(program,t),gl.linkProgram(program),gl.getProgramParameter(program,gl.LINK_STATUS)||console.error(gl.getProgramInfoLog(program)),gl.useProgram(program);const n=new Float32Array([-1,-1,1,-1,-1,1,1,1]),i=gl.createBuffer();gl.bindBuffer(gl.ARRAY_BUFFER,i),gl.bufferData(gl.ARRAY_BUFFER,n,gl.STATIC_DRAW);const o=gl.getAttribLocation(program,"a_position");gl.enableVertexAttribArray(o),gl.vertexAttribPointer(o,2,gl.FLOAT,!1,0,0),resolutionLocation=gl.getUniformLocation(program,"u_resolution"),timeLocation=gl.getUniformLocation(program,"u_time"),mouseLocation=gl.getUniformLocation(program,"u_mouse"),pulseLocation=gl.getUniformLocation(program,"u_pulse"),lightCenterLocation=gl.getUniformLocation(program,"u_lightCenter"),lightIntensityLocation=gl.getUniformLocation(program,"u_lightIntensity"),pourProgressLocation=gl.getUniformLocation(program,"u_pourProgress"),orbCenterLocation=gl.getUniformLocation(program,"u_orbCenter"),redLightActiveLocation=gl.getUniformLocation(program,"u_redLightActive"),calmFactorLocation=gl.getUniformLocation(program,"u_calmFactor"),entityStirLocation=gl.getUniformLocation(program,"u_entityStir")}let time=0,isAnimating=!0,lastTime=performance.now(),mouseX=.5,mouseY=.5,prevMouseX=.5,prevMouseY=.5,mouseVelocity=0,pourProgress=0,targetPourProgress=0,redLightActive=0,targetRedLightActive=0,calmFactor=0,targetCalmFactor=0,entityStir=0,entityStirActive=!1,entityStirStartTime=0;const STIR_DURATION=8e3;let lightState="absent",lightStateStartTime=0,lightIntensity=0,lightCenterX=.5,lightCenterY=.5,lightTargetX=.5,lightTargetY=.5,entryEdge="top",entryOffset=.5,transitionDuration=5e3,detectionLevel=0,lastMovementTime=0,pulse=0,pulseState="idle",pulseStartTime=0,pulsePeakIntensity=1,pulseDecayTime=2e3,pulseOvershoot=1.3,pulseAttackTime=150,pulseDelayTime=200,currentPhase="A",lastScrollY=0,scrollingDown=!0,systemsLoaded={medium:!1,goodreads:!1,github:!1},allSystemsSynced=!1,stirQueue=0,isStirring=!1;const HEARTBEAT_INTERVAL=9e3;function queueStir(){"A"===currentPhase&&(stirQueue++,isStirring||processHeartbeat())}function cancelStirs(){stirQueue=0,entityStirActive=!1,entityStir=0,isStirring=!1}function processHeartbeat(){stirQueue<=0?isStirring=!1:(isStirring=!0,stirQueue--,entityStirActive=!0,entityStirStartTime=performance.now(),setTimeout(processHeartbeat,9e3))}function updateEntityStir(e){if(!entityStirActive)return void(entityStir=0);const t=(e-entityStirStartTime)/8e3;t>=1?(entityStir=0,entityStirActive=!1):entityStir=t}function checkAllSynced(){systemsLoaded.medium&&systemsLoaded.goodreads&&systemsLoaded.github&&(allSystemsSynced=!0,targetCalmFactor=1)}const bioScreen=document.getElementById("bioScreen"),triptych=document.getElementById("triptych"),stickyAvatar=document.getElementById("stickyAvatar");function updatePhaseFromScroll(){if(document.body.classList.contains("default-mode"))return;const e=window.scrollY,t=e/window.innerHeight;if(scrollingDown=e>lastScrollY,lastScrollY=e,t<.02)currentPhase="A",targetPourProgress=0,targetRedLightActive=0,bioScreen.classList.remove("hidden"),triptych.classList.remove("visible"),stickyAvatar.classList.remove("visible");else if(t<.25){"A"===currentPhase&&cancelStirs(),currentPhase="B",targetPourProgress=(t-.02)/.23;targetRedLightActive=.5*((t-.02)/.23),bioScreen.classList.add("hidden"),triptych.classList.remove("visible"),stickyAvatar.classList.remove("visible")}else if(t<.35){currentPhase="C",targetPourProgress=1;targetRedLightActive=.5+.5*((t-.25)/.1),bioScreen.classList.add("hidden"),triptych.classList.add("visible"),stickyAvatar.classList.add("visible")}else currentPhase="D",targetPourProgress=1,targetRedLightActive=1,bioScreen.classList.add("hidden"),triptych.classList.add("visible"),stickyAvatar.classList.add("visible")}function updateMousePosition(e,t){prevMouseX=mouseX,prevMouseY=mouseY,mouseX=e/window.innerWidth,mouseY=1-t/window.innerHeight;const n=mouseX-prevMouseX,i=mouseY-prevMouseY;mouseVelocity=Math.sqrt(n*n+i*i),mouseVelocity>.001&&(lastMovementTime=performance.now())}function easeOutCubic(e){return 1-Math.pow(1-e,3)}function easeInOutCubic(e){return e<.5?4*e*e*e:1-Math.pow(-2*e+2,3)/2}function exponentialDecay(e){return Math.exp(2*-e)}function chooseEntryEdge(){const e=["top","right","bottom","left"];return e[Math.floor(Math.random()*e.length)]}function getEdgePosition(e,t){switch(e){case"top":return{x:entryOffset,y:.7*t-.2};case"bottom":return{x:entryOffset,y:1.2-.7*t};case"left":return{x:.7*t-.2,y:entryOffset};case"right":return{x:1.2-.7*t,y:entryOffset}}}window.addEventListener("scroll",updatePhaseFromScroll),updatePhaseFromScroll(),document.addEventListener("mousemove",e=>updateMousePosition(e.clientX,e.clientY)),document.addEventListener("touchmove",e=>{e.touches.length>0&&updateMousePosition(e.touches[0].clientX,e.touches[0].clientY)});let smoothLightIntensity=0;function updateLightEntity(e){if(("A"===currentPhase||"B"===currentPhase)&&("absent"!==lightState&&"withdrawing"!==lightState&&(lightState="withdrawing",lightStateStartTime=e),"withdrawing"===lightState||"absent"===lightState))return void(pulse=0);"withdrawing"===lightState&&(lightState="absent",lightStateStartTime=e);const t=e-lightStateStartTime;switch(mouseVelocity*=.85,detectionLevel*=.98,("searching"===lightState||"investigating"===lightState)&&mouseVelocity>.002&&e-lastMovementTime<100&&(detectionLevel=Math.min(1,detectionLevel+20*mouseVelocity)),lightState){case"absent":if(lightIntensity=0,pulse=0,detectionLevel=0,t>8e3+7e3*Math.random()){lightState="entering",lightStateStartTime=e,transitionDuration=5e3+3e3*Math.random(),entryEdge=chooseEntryEdge(),entryOffset=.3+.4*Math.random();const t=getEdgePosition(entryEdge,0);lightCenterX=t.x,lightCenterY=t.y,lightTargetX=.3+.4*Math.random(),lightTargetY=.3+.4*Math.random()}break;case"entering":const n=Math.min(1,t/transitionDuration);lightIntensity=easeInOutCubic(n);const i=getEdgePosition(entryEdge,n);lightCenterX=i.x+(lightTargetX-i.x)*easeInOutCubic(.7*n),lightCenterY=i.y+(lightTargetY-i.y)*easeInOutCubic(.7*n),t>=transitionDuration&&(lightState="searching",lightStateStartTime=e);break;case"searching":lightIntensity=1;const o=1e-4*e;lightTargetX=.5+.2*Math.sin(1.3*o),lightTargetY=.5+.2*Math.cos(.9*o),lightCenterX+=.001*(lightTargetX-lightCenterX),lightCenterY+=.001*(lightTargetY-lightCenterY),detectionLevel>.3&&(lightState="investigating",lightStateStartTime=e,lightTargetX=mouseX,lightTargetY=mouseY),t>15e3+1e4*Math.random()&&(lightState="leaving",lightStateStartTime=e,transitionDuration=5e3+3e3*Math.random(),entryEdge=chooseEntryEdge(),entryOffset=.3+.4*Math.random());break;case"investigating":lightIntensity=1,lightCenterX+=.002*(lightTargetX-lightCenterX),lightCenterY+=.002*(lightTargetY-lightCenterY);Math.sqrt(Math.pow(lightCenterX-lightTargetX,2)+Math.pow(lightCenterY-lightTargetY,2))<.15&&"idle"===pulseState&&(pulseState="delay",pulseStartTime=e,pulsePeakIntensity=.6+.2*Math.random(),pulseDecayTime=3e3+2e3*Math.random(),pulseOvershoot=1.05+.1*Math.random(),pulseAttackTime=300,pulseDelayTime=500),detectionLevel>.5&&e-lastMovementTime<500&&(lightTargetX=mouseX,lightTargetY=mouseY,lightStateStartTime=e),t>8e3+7e3*Math.random()&&detectionLevel<.2&&(lightState="leaving",lightStateStartTime=e,transitionDuration=5e3+3e3*Math.random(),entryEdge=chooseEntryEdge(),entryOffset=.3+.4*Math.random());break;case"leaving":const a=Math.min(1,t/transitionDuration);lightIntensity=1-easeInOutCubic(a);const s=getEdgePosition(entryEdge,1-a);lightCenterX=s.x,lightCenterY=s.y,t>=transitionDuration&&(lightState="absent",lightStateStartTime=e,pulse=0,pulseState="idle")}updatePulse(e)}function updatePulse(e){const t=e-pulseStartTime;switch(pulseState){case"delay":pulse=0,t>=pulseDelayTime&&(pulseState="attack",pulseStartTime=e);break;case"attack":const n=Math.min(1,t/pulseAttackTime);pulse=easeOutCubic(n)*pulseOvershoot*pulsePeakIntensity,t>=pulseAttackTime&&(pulseState="decay",pulseStartTime=e);break;case"decay":pulse=pulseOvershoot*pulsePeakIntensity*exponentialDecay(t/pulseDecayTime),(pulse<.01||t>=3*pulseDecayTime)&&(pulse=0,pulseState="idle");break;default:pulse=0}}function render(){if(!isAnimating||!webglEnabled||document.body.classList.contains("default-mode"))return void requestAnimationFrame(render);const e=performance.now();lastTime=e,time+=.016,pourProgress+=.06*(targetPourProgress-pourProgress);const t=targetRedLightActive-redLightActive;redLightActive+=t>0?.06*t:.008*t;const n=("withdrawing"===lightState?0:lightIntensity)-smoothLightIntensity;smoothLightIntensity+=n>0?.05*n:.008*n,calmFactor+=.02*(targetCalmFactor-calmFactor),updateEntityStir(e),updateLightEntity(e),gl.uniform2f(resolutionLocation,canvas.width,canvas.height),gl.uniform1f(timeLocation,time),gl.uniform2f(mouseLocation,mouseX,mouseY),gl.uniform1f(pulseLocation,pulse),gl.uniform2f(lightCenterLocation,lightCenterX,lightCenterY),gl.uniform1f(lightIntensityLocation,smoothLightIntensity),gl.uniform1f(pourProgressLocation,pourProgress),gl.uniform2f(orbCenterLocation,.5,.58),gl.uniform1f(redLightActiveLocation,redLightActive),gl.uniform1f(calmFactorLocation,calmFactor),gl.uniform1f(entityStirLocation,entityStir),gl.drawArrays(gl.TRIANGLE_STRIP,0,4),requestAnimationFrame(render)}document.addEventListener("visibilitychange",()=>{isAnimating=!document.hidden,isAnimating&&(lastTime=performance.now(),requestAnimationFrame(render))}),lightStateStartTime=performance.now(),webglEnabled&&render();const toggleBtn=document.getElementById("toggleMode");function stripHtml(e){const t=document.createElement("div");return t.innerHTML=e,t.textContent||t.innerText||""}function truncate(e,t){return e.length<=t?e:e.slice(0,t).trim()+"…"}async function loadMediumPosts(){try{const e=await fetch("https://api.rss2json.com/v1/api.json?rss_url=https://medium.com/feed/sadasant"),t=await e.json(),n=t.items?.slice(0,3)||[];document.getElementById("medium-posts").innerHTML=n.length?n.map(e=>{const t=truncate(stripHtml(e.description),120);return`\n            <div class="card">\n              <a href="${e.link}">${e.title}</a>\n              <div class="meta">\n                <span class="card-excerpt">${t}</span>\n                <span class="card-date">${new Date(e.pubDate).toLocaleDateString()}</span>\n              </div>\n            </div>\n          `}).join(""):'<div class="card"><span class="meta">No posts found</span></div>',systemsLoaded.medium=!0,queueStir(),checkAllSynced()}catch(e){document.getElementById("medium-posts").innerHTML='<div class="card"><span class="meta">Could not load</span></div>',systemsLoaded.medium=!0,checkAllSynced()}}async function loadGoodreadsUpdates(){try{const e=await fetch("https://api.rss2json.com/v1/api.json?rss_url=https://www.goodreads.com/user/updates_rss/154858063-daniel-rodriguez"),t=await e.json(),n=t.items?.slice(0,3)||[];document.getElementById("goodreads-updates").innerHTML=n.length?n.map(e=>{const t=e.title.split("|"),n=t[0]?.trim()||e.title,i=t[1]?.trim()||"",o=e.description?.match(/<img[^>]+src="([^"]+)"/),a=o?o[1]:null;return`\n            <div class="card book-card">\n              ${a?`<img src="${a}" alt="${n}" class="book-cover">`:""}\n              <div class="book-info">\n                <span class="book-title">${n}</span>\n                ${i?`<span class="book-author">${i}</span>`:""}\n                <span class="card-date">${new Date(e.pubDate).toLocaleDateString()}</span>\n              </div>\n            </div>\n          `}).join(""):'<div class="card"><span class="meta">No updates found</span></div>',systemsLoaded.goodreads=!0,queueStir(),checkAllSynced()}catch(e){document.getElementById("goodreads-updates").innerHTML='<div class="card"><span class="meta">Could not load</span></div>',systemsLoaded.goodreads=!0,checkAllSynced()}}async function loadGitHubRepos(){try{const e=await fetch("https://api.github.com/search/commits?q=author:sadasant&sort=author-date&order=desc&per_page=100",{headers:{Accept:"application/vnd.github.cloak-preview"}}),t=await e.json();if(t.items&&t.items.length>0){const e={},n={};for(const i of t.items){const t=i.repository.full_name;e[t]=(e[t]||0)+1,n[t]||(n[t]={name:t,url:i.repository.html_url,description:i.repository.description})}const i=Object.entries(e).sort((e,t)=>t[1]-e[1]).slice(0,12).map(([e,t])=>({...n[e],commits:t}));document.getElementById("github-activity").innerHTML=`\n            <div class="repo-grid">\n              ${i.map(e=>{const t=e.name.split("/")[1]||e.name,n=e.name.split("/")[0],i="sadasant"===n.toLowerCase();return`\n                  <a href="${e.url}" class="repo-chip">\n                    <span class="repo-name">${t}</span>\n                    <span class="repo-meta">${i?"":n+" · "}${e.commits}</span>\n                  </a>\n                `}).join("")}\n            </div>\n          `}else{const e=await fetch("https://api.github.com/users/sadasant/repos?sort=stars&per_page=20"),t=(await e.json()).filter(e=>!e.fork&&e.stargazers_count>0).slice(0,12);document.getElementById("github-activity").innerHTML=t.length?`\n            <div class="repo-grid">\n              ${t.map(e=>`\n                <a href="${e.html_url}" class="repo-chip">\n                  <span class="repo-name">${e.name}</span>\n                  <span class="repo-meta">★ ${e.stargazers_count}</span>\n                </a>\n              `).join("")}\n            </div>\n          `:'<div class="card"><span class="meta">No public repos</span></div>'}systemsLoaded.github=!0,queueStir(),checkAllSynced()}catch(e){console.error("GitHub load error:",e),document.getElementById("github-activity").innerHTML='<div class="card"><span class="meta">Could not load</span></div>',systemsLoaded.github=!0,checkAllSynced()}}toggleBtn.addEventListener("click",()=>{document.body.classList.toggle("default-mode"),toggleBtn.textContent=document.body.classList.contains("default-mode")?"WebGL":"Default"}),loadMediumPosts(),loadGoodreadsUpdates(),loadGitHubRepos(),function(){const e=document.getElementById("email-link"),t=["sadasant","pm","me"],n=t[0]+"@"+t[1]+"."+t[2];e.href="mailto:"+n,e.title=n}(),document.querySelectorAll(".triptych-column").forEach(e=>{let t=null;e.addEventListener("click",n=>{"A"!==n.target.tagName&&(t&&clearTimeout(t),e.classList.add("struck"),t=setTimeout(()=>{e.classList.remove("struck")},800))})})</script></body></html>