<!DOCTYPE html><html lang="en"> <head><meta charset="utf-8"><meta name="viewport" content="width=device-width, initial-scale=1"><!-- Tints the mobile browser chrome to the page background. One tag, no media
       attribute: a media-scoped pair would track the OS, which this site
       deliberately does not follow. The boot script owns the value instead,
       which is why this must be parsed BEFORE it. --><meta name="theme-color" content="#f9f9fb"><!-- Runs pre-paint, before the stylesheet, so the theme is settled on the
       first frame and nothing flashes. Also carries the `js` class the
       content-hiding animation CSS is gated on. Plain JS only (is:inline is not
       transformed). KEEP IN SYNC with the copy in 404.astro. --><script>
    (function () {
      var root = document.documentElement;
      root.classList.add('js');
      // Light is the default: dark applies only when chosen, so an absent key
      // means light. The OS preference is deliberately not consulted.
      var dark = false;
      try { dark = localStorage.getItem('theme') === 'dark'; } catch (e) {}
      if (dark) root.setAttribute('data-theme', 'dark');
      var tint = function (isDark) {
        var meta = document.querySelector('meta[name="theme-color"]');
        // Must equal the --bg pair in global.css exactly.
        if (meta) meta.setAttribute('content', isDark ? '#141210' : '#f9f9fb');
      };
      tint(dark);
      window.__setTheme = function (mode) {
        var d = mode === 'dark';
        if (d) root.setAttribute('data-theme', 'dark');
        else root.removeAttribute('data-theme');
        try {
          if (d) localStorage.setItem('theme', 'dark');
          else localStorage.removeItem('theme');
        } catch (e) {}
        tint(d);
      };
    })();
  </script><title>Ashwin Rajendraprasad · Computer Vision Architect</title><meta name="description" content="Ashwin Rajendraprasad, Chief AI Officer &amp; Computer Vision Architect. Vision systems deployed across 60+ countries to 100M+ users."><link rel="icon" type="image/svg+xml" href="/favicon.svg"><!-- Fallbacks the SVG cannot cover: surfaces that probe /favicon.ico
       directly, and iOS, which refuses SVG for a home-screen bookmark and would
       otherwise save a screenshot. A portfolio is exactly the kind of page
       someone pins. Both come from `npm run icons`. --><link rel="icon" href="/favicon.ico" sizes="32x32"><link rel="apple-touch-icon" href="/apple-touch-icon.png"><link rel="canonical" href="https://ashwin.vision/"><meta property="og:title" content="Ashwin Rajendraprasad · Computer Vision Architect"><meta property="og:description" content="Ashwin Rajendraprasad, Chief AI Officer &amp; Computer Vision Architect. Vision systems deployed across 60+ countries to 100M+ users."><meta property="og:type" content="website"><meta property="og:url" content="https://ashwin.vision/"><meta property="og:site_name" content="Ashwin Rajendraprasad"><meta property="og:image" content="https://ashwin.vision/og.png"><meta property="og:image:width" content="2400"><meta property="og:image:height" content="1260"><meta name="twitter:card" content="summary_large_image"><meta name="twitter:image" content="https://ashwin.vision/og.png"><script type="application/ld+json">{"@context":"https://schema.org","@type":"Person","@id":"https://ashwin.vision/#person","name":"Ashwin Rajendraprasad","jobTitle":"Chief AI Officer \u0026 Computer Vision Architect","url":"https://ashwin.vision/","mainEntityOfPage":"https://ashwin.vision/","sameAs":["https://www.linkedin.com/in/ashwinrajendraprasad/","https://cloudsignal.ai"],"worksFor":{"@type":"Organization","name":"FaceCake Marketing Technologies"}}</script><link rel="stylesheet" href="/_astro/profile.IQbEEj2L.css"></head> <body> <a href="#main" class="skip-link">Skip to content</a> <!-- NAV --> <nav aria-label="Primary"> <div class="nav-inner"> <div class="nav-name">Ashwin Rajendraprasad</div> <div class="nav-links"> <a href="#about">About</a> <a href="#domains">Vision</a> <a href="#career">Career</a> <a href="https://www.linkedin.com/in/ashwinrajendraprasad/" target="_blank" rel="noopener" class="nav-cta">Connect</a> <button type="button" class="theme-toggle tt-nav" data-mode="light" aria-label="Switch to dark theme" title="Change colour theme"> <svg class="tt-i tt-i-dark" viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"></path></svg> <svg class="tt-i tt-i-light" viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2m0 16v2M4.9 4.9l1.4 1.4m11.4 11.4 1.4 1.4M2 12h2m16 0h2M4.9 19.1l1.4-1.4m11.4-11.4 1.4-1.4"></path></svg> </button> </div>  <button type="button" class="theme-toggle tt-bar" data-mode="light" aria-label="Switch to dark theme" title="Change colour theme"> <svg class="tt-i tt-i-dark" viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M21 12.8A9 9 0 1 1 11.2 3a7 7 0 0 0 9.8 9.8z"></path></svg> <svg class="tt-i tt-i-light" viewBox="0 0 24 24" width="20" height="20" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" aria-hidden="true" focusable="false"><circle cx="12" cy="12" r="4"></circle><path d="M12 2v2m0 16v2M4.9 4.9l1.4 1.4m11.4 11.4 1.4 1.4M2 12h2m16 0h2M4.9 19.1l1.4-1.4m11.4-11.4 1.4-1.4"></path></svg> </button> <button class="hamburger" aria-label="Menu" aria-expanded="false" aria-controls="mobile-menu"> <span></span><span></span><span></span> </button> </div> <div id="mobile-menu" class="mobile-menu"> <a href="#about">About</a> <a href="#domains">Vision</a> <a href="#career">Career</a> <a href="https://www.linkedin.com/in/ashwinrajendraprasad/" target="_blank" rel="noopener" class="nav-cta">Connect</a> </div> </nav> <main id="main"> <!-- HERO --> <section class="hero wrap"> <div class="hero-inner"> <div class="hero-eyebrow reveal"> <span class="dot"></span>
