(function () {
  'use strict';

  var prefersReducedMotion =
    window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches;

  /* ------------------------------------------------------------------
     Scroll reveal ("plate-load"): adds .visible once per .reveal element.
     The stats band is revealed up front rather than through the observer:
     its figures are key content and must never depend on an intersection
     threshold being met to become readable.
     ------------------------------------------------------------------ */
  function initReveal() {
    var items = Array.prototype.slice.call(document.querySelectorAll('.reveal'));
    if (!items.length) return;

    var grids = document.querySelectorAll('.stats-band__grid');
    Array.prototype.forEach.call(grids, function (grid) {
      var children = grid.querySelectorAll('.reveal');
      Array.prototype.forEach.call(children, function (el, i) {
        el.style.setProperty('--reveal-delay', i * 80 + 'ms');
      });
    });

    var statsBands = Array.prototype.slice.call(document.querySelectorAll('.stats-band'));
    var forced = [];
    statsBands.forEach(function (band) {
      forced.push(band);
      Array.prototype.forEach.call(band.querySelectorAll('.reveal'), function (el) {
        forced.push(el);
      });
    });
    forced.forEach(function (el) {
      if (el.classList.contains('reveal')) {
        el.classList.add('visible');
      }
    });
    items = items.filter(function (el) {
      return forced.indexOf(el) === -1;
    });

    if (prefersReducedMotion || !('IntersectionObserver' in window)) {
      items.forEach(function (el) {
        el.classList.add('visible');
      });
      return;
    }

    var observer = new IntersectionObserver(
      function (entries) {
        entries.forEach(function (entry) {
          if (entry.isIntersecting) {
            entry.target.classList.add('visible');
            observer.unobserve(entry.target);
          }
        });
      },
      { threshold: 0.12, rootMargin: '0px 0px -5% 0px' }
    );

    items.forEach(function (el) {
      observer.observe(el);
    });
  }

  /* ------------------------------------------------------------------
     Tabs (services-tabbed): ARIA tab pattern, instant switching.
     Tabs expose their state through aria-selected (not aria-expanded:
     a tab is never a disclosure toggle, one panel is always open).
     Closed panels use the hidden attribute; opening removes it.
     ------------------------------------------------------------------ */
  function initTabs() {
    var tabsRoots = document.querySelectorAll('.tabs');
    Array.prototype.forEach.call(tabsRoots, function (tabsRoot) {
      var list = tabsRoot.querySelector('.tabs__list');
      var tabs = Array.prototype.slice.call(tabsRoot.querySelectorAll('.tabs__tab'));
      if (!list || !tabs.length) {
        console.error('[tabs] .tabs__list or .tabs__tab not found');
        return;
      }

      function panelFor(tab) {
        var id = tab.getAttribute('aria-controls');
        return id ? document.getElementById(id) : null;
      }

      function activate(tab, moveFocus) {
        tabs.forEach(function (t) {
          var selected = t === tab;
          var panel = panelFor(t);
          t.classList.toggle('is-active', selected);
          t.setAttribute('aria-selected', selected ? 'true' : 'false');
          t.removeAttribute('aria-expanded');
          t.setAttribute('tabindex', selected ? '0' : '-1');
          if (!panel) {
            console.error('[tabs] panel not found for #' + t.id);
            return;
          }
          if (selected) {
            panel.removeAttribute('hidden');
          } else {
            panel.setAttribute('hidden', '');
          }
        });
        if (moveFocus) tab.focus();
        if (list.scrollWidth > list.clientWidth) {
          var tabLeft = tab.offsetLeft;
          var tabRight = tabLeft + tab.offsetWidth;
          if (tabLeft < list.scrollLeft || tabRight > list.scrollLeft + list.clientWidth) {
            list.scrollLeft = tabLeft - (list.clientWidth - tab.offsetWidth) / 2;
          }
        }
      }

      var initial =
        tabs.filter(function (t) {
          return t.classList.contains('is-active');
        })[0] || tabs[0];
      activate(initial, false);

      tabs.forEach(function (tab, index) {
        tab.addEventListener('click', function () {
          activate(tab, false);
        });
        tab.addEventListener('keydown', function (event) {
          var next = null;
          if (event.key === 'ArrowRight' || event.key === 'ArrowDown') {
            next = tabs[(index + 1) % tabs.length];
          } else if (event.key === 'ArrowLeft' || event.key === 'ArrowUp') {
            next = tabs[(index - 1 + tabs.length) % tabs.length];
          } else if (event.key === 'Home') {
            next = tabs[0];
          } else if (event.key === 'End') {
            next = tabs[tabs.length - 1];
          }
          if (next) {
            event.preventDefault();
            activate(next, true);
          }
        });
      });
    });
  }

  /* ------------------------------------------------------------------
     Contact form: static validation + inline confirmation.
     ------------------------------------------------------------------ */
  function initContactForm() {
    var form = document.getElementById('contact-form');
    if (!form) return;

    var success = document.getElementById('contact-form-success');
    var resetButton = document.getElementById('contact-form-reset');
    var nameInput = document.getElementById('contact-name');
    var emailInput = document.getElementById('contact-email');
    var nameError = document.getElementById('contact-name-error');
    var emailError = document.getElementById('contact-email-error');
    var submitButton = form.querySelector('button[type="submit"]');

    if (!success || !nameInput || !emailInput || !nameError || !emailError) {
      console.error('[contact-form] required form elements not found');
      return;
    }

    var emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;

    function setError(input, message, show) {
      message.classList.toggle('is-visible', show);
      if (show) {
        input.setAttribute('aria-invalid', 'true');
      } else {
        input.removeAttribute('aria-invalid');
      }
    }

    function validate() {
      var nameOk = nameInput.value.trim().length > 0;
      var emailOk = emailPattern.test(emailInput.value.trim());
      setError(nameInput, nameError, !nameOk);
      setError(emailInput, emailError, !emailOk);
      if (!nameOk) return nameInput;
      if (!emailOk) return emailInput;
      return null;
    }

    nameInput.addEventListener('input', function () {
      if (nameInput.value.trim()) setError(nameInput, nameError, false);
    });
    emailInput.addEventListener('input', function () {
      if (emailPattern.test(emailInput.value.trim())) setError(emailInput, emailError, false);
    });

    form.addEventListener('submit', function (event) {
      if (form.getAttribute('action')) return;
      event.preventDefault();

      if (submitButton) submitButton.disabled = true;
      try {
        var firstInvalid = validate();
        if (firstInvalid) {
          firstInvalid.focus();
          return;
        }
        form.hidden = true;
        success.classList.add('is-visible');
        success.focus();
      } finally {
        if (submitButton) submitButton.disabled = false;
      }
    });

    if (resetButton) {
      resetButton.addEventListener('click', function () {
        form.reset();
        setError(nameInput, nameError, false);
        setError(emailInput, emailError, false);
        success.classList.remove('is-visible');
        form.hidden = false;
        nameInput.focus();
      });
    } else {
      console.error('[contact-form] #contact-form-reset not found');
    }
  }

  /* ------------------------------------------------------------------
     Scroll-to-top button.
     ------------------------------------------------------------------ */
  function initScrollToTop() {
    var button = document.querySelector('.scroll-to-top');
    if (!button) return;

    function update() {
      button.classList.toggle('visible', window.scrollY > 600);
    }

    window.addEventListener('scroll', update, { passive: true });
    update();

    button.addEventListener('click', function () {
      window.scrollTo({ top: 0, behavior: prefersReducedMotion ? 'auto' : 'smooth' });
    });
  }

  function init() {
    initReveal();
    initTabs();
    initContactForm();
    initScrollToTop();
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', init);
  } else {
    init();
  }
})();
/* ============================================================
   NAVIGATION SCAFFOLD - DO NOT EDIT (managed by Orchestra)
   ============================================================ */
