dad2b237d480ba64706fbb6c63d092b2738a2413
jnavarr5
  Mon Aug 24 15:33:38 2026 -0700
Add ### headings as indented TOC sub-items and fix docs sidebar highlighting, refs #36320

diff --git docs/staticPage.html docs/staticPage.html
index fb00a516517..7f08813bb12 100755
--- docs/staticPage.html
+++ docs/staticPage.html
@@ -21,41 +21,91 @@
 <script>
 // Scroll-spy: highlight the current section in the sidebar TOC
 (function() {
     var toc = document.getElementById('docs-toc');
     if (!toc) return;
     var links = toc.querySelectorAll('a[href^="#"]');
     if (!links.length) return;
     var sections = [];
     for (var i = 0; i < links.length; i++) {
         var id = links[i].getAttribute('href').slice(1);
         var el = document.getElementById(id);
         if (el) sections.push({link: links[i], target: el});
     }
     if (!sections.length) return;
 
+    function setActive(link) {
+        for (var i = 0; i < sections.length; i++) {
+            sections[i].link.parentNode.classList.remove('active');
+        }
+        link.parentNode.classList.add('active');
+    }
+
+    // Jumping to a section in the final screenful leaves the page at its
+    // maximum scroll, where the sweep below would hand the highlight to a
+    // later section.  So a clicked link stays pinned until the reader scrolls.
+    var pinned = null;
+
     function onScroll() {
+        if (pinned) {
+            setActive(pinned);
+            return;
+        }
         var scrollY = window.scrollY || window.pageYOffset;
+        var viewHeight = window.innerHeight;
+        var maxScroll = document.documentElement.scrollHeight - viewHeight;
+        // A section becomes current once it passes a reading line 80px below
+        // the top of the viewport.  Sections in the final screenful never
+        // reach that line, because the page stops scrolling first, so once we
+        // enter the last screen the line sweeps down to the bottom of the
+        // viewport.  That gives every trailing section its turn and leaves the
+        // last one current at the very bottom.
+        var sweepStart = Math.max(0, maxScroll - viewHeight);
+        var t = 0;
+        if (maxScroll > sweepStart) {
+            t = (scrollY - sweepStart) / (maxScroll - sweepStart);
+            t = Math.max(0, Math.min(1, t));
+        }
+        var line = scrollY + 80 + t * (viewHeight - 80);
         var current = sections[0];
-        // At the bottom of the page the last section is often too short to
-        // scroll within the 80px threshold, so highlight it explicitly.
-        if (window.innerHeight + scrollY >= document.documentElement.scrollHeight - 2) {
-            current = sections[sections.length - 1];
-        } else {
         for (var i = 0; i < sections.length; i++) {
-                if (sections[i].target.offsetTop <= scrollY + 80) {
+            if (sections[i].target.offsetTop <= line) {
                 current = sections[i];
             }
         }
+        setActive(current.link);
     }
+
     for (var i = 0; i < sections.length; i++) {
-            sections[i].link.parentNode.classList.remove('active');
+        sections[i].link.addEventListener('click', function() {
+            pinned = this;
+            setActive(this);
+        });
     }
-        current.link.parentNode.classList.add('active');
+
+    // Any genuine scroll input hands control back to the sweep.  mousedown
+    // covers dragging the scrollbar, and fires before the click above, so
+    // clicking a TOC link still ends up pinned.
+    function release() {
+        pinned = null;
     }
+    window.addEventListener('wheel', release, {passive: true});
+    window.addEventListener('touchmove', release, {passive: true});
+    window.addEventListener('keydown', release);
+    window.addEventListener('mousedown', release);
 
     window.addEventListener('scroll', onScroll);
+
+    // Landing on the page with a #section URL has the same problem as a click.
+    if (window.location.hash) {
+        for (var i = 0; i < sections.length; i++) {
+            if ('#' + sections[i].target.id === window.location.hash) {
+                pinned = sections[i].link;
+                break;
+            }
+        }
+    }
     onScroll();
 })();
 </script>
 
 <!--#include virtual="$$ROOT/inc/gbPageEnd.html" -->