Available for advisory &amp; leadership engagements
</div> <h1 class="reveal reveal-d1">
I architect the systems<br class="br-desktop">
that teach machines<br class="br-desktop">
to <em>see.</em> </h1> <p class="hero-desc reveal reveal-d2"> <strong>Ashwin Rajendraprasad</strong>, Chief AI Officer &amp; Computer Vision Architect. Two decades designing vision systems across retail, healthcare, entertainment, and enterprise. Deployed to <strong>100M+ users</strong> in <strong>60+ countries</strong>.
</p> <div class="stats-row reveal reveal-d3"> <div class="stat-cell"> <div class="stat-val">20<span>+</span></div> <div class="stat-label">Years in AI &amp; CV</div> </div> <div class="stat-cell"> <div class="stat-val">60<span>+</span></div> <div class="stat-label">Countries</div> </div> <div class="stat-cell"> <div class="stat-val">100M<span>+</span></div> <div class="stat-label">End users</div> </div> </div> </div> </section> <!-- PARTNERS --> <div class="partners"> <div class="partners-label">Brands I've built for</div>  <div class="partners-rail"> <div class="partners-track"> <div class="partners-half"> <span class="partner">Google</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Microsoft</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Disney</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">CHANEL</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Bloomingdale&#39;s</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">NFL</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Sephora</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">NARS</span><span class="partner-dot" aria-hidden="true"></span> </div><div class="partners-half" aria-hidden="true"> <span class="partner">Google</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Microsoft</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Disney</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">CHANEL</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Bloomingdale&#39;s</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">NFL</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Sephora</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">NARS</span><span class="partner-dot" aria-hidden="true"></span> </div><div class="partners-half" aria-hidden="true"> <span class="partner">Google</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Microsoft</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Disney</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">CHANEL</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Bloomingdale&#39;s</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">NFL</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">Sephora</span><span class="partner-dot" aria-hidden="true"></span><span class="partner">NARS</span><span class="partner-dot" aria-hidden="true"></span> </div> </div> </div> </div> <!-- ABOUT --> <section class="section wrap" id="about"> <div class="section-head reveal"> <span class="section-num">01</span> <h2 class="section-title">Who I Am</h2> <span class="section-line" aria-hidden="true"></span> </div> <div class="about-layout"> <div class="about-left reveal"> <img class="about-photo" src="/ashwin-portrait.webp" alt="Ashwin Rajendraprasad" width="880" height="1100" loading="lazy"> </div> <div class="about-right reveal reveal-d2"> <p>I started in <strong>academia at USC</strong>, studying how machines interpret visual and sensory input. That research became the foundation for everything since: two decades of designing <strong>end-to-end computer vision architectures</strong> that move from lab to production to global scale.</p><p>As <strong>Chief AI Officer at FaceCake</strong>, I lead the design of the vision systems behind experiences used by <strong>100M+ people worldwide</strong>: real-time perception, spatial understanding, AR rendering, and the multimodal models that tie them together. I also launched <strong>AVA</strong> at Google Cloud Next '24, fusing computer vision with large language models into a new kind of visual AI agent.</p><p>My work has shaped how major brands deploy visual intelligence: <strong>Disney's Magic Mirror</strong>, <strong>CHANEL's global AR eyewear</strong>, experiences for Microsoft, Bloomingdale's, the NFL, Sephora, and NARS. The model is the easy part. The work I care about is the system around it, the part that has to hold up once real people are using it.</p> </div> </div> <div class="about-philosophy reveal"> <div class="about-label">Philosophy</div> <p class="about-quote">
Vision is the richest sense we have. I build the systems that give machines that same power — and <em>turn it into products</em> that people actually use.
</p> </div> </section> <!-- DOMAINS --> <section class="section wrap" id="domains"> <div class="section-head reveal"> <span class="section-num">02</span> <h2 class="section-title">Where Vision Meets the World</h2> <span class="section-line" aria-hidden="true"></span> </div> <div class="domains-intro reveal"> <h2>Computer vision is the <em>interface layer</em> between AI and the physical world</h2> <p>Across industries, devices, and scales, I design systems that act on what a camera sees.</p> </div> <div class="domain-cards reveal"> <div class="domain-card"> <div class="dc-num">01</div> <h3>Real-Time Perception</h3> <p>3D tracking (500+ facial landmarks), skeletal estimation, depth sensing, segmentation, and gaze analysis, all at real-time speeds on cloud and edge.</p> </div><div class="domain-card"> <div class="dc-num">02</div> <h3>Spatial Intelligence &amp; AR</h3> <p>Augmented reality systems that understand 3D space, composite virtual objects in real time, and run on web, mobile, and kiosks.</p> </div><div class="domain-card"> <div class="dc-num">03</div> <h3>Visual AI for Commerce</h3> <p>Virtual try-on, visual search, style recognition, and personalization engines that change how people shop.</p> </div><div class="domain-card"> <div class="dc-num">04</div> <h3>Multimodal &amp; Agentic AI</h3> <p>Systems that fuse visual understanding with language models to create AI agents that see, reason, and converse.</p> </div><div class="domain-card"> <div class="dc-num">05</div> <h3>Medical &amp; Precision Vision</h3> <p>AR-guided surgical systems, marker tracking for ophthalmic procedures, and precision alignment algorithms where accuracy is life-critical.</p> </div><div class="domain-card"> <div class="dc-num">06</div> <h3>AI Architecture &amp; Scale</h3> <p>End-to-end ML infrastructure: inference pipelines, MLOps, edge deployment, and the engineering discipline to ship vision systems to millions.</p> </div> </div> </section> <!-- IMPACT --> <div class="impact"> <div class="wrap"> <div class="impact-grid reveal"> <div class="impact-item"> <div class="impact-val">100M<span>+</span></div> <div class="impact-label">End users served<br>worldwide</div> </div> <div class="impact-item"> <div class="impact-val">60<span>+</span></div> <div class="impact-label">Countries with live<br>deployments</div> </div> <div class="impact-item"> <div class="impact-val">20<span>+</span></div> <div class="impact-label">Years architecting<br>vision systems</div> </div> <div class="impact-item"> <div class="impact-val">25<span>+</span></div> <div class="impact-label">Global brand<br>partnerships</div> </div> </div> </div> </div> <!-- DEPLOYMENTS --> <section class="section wrap"> <div class="section-head reveal"> <span class="section-num">03</span> <h2 class="section-title">Select Deployments</h2> <span class="section-line" aria-hidden="true"></span> </div> <div class="deploy-grid"> <div class="deploy-card reveal"> <div class="deploy-partner">Google</div> <h3>AVA · Visual AI Agent</h3> <p>AI + AR personal shopping assistant fusing computer vision with LLMs on Vertex AI. Conversational product discovery grounded in what the camera sees.</p> <div class="deploy-stat"><strong>Google Cloud Next '24</strong></div> </div><div class="deploy-card reveal reveal-d1"> <div class="deploy-partner">Disney</div> <h3>Magic Mirror</h3> <p>Award-winning AR experience at Walt Disney World and Hong Kong Disneyland. Millions of guests have stepped up to the mirror.</p> <div class="deploy-stat"><strong>2 parks</strong> &middot; millions of interactions</div> </div><div class="deploy-card reveal reveal-d2"> <div class="deploy-partner">CHANEL</div> <h3>Global AR Eyewear</h3> <p>Real-time 3D perception and virtual overlay deployed across CHANEL&#39;s worldwide retail presence.</p> <div class="deploy-stat"><strong>60+ countries</strong></div> </div><div class="deploy-card reveal"> <div class="deploy-partner">Microsoft</div> <h3>Virtual Dressing Room</h3> <p>Virtual try-on combining spatial tracking with garment simulation that follows the body as it moves.</p> <div class="deploy-stat"><strong>Real-time CV</strong></div> </div><div class="deploy-card reveal reveal-d1"> <div class="deploy-partner">Bloomingdale&#39;s</div> <h3>Interactive Retail</h3> <p>In-store AR experiences that read body position for virtual apparel fitting.</p> <div class="deploy-stat"><strong>In-store</strong></div> </div><div class="deploy-card reveal reveal-d2"> <div class="deploy-partner">Sephora · NARS · NFL</div> <h3>Brand Experiences</h3> <p>Virtual try-on and interactive fan experiences, built on the same vision stack.</p> <div class="deploy-stat"><strong>Multi-brand</strong></div> </div> </div> </section> <!-- CAREER --> <section class="section wrap" id="career"> <div class="section-head reveal"> <span class="section-num">04</span> <h2 class="section-title">Career</h2> <span class="section-line" aria-hidden="true"></span> </div> <div class="career-grid"> <div class="career-card reveal"> <span class="career-badge">Current</span> <h3>Chief AI Officer</h3> <div class="career-company">FaceCake Marketing Technologies</div> <p>Progressed from Senior CV Engineer to VP of Engineering to Chief AI Officer. Designed the computer vision and AR platform from the ground up and scaled it globally with Google, CHANEL, Disney, and Microsoft.</p> </div><div class="career-card reveal reveal-d1"> <span class="career-badge">Previous</span> <h3>Computer Vision Algorithm Engineer</h3> <div class="career-company">Vantage Surgical Systems</div> <p>AR-enhanced surgical guidance for ophthalmic laser procedures. Real-time marker tracking and precision alignment in C++.</p> </div> </div> </section> <!-- SKILLS --> <section class="section wrap"> <div class="section-head reveal"> <span class="section-num">05</span> <h2 class="section-title">Stack</h2> <span class="section-line" aria-hidden="true"></span> </div> <div class="skills-flow reveal"> <span class="sk hl">Computer Vision</span><span class="sk hl">Machine Learning</span><span class="sk hl">Augmented Reality</span><span class="sk hl">LLMs</span><span class="sk hl">Agentic AI</span><span class="sk">PyTorch</span><span class="sk">TensorFlow</span><span class="sk">OpenCV</span><span class="sk">3D Perception</span><span class="sk">Depth Sensing</span><span class="sk">ARKit / ARCore</span><span class="sk">Unity3D</span><span class="sk">RAG</span><span class="sk">LangChain</span><span class="sk">MCP / A2A</span><span class="sk">Python</span><span class="sk">C / C++</span><span class="sk">Swift</span><span class="sk">TypeScript</span><span class="sk">Vertex AI</span><span class="sk">AWS SageMaker</span><span class="sk">Docker</span><span class="sk">Kubernetes</span><span class="sk">MLOps</span><span class="sk">Edge AI</span><span class="sk">NVIDIA Jetson</span><span class="sk">AI Governance</span><span class="sk">NIST AI RMF</span> </div> </section> <!-- AWARDS --> <section class="section wrap"> <div class="section-head reveal"> <span class="section-num">06</span> <h2 class="section-title">Recognition</h2> <span class="section-line" aria-hidden="true"></span> </div> <div class="awards-grid"> <div class="award-card reveal"> <div class="award-yr">2026</div> <h3>Top 50 Fearless Leader of the Year</h3> <p>International Association of Top Professionals (IAOTP)</p> </div><div class="award-card reveal reveal-d1"> <div class="award-yr">2025</div> <h3>Top Innovative Chief AI Officer of the Year</h3> <p>International Association of Top Professionals (IAOTP)</p> </div><div class="award-card reveal"> <div class="award-yr">Press</div> <h3>Most Innovative Chief AI Officers Transforming Technology</h3> <p>CIO World Magazine</p> </div><div class="award-card reveal reveal-d1"> <div class="award-yr">USC</div> <h3>Outstanding Academic Achievement &amp; Phi Kappa Phi</h3> <p>University of Southern California</p> </div> </div> </section> <!-- EDUCATION --> <section class="section wrap"> <div class="section-head reveal"> <span class="section-num">07</span> <h2 class="section-title">Education</h2> <span class="section-line" aria-hidden="true"></span> </div> <div class="reveal"> <div class="edu-card"> <h3 class="edu-degree">M.S. Electrical Engineering</h3> <div class="edu-school">University of Southern California</div> <div class="edu-detail">Specialization: Computer Vision &amp; Machine Learning</div> </div> </div> </section> <!-- CTA --> <section class="cta" id="contact"> <div class="wrap"> <h2 class="reveal">Let's build what<br class="br-desktop"> <em>comes next.</em></h2> <p class="sub reveal reveal-d1">Available for advisory, fractional AI leadership, and speaking engagements.</p> <div class="reveal reveal-d2"> <a href="https://www.linkedin.com/in/ashwinrajendraprasad/" target="_blank" rel="noopener" class="cta-btn">Connect on LinkedIn &nearr;</a> </div> </div> </section> </main> <!-- FOOTER --> <footer> <div class="footer-inner"> <span>&copy; 2026 Ashwin Rajendraprasad</span> <div class="footer-links"> <span>Los Angeles, CA</span> <a href="https://www.linkedin.com/in/ashwinrajendraprasad/" target="_blank" rel="noopener">LinkedIn</a> </div> </div> </footer> <script>
  // Reduced motion check
  const prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  const hasIO = 'IntersectionObserver' in window;

  // Scroll reveal (no-IO browsers: show everything immediately)
  if (hasIO) {
    const io = new IntersectionObserver((entries) => {
      entries.forEach(e => { if (e.isIntersecting) e.target.classList.add('vis'); });
    }, { threshold: 0.1 });
    document.querySelectorAll('.reveal').forEach(el => io.observe(el));

    document.querySelectorAll('.hero .reveal').forEach(el => {
      setTimeout(() => el.classList.add('vis'), 80);
    });
  } else {
    document.querySelectorAll('.reveal, .skills-flow').forEach(el => el.classList.add('vis'));
  }

  // Hamburger menu. Guarded because everything below — the active-nav observer,
  // the impact count-up, the skill stagger — shares this one inline script, and
  // an unguarded throw here would take all of it with it.
  const hamburger = document.querySelector('.hamburger');
  const mobileMenu = document.querySelector('.mobile-menu');
  const siteNav = document.querySelector('nav');
  if (hamburger && mobileMenu) {
    // No body scroll-lock: the panel is a dropdown with its own max-height and
    // overflow, not a full-screen overlay. It closes four ways instead — the
    // button, a link, Escape, or a press outside the header.
    const closeMenu = (returnFocus) => {
      if (!mobileMenu.classList.contains('open')) return;
      hamburger.classList.remove('open');
      mobileMenu.classList.remove('open');
      hamburger.setAttribute('aria-expanded', 'false');
      if (returnFocus) hamburger.focus();
    };
    hamburger.addEventListener('click', () => {
      const open = hamburger.classList.toggle('open');
      mobileMenu.classList.toggle('open');
      hamburger.setAttribute('aria-expanded', String(open));
    });
    mobileMenu.querySelectorAll('a').forEach(link => {
      link.addEventListener('click', () => closeMenu(false));
    });
    document.addEventListener('keydown', (e) => {
      if (e.key === 'Escape') closeMenu(true);
    });
    // pointerdown, not click: fires before focus moves, so a tap outside closes
    // the panel without also activating whatever sits under it.
    document.addEventListener('pointerdown', (e) => {
      if (siteNav && !siteNav.contains(e.target)) closeMenu(false);
    });
  }

  // Light/dark toggle. __setTheme is defined by the boot script in <head>;
  // guard so a markup change cannot take the rest of this script down with it.
  // Tapping it must NOT close the mobile menu (that binds to links only), so a
  // user can flip the theme and watch the page change.
  var toggles = document.querySelectorAll('.theme-toggle');
  if (toggles.length && window.__setTheme) {
    var ttIsDark = function () {
      return document.documentElement.getAttribute('data-theme') === 'dark';
    };
    var ttSync = function () {
      var dark = ttIsDark();
      for (var i = 0; i < toggles.length; i++) {
        toggles[i].setAttribute('data-mode', dark ? 'dark' : 'light');
        toggles[i].setAttribute('aria-label', dark ? 'Switch to light theme' : 'Switch to dark theme');
      }
    };
    for (var t = 0; t < toggles.length; t++) {
      toggles[t].addEventListener('click', function () {
        window.__setTheme(ttIsDark() ? 'light' : 'dark');
        ttSync();
      });
    }
    ttSync();
  }

  // Active nav indicator
  if (hasIO) {
    const sections = document.querySelectorAll('section[id]');
    const navLinksAll = document.querySelectorAll('.nav-links a:not(.nav-cta), .mobile-menu a:not(.nav-cta)');
    const navObs = new IntersectionObserver((entries) => {
      entries.forEach(entry => {
        if (entry.isIntersecting) {
          const id = entry.target.getAttribute('id');
          navLinksAll.forEach(link => {
            link.classList.toggle('active', link.getAttribute('href') === '#' + id);
          });
        }
      });
      // threshold 0 as well as 0.15: the -60% rootMargin leaves a root 40% of the
      // viewport tall, and a ratio is measured against the whole target, so any
      // section taller than ~2.7 viewports (#domains on a phone) could never reach
      // 0.15 and never lit its nav link.
    }, { threshold: [0, 0.15], rootMargin: '0px 0px -60% 0px' });
    sections.forEach(s => navObs.observe(s));
  }

  // Counting animation for impact numbers
  // (without IO the server-rendered values simply stay put)
  if (hasIO) {
    const countObs = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          e.target.querySelectorAll('.impact-val').forEach(el => {
            const numNode = el.firstChild;
            if (!numNode || numNode.nodeType !== Node.TEXT_NODE) return;
            // Split "100M" into the number to animate and the suffix to keep;
            // only the text node is rewritten, so the trailing <span>+</span> survives too.
            const match = numNode.textContent.match(/^(\d+)(\D*)$/);
            if (!match) return;
            const target = parseInt(match[1]);
            const suffix = match[2];
            if (prefersReducedMotion) {
              numNode.textContent = String(target) + suffix;
              return;
            }
            let current = 0;
            const step = Math.ceil(target / 40);
            const timer = setInterval(() => {
              current += step;
              if (current >= target) { current = target; clearInterval(timer); }
              numNode.textContent = String(current) + suffix;
            }, 30);
          });
          countObs.unobserve(e.target);
        }
      });
    }, { threshold: 0.3 });
    document.querySelectorAll('.impact-grid').forEach(el => countObs.observe(el));
  }

  // Skill pill stagger delays
  const skillsFlow = document.querySelector('.skills-flow');
  if (skillsFlow && !prefersReducedMotion && hasIO) {
    const skillObs = new IntersectionObserver((entries) => {
      entries.forEach(e => {
        if (e.isIntersecting) {
          const pills = e.target.querySelectorAll('.sk');
          pills.forEach((pill, i) => {
            pill.style.transitionDelay = (i * 25) + 'ms';
          });
          skillObs.unobserve(e.target);
        }
      });
    }, { threshold: 0.1 });
    skillObs.observe(skillsFlow);
  }
</script></body> </html>