(function () {
  var nav = document.getElementById('ns-nav');
  var linksContainer = document.getElementById('ns-links');
  var burger = document.getElementById('ns-hamburger');
  var overlay = document.getElementById('ns-overlay');
  if (!nav || !linksContainer || !burger) return;

  var isOpen = false;

  function openMenu() {
    isOpen = true;
    linksContainer.classList.add('open');
    if (overlay) {
      overlay.classList.add('open');
      overlay.setAttribute('aria-hidden', 'false');
    }
    burger.setAttribute('aria-expanded', 'true');
    document.body.style.overflow = 'hidden';
  }

  function closeMenu() {
    isOpen = false;
    linksContainer.classList.remove('open');
    if (overlay) {
      overlay.classList.remove('open');
      overlay.setAttribute('aria-hidden', 'true');
    }
    burger.setAttribute('aria-expanded', 'false');
    document.body.style.overflow = '';
  }

  function toggleMenu() {
    if (isOpen) closeMenu();
    else openMenu();
  }

  // Event listeners
  burger.addEventListener('click', toggleMenu);
  if (overlay) overlay.addEventListener('click', closeMenu);

  linksContainer.querySelectorAll('a').forEach(function (link) {
    link.addEventListener('click', function () {
      if (window.innerWidth <= 768) closeMenu();
    });
  });

  // Keyboard: Escape closes menu
  document.addEventListener('keydown', function (e) {
    if (e.key === 'Escape' && isOpen) {
      closeMenu();
      burger.focus();
    }
  });

  // Resize safety: prevents double nav when resizing from mobile to desktop
  window.addEventListener(
    'resize',
    function () {
      if (window.innerWidth > 768 && isOpen) {
        closeMenu();
      }
    },
    { passive: true }
  );

  // Scroll effect: transparent to solid background
  window.addEventListener(
    'scroll',
    function () {
      nav.classList.toggle('scrolled', window.scrollY > 80);
    },
    { passive: true }
  );

  // Active link highlighting via IntersectionObserver
  var sections = [];
  linksContainer.querySelectorAll('a[href^="#"]').forEach(function (a) {
    var id = a.getAttribute('href').slice(1);
    var el = document.getElementById(id);
    if (el) sections.push({ el: el, link: a });
  });
  if (sections.length && 'IntersectionObserver' in window) {
    var obs = new IntersectionObserver(
      function (entries) {
        entries.forEach(function (entry) {
          if (entry.isIntersecting) {
            linksContainer.querySelectorAll('a').forEach(function (a) {
              a.classList.remove('active');
            });
            sections.forEach(function (s) {
              if (s.el === entry.target) s.link.classList.add('active');
            });
          }
        });
      },
      { rootMargin: '-20% 0px -70% 0px', threshold: 0 }
    );
    sections.forEach(function (s) {
      obs.observe(s.el);
    });
  }
})();

