Home-page interactions for index.html.
No framework — plain DOM code. Everything here is a progressive enhancement: with JavaScript off, the page still reads and works, just without the extras. Each feature checks that its elements exist first, so the file is safe to load on a page that lacks some of them.
Features, in file order:
- Mobile navigation disclosure (hamburger menu).
- In-page anchor links: smooth scroll + move keyboard focus to the target.
- Sticky header: shadow once scrolled; hides near the end of the page.
- "How it works": connector line draws in when the section is visible.
- Scroll-reveal entrance animations for
[data-reveal]elements. - Scrollspy: highlights the nav link for the section in view.
- "Get Started" drawer tab: shown only in the middle stretch of the page.
- Booking-band maze: route draws itself once the footer comes into view.
- "Fine print" sample statement: hovering a note highlights its line, and each marked line's highlighter swipes across the row as one stroke.
The FAQ needs nothing here: each question is a native <details>.
Loaded as <script type="module">, which is deferred by default, so the
whole document has been parsed by the time this runs.
Several features throttle scroll handling with requestAnimationFrame and
a ticking flag: however many scroll events fire, the work runs at most
once per frame.
Members
(inner, constant) REVEAL_MS
How long to wait before clearing reveal-in (longest entrance, in ms).
(inner, constant) STAGGERED :Object.<string, string>
For each data-reveal value whose children enter one after another,
the selector for those children (relative to the element).
Type:
- Object.<string, string>
(inner, constant) STAGGER_CAP
Highest stagger index; items beyond this all share the last delay.
(inner) currentId
Id of the section currently marked, or null. @type {string|null}
(inner, constant) drawerTab
The fixed "Get Started" tab. @type {HTMLElement|null}
(inner, constant) finePrint
The sample-statement container. @type {HTMLElement|null}
(inner, constant) header
The sticky site header. @type {HTMLElement|null}
(inner, constant) howFlow
The "How it works" steps container. @type {HTMLElement|null}
(inner) lastY
Scroll position at the previous update, to detect direction.
(inner, constant) linksById
Section id → the nav links pointing at it. @type {Map<string, HTMLAnchorElement[]>}
(inner, constant) mobileNav
The mobile nav panel it controls. @type {HTMLElement|null}
(inner, constant) navLinks
Every in-page nav link (desktop and mobile). @type {HTMLAnchorElement[]}
(inner, constant) navToggle
The hamburger button. @type {HTMLElement|null}
(inner, constant) osReducedMotion :MediaQueryList
Live media query for the device's reduced-motion setting.
Type:
- MediaQueryList
(inner) pending
Elements that haven't been revealed yet. Shrinks as they play.
(inner, constant) prefersReducedMotion :Object
Whether motion should be reduced right now: the device asks for it, or the
visitor turned Animations off in Site preferences (data-motion="off" on
<html>, see theme.js / prefs.js). Checked by every animated feature;
under reduced motion, content is simply shown.
Type:
- Object
(inner, constant) routeMazes
The maze graphics (wide and phone versions). @type {NodeListOf
(inner) routeTicking
True while a check is already queued for the next frame.
(inner, constant) sections
The distinct sections to observe. @type {HTMLElement[]}
(inner, constant) siteFooter
The footer whose arrival triggers the route drawing. @type {HTMLElement|null}
(inner, constant) tabShowAt
The tab appears once this section reaches the viewport. @type {HTMLElement|null}
(inner) tabTicking
True while an update is already queued for the next frame.
(inner, constant) tabTuckAt
The tab tucks away once this section is well into view. @type {HTMLElement|null}
(inner, constant) tail :HTMLElement|null
The closing region of the page; the header hides as this approaches. Falls back to the footer if there is no #schedule section.
Type:
- HTMLElement | null
(inner) ticking
True while an update is already queued for the next frame.
(inner) ticking
True while a sweep is already queued for the next frame.
(inner, constant) visible
Sections currently crossing the viewport's midline. @type {Set
Methods
(inner) checkRoute() → {void}
Once the footer's top is above 92% of the viewport height, adds
is-routed to every maze (the CSS draws the route) and stops listening —
it only happens once per page view.
Returns:
- Type
- void
(inner) focusTarget(hash) → {void}
Moves keyboard focus to the element an in-page link points at.
Most section elements aren't normally focusable, so a temporary
tabindex="-1" is added (focusable by script, but not added to the Tab
order) and removed again once focus leaves. Scrolling is left to the
caller (preventScroll), so focus doesn't fight the smooth scroll.
Parameters:
| Name | Type | Description |
|---|---|---|
hash |
string | A fragment such as |
Returns:
- Type
- void
(inner) onRouteScroll() → {void}
Scroll/resize handler: queues one checkRoute per animation frame.
Declared with const after checkRoute, which is fine: checkRoute only
reads it when it runs, by which point it exists.
Returns:
- Type
- void
(inner) onScroll() → {void}
Scroll/resize handler: queues one sweep per animation frame.
Returns:
- Type
- void
(inner) setCurrent(id) → {void}
Marks the nav links for section id with aria-current="true" and
clears the mark from all others. No-op if id is already current.
Parameters:
| Name | Type | Description |
|---|---|---|
id |
string | null | Section id to mark, or |
Returns:
- Type
- void
(inner) setNav(open) → {void}
Opens or closes the mobile menu and updates the button to match
(aria-expanded for assistive tech, aria-label for its name).
Parameters:
| Name | Type | Description |
|---|---|---|
open |
boolean |
|
Returns:
- Type
- void
(inner) sweep() → {void}
Checks every still-pending element's position and reveals the ones that are due. An element is due once its top is above 92% of the viewport height — or unconditionally once the page is scrolled to the bottom (short final sections might never reach that line).
Elements already above the top of the viewport are shown without an entrance (the visitor jumped past them).
Returns:
- Type
- void
(inner) syncDrawerTab() → {void}
Shows or tucks away the drawer tab based on scroll position.
is-tuckedhides it (before #what is reached, or once #intake is in the lower quarter of the viewport or above).tab-wait(the initial authored state) is removed on first run.- The first time the tab appears,
is-arrivingplays a one-off attention animation (skipped under reduced motion).
Returns:
- Type
- void
(inner) update() → {void}
Recomputes the header's classes from the current scroll position:
scrolledonce the page has moved more than 8px from the top;header-hiddenwhen the closing region is near and the visitor isn't scrolling up.
Returns:
- Type
- void