/** * honeyDue landing — minimal progressive enhancement. * - Mobile nav toggle * - Nav background on scroll * - Reveal-on-scroll via IntersectionObserver * - Smooth scroll for anchor links (offset for sticky nav) * - Comb completion-grid generator * - Respects prefers-reduced-motion */ (function () { 'use strict'; var prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches; document.addEventListener('DOMContentLoaded', function () { initMobileNav(); initScrollNav(); initSmoothScroll(); initReveal(); initCombGrid(); }); /* ---------- Mobile navigation ---------- */ function initMobileNav() { var toggle = document.getElementById('nav-toggle'); var links = document.getElementById('nav-links'); if (!toggle || !links) return; toggle.addEventListener('click', function () { var isOpen = links.classList.toggle('is-open'); toggle.classList.toggle('is-active', isOpen); toggle.setAttribute('aria-expanded', isOpen ? 'true' : 'false'); }); // Close menu when a nav link is tapped links.querySelectorAll('.nav-link').forEach(function (link) { link.addEventListener('click', function () { links.classList.remove('is-open'); toggle.classList.remove('is-active'); toggle.setAttribute('aria-expanded', 'false'); }); }); } /* ---------- Sticky nav scroll state ---------- */ function initScrollNav() { var nav = document.getElementById('nav'); if (!nav) return; var ticking = false; function update() { nav.classList.toggle('is-scrolled', window.pageYOffset > 16); ticking = false; } window.addEventListener('scroll', function () { if (!ticking) { window.requestAnimationFrame(update); ticking = true; } }, { passive: true }); update(); } /* ---------- Smooth scroll with sticky-nav offset ---------- */ function initSmoothScroll() { document.querySelectorAll('a[href^="#"]').forEach(function (anchor) { anchor.addEventListener('click', function (e) { var href = anchor.getAttribute('href'); if (!href || href === '#' || href.length < 2) return; var target = document.querySelector(href); if (!target) return; e.preventDefault(); var nav = document.querySelector('.nav'); var navHeight = nav ? nav.offsetHeight : 0; var top = target.getBoundingClientRect().top + window.pageYOffset - navHeight - 8; window.scrollTo({ top: top, behavior: prefersReducedMotion ? 'auto' : 'smooth' }); }); }); } /* ---------- Reveal-on-scroll ---------- */ function initReveal() { var elements = document.querySelectorAll('.reveal'); if (!elements.length) return; if (prefersReducedMotion || !('IntersectionObserver' in window)) { elements.forEach(function (el) { el.classList.add('is-visible'); }); return; } var io = new IntersectionObserver(function (entries) { entries.forEach(function (entry) { if (entry.isIntersecting) { entry.target.classList.add('is-visible'); io.unobserve(entry.target); } }); }, { threshold: 0.12, rootMargin: '0px 0px -10% 0px' }); elements.forEach(function (el) { io.observe(el); }); } /* ---------- Completion-grid (the comb) ---------- Generates a deterministic honeycomb of cells, more filled near the top, fading toward the bottom — visualizes "your hive fills across the season". Eight columns, ~80 cells. */ function initCombGrid() { var grid = document.getElementById('combGrid'); if (!grid) return; var cols = 8; var rows = 10; var total = cols * rows; // Deterministic "filled" pattern — top rows almost all filled, // middle rows half, bottom rows mostly empty with a few faint. // Seeded by row+col so it's stable across renders. for (var i = 0; i < total; i++) { var row = Math.floor(i / cols); var col = i % cols; var cell = document.createElement('div'); cell.className = 'comb-cell'; // Row weight 0..1 (1 = all filled, 0 = none) var w = 1 - (row / (rows - 1)); // Add small deterministic variance based on (row * 13 + col * 7) % 11 var noise = ((row * 13 + col * 7) % 11) / 11; var fillScore = w * 0.85 + (1 - noise) * 0.15; if (fillScore > 0.7) { cell.classList.add('is-filled'); } else if (fillScore > 0.42) { cell.classList.add('is-faint'); } grid.appendChild(cell); } } })();