/* ============================================================
   NAV CONTROLS SCAFFOLD - DO NOT EDIT (managed by Orchestra)
   Wires the optional right-slot controls (search trigger and
   theme toggle) to their behaviour. Both branches are guarded
   by an element-existence check, so this block is inert on
   builds that do not opt in to controls.

   Triggered-component invariant:
   - The search modal defaults to hidden and display none.
   - Open state is set only here, in response to a user click.
   - Close paths: backdrop click, close button, Escape key.
   - Re-opening focus returns to the trigger for keyboard users.

   Do not duplicate this logic in script.js. Do not pre-open the
   modal in markup. Open state belongs to the user.
   ============================================================ */
(function () {
  // Search modal
  var trigger = document.getElementById('ns-search-trigger');
  var modal = document.getElementById('ns-search-modal');
  if (trigger && modal) {
    var input = modal.querySelector('#ns-search-input');
    var closers = modal.querySelectorAll('[data-ns-search-close]');

    // Function expressions rather than declarations inside the if block,
    // to satisfy the no-inner-declarations lint rule.
    var openSearch = function () {
      modal.removeAttribute('hidden');
      modal.classList.add('open');
      modal.setAttribute('aria-hidden', 'false');
      trigger.setAttribute('aria-expanded', 'true');
      document.body.style.overflow = 'hidden';
      if (input)
        setTimeout(function () {
          input.focus();
        }, 30);
    };
    var closeSearch = function () {
      modal.classList.remove('open');
      modal.setAttribute('hidden', '');
      modal.setAttribute('aria-hidden', 'true');
      trigger.setAttribute('aria-expanded', 'false');
      document.body.style.overflow = '';
      trigger.focus();
    };

    trigger.addEventListener('click', openSearch);
    closers.forEach(function (el) {
      el.addEventListener('click', closeSearch);
    });
    document.addEventListener('keydown', function (e) {
      if (e.key === 'Escape' && modal.classList.contains('open')) closeSearch();
    });
  }

  // Theme toggle
  // Sets data-theme on the html element; consumer CSS variables read from
  // that attribute to recolour the site. Persisted in localStorage so the
  // choice survives reloads.
  var themeBtn = document.getElementById('ns-theme-toggle');
  if (themeBtn) {
    var STORAGE_KEY = 'orch-theme';

    var applyTheme = function (t) {
      document.documentElement.setAttribute('data-theme', t);
      themeBtn.setAttribute('aria-pressed', t === 'light' ? 'true' : 'false');
    };

    var stored = null;
    try {
      stored = localStorage.getItem(STORAGE_KEY);
    } catch (_) {
      stored = null;
    }
    applyTheme(stored === 'light' ? 'light' : 'dark');

    themeBtn.addEventListener('click', function () {
      var current = document.documentElement.getAttribute('data-theme');
      var next = current === 'light' ? 'dark' : 'light';
      try {
        localStorage.setItem(STORAGE_KEY, next);
      } catch (_) {
        next = next;
      }
      applyTheme(next);
    });
  }
})();
/* END NAVIGATION SCAFFOLD */

/* ============================================================
   NAV PAGE-ACTIVE SCAFFOLD - DO NOT EDIT (managed by Orchestra)
   Multi-page builds: mark the current page nav link active by
   matching normalised pathnames. Works under file, http, https,
   index or root, trailing slash, and extension-less routes.
   ============================================================ */
(function () {
  var links = document.getElementById('ns-links');
  if (!links) return;
  function pageKey(p) {
    try {
      p = decodeURIComponent(p || '');
    } catch (_) {
      p = p || '';
    }
    p = p.split('#')[0].split('?')[0].split('\\').join('/');
    var seg = p.substring(p.lastIndexOf('/') + 1).toLowerCase();
    if (seg === '' || seg === 'index.html' || seg === 'home.html') return 'index.html';
    if (seg.indexOf('.') === -1) seg += '.html';
    return seg;
  }
  var current = pageKey(window.location.pathname || window.location.href);
  links.querySelectorAll('a').forEach(function (a) {
    var href = a.getAttribute('href') || '';
    if (href.charAt(0) === '#') return;
    if (pageKey(href) === current) {
      a.classList.add('active');
      a.setAttribute('aria-current', 'page');
    } else {
      a.classList.remove('active');
      a.removeAttribute('aria-current');
    }
  });
})();
/* END NAV PAGE-ACTIVE SCAFFOLD */

/* ============================================================
   FOOTER SCAFFOLD - newsletter (managed by Orchestra)
   No backend on a static site: validate, confirm, done. A real
   endpoint is wired later by giving the form an action attribute.
   ============================================================ */
document.querySelectorAll('form[data-ns-newsletter]').forEach(function (form) {
  form.addEventListener('submit', function (event) {
    if (form.getAttribute('action')) return;
    event.preventDefault();
    var input = form.querySelector('input[type="email"]');
    if (input && !input.checkValidity()) {
      input.reportValidity();
      return;
    }
    var note = form.querySelector('[data-ns-newsletter-note]');
    if (note) {
      note.hidden = false;
      note.textContent = note.getAttribute('data-success') || note.textContent;
    }
    form.reset();
  });
});
/* END FOOTER SCAFFOLD */

/* ============================================================
   FOOTER SCAFFOLD — newsletter (managed by Orchestra)
   No backend on a static site: validate, confirm, done. A real
   endpoint is wired later by giving the form an action attribute.
   ============================================================ */
document.querySelectorAll('form[data-ns-newsletter]').forEach(function (form) {
  form.addEventListener('submit', function (event) {
    if (form.getAttribute('action')) return;  // real endpoint wired — let it post
    event.preventDefault();
    var input = form.querySelector('input[type="email"]');
    if (input && !input.checkValidity()) {
      input.reportValidity();
      return;
    }
    var note = form.querySelector('[data-ns-newsletter-note]');
    if (note) {
      note.hidden = false;
      note.textContent = note.getAttribute('data-success') || note.textContent;
    }
    form.reset();
  });
});
/* END FOOTER SCAFFOLD */
