/* Skjerva Fjordhytter, script.js
 *
 * Class contract with style.css:
 *   .reveal  ->  .visible         scroll reveal (700ms fade and 12px rise live in CSS)
 *   .motif-arsstripe.visible      year strip entered the viewport. Each tick gets --d (40ms steps)
 *   .scroll-to-top.visible        back-to-top button shown after 600px of scroll
 *   input[aria-invalid="true"]    field in error state, with its #<id>-error message revealed
 *
 * The navigation and the footer are scaffold-owned and wired by the scaffold, not here.
 */
(function () {
  'use strict';

  var prefersReducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
  var hasObserver = 'IntersectionObserver' in window;

  /* Scroll reveal */
  function initReveal() {
    var items = document.querySelectorAll('.reveal');
    if (!items.length) return;

    if (prefersReducedMotion || !hasObserver) {
      items.forEach(function (el) {
        el.classList.add('visible');
      });
      return;
    }

    var observer = new IntersectionObserver(
      function (entries) {
        entries.forEach(function (entry) {
          if (!entry.isIntersecting) return;
          entry.target.classList.add('visible');
          observer.unobserve(entry.target);
        });
      },
      { threshold: 0.12, rootMargin: '0px 0px -6% 0px' }
    );

    items.forEach(function (el) {
      observer.observe(el);
    });
  }

  /* Year strip: ticks light up left to right, 40ms apart */
  function initArsstripe() {
    var strips = document.querySelectorAll('.motif-arsstripe');
    if (!strips.length) return;

    strips.forEach(function (strip) {
      var ticks = strip.querySelectorAll('.motif-arsstripe__tick');
      ticks.forEach(function (tick, i) {
        tick.style.setProperty('--d', i * 40 + 'ms');
      });
    });

    if (prefersReducedMotion || !hasObserver) {
      strips.forEach(function (strip) {
        strip.classList.add('visible');
      });
      return;
    }

    var observer = new IntersectionObserver(
      function (entries) {
        entries.forEach(function (entry) {
          if (!entry.isIntersecting) return;
          entry.target.classList.add('visible');
          observer.unobserve(entry.target);
        });
      },
      { threshold: 0.6 }
    );

    strips.forEach(function (strip) {
      observer.observe(strip);
    });
  }

  /* Back to top (not present on every page) */
  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 });
    button.addEventListener('click', function () {
      window.scrollTo({ top: 0, behavior: prefersReducedMotion ? 'auto' : 'smooth' });
    });
    update();
  }

  /* Booking request form (bestill.html) */
  function initBookingForm() {
    var form = document.getElementById('booking-form');
    if (!form) return;

    var success = document.getElementById('booking-success');
    var resetButton = document.getElementById('booking-reset');
    var submitButton = form.querySelector('[type="submit"]');

    if (!success || !resetButton || !submitButton) {
      console.error('[booking] #booking-success, #booking-reset or the submit button is missing');
      return;
    }

    var fields = {
      name: document.getElementById('name'),
      email: document.getElementById('email'),
      phone: document.getElementById('phone'),
      cabin: document.getElementById('cabin'),
      arrival: document.getElementById('arrival'),
      departure: document.getElementById('departure'),
      guests: document.getElementById('guests'),
      message: document.getElementById('message'),
    };

    Object.keys(fields).forEach(function (key) {
      if (!fields[key]) console.error('[booking] field #' + key + ' not found');
    });

    var recipient = form.getAttribute('data-mailto') || 'booking@skjervafjordhytter.no';
    var DAY_MS = 24 * 60 * 60 * 1000;

    function toDateValue(date) {
      var m = String(date.getMonth() + 1).padStart(2, '0');
      var d = String(date.getDate()).padStart(2, '0');
      return date.getFullYear() + '-' + m + '-' + d;
    }

    function parseDate(value) {
      var parts = /^(\d{4})-(\d{2})-(\d{2})$/.exec(value);
      if (!parts) return null;
      return Date.UTC(Number(parts[1]), Number(parts[2]) - 1, Number(parts[3]));
    }

    function formatDate(value) {
      var parts = value.split('-');
      return parts[2] + '.' + parts[1] + '.' + parts[0];
    }

    function errorElement(input) {
      return document.getElementById(input.id + '-error');
    }

    function showError(input, message) {
      var error = errorElement(input);
      input.setAttribute('aria-invalid', 'true');
      if (error) {
        error.textContent = message;
        error.removeAttribute('hidden');
      }
    }

    function clearError(input) {
      var error = errorElement(input);
      input.removeAttribute('aria-invalid');
      if (error) {
        error.textContent = '';
        error.setAttribute('hidden', '');
      }
    }

    function clearAllErrors() {
      ['name', 'email', 'phone', 'cabin', 'arrival', 'departure'].forEach(function (key) {
        if (fields[key]) clearError(fields[key]);
      });
    }

    function validate() {
      var invalid = [];
      var emailPattern = /^[^\s@]+@[^\s@]+\.[^\s@]{2,}$/;

      clearAllErrors();

      if (fields.name.value.trim() === '') {
        showError(fields.name, 'Skriv inn navnet ditt.');
        invalid.push(fields.name);
      }

      if (!emailPattern.test(fields.email.value.trim())) {
        showError(fields.email, 'Skriv inn en gyldig e-postadresse.');
        invalid.push(fields.email);
      }

      if (fields.cabin.value === '') {
        showError(fields.cabin, 'Velg en hytte.');
        invalid.push(fields.cabin);
      }

      var arrival = parseDate(fields.arrival.value);
      var departure = parseDate(fields.departure.value);

      if (arrival === null) {
        showError(fields.arrival, 'Velg ankomstdato.');
        invalid.push(fields.arrival);
      }

      if (departure === null) {
        showError(fields.departure, 'Velg avreisedato.');
        invalid.push(fields.departure);
      } else if (arrival !== null && departure - arrival < 2 * DAY_MS) {
        showError(fields.departure, 'Avreise må være minst to netter etter ankomst.');
        invalid.push(fields.departure);
      }

      return invalid;
    }

    function buildMailto() {
      var cabin = fields.cabin.value;
      var subject = 'Bestillingsforespørsel: ' + cabin;
      var lines = [
        'Navn: ' + fields.name.value.trim(),
        'E-post: ' + fields.email.value.trim(),
        'Telefon: ' + (fields.phone.value.trim() || 'ikke oppgitt'),
        'Hytte: ' + cabin,
        'Ankomst: ' + formatDate(fields.arrival.value),
        'Avreise: ' + formatDate(fields.departure.value),
        'Antall gjester: ' + fields.guests.value,
        '',
        'Melding:',
        fields.message.value.trim() || '(ingen melding)',
      ];
      return (
        'mailto:' +
        recipient +
        '?subject=' +
        encodeURIComponent(subject) +
        '&body=' +
        encodeURIComponent(lines.join('\n'))
      );
    }

    function showSuccess() {
      form.setAttribute('hidden', '');
      success.removeAttribute('hidden');
      success.focus();
    }

    function resetForm() {
      form.reset();
      clearAllErrors();
      success.setAttribute('hidden', '');
      form.removeAttribute('hidden');
      applyPreselectedCabin();
      updateDateLimits();
      fields.name.focus();
    }

    function updateDateLimits() {
      var today = new Date();
      fields.arrival.min = toDateValue(today);
      var arrival = parseDate(fields.arrival.value);
      if (arrival !== null) {
        fields.departure.min = toDateValue(
          new Date(arrival + 2 * DAY_MS + today.getTimezoneOffset() * 60000)
        );
      } else {
        fields.departure.min = toDateValue(new Date(today.getTime() + 2 * DAY_MS));
      }
    }

    function applyPreselectedCabin() {
      var params = new URLSearchParams(window.location.search);
      var wanted = params.get('hytte');
      if (!wanted) return;
      var match = Array.prototype.some.call(fields.cabin.options, function (option) {
        return option.value === wanted;
      });
      if (match) fields.cabin.value = wanted;
    }

    form.addEventListener('submit', function (event) {
      /* Adding an action attribute to the form hands submission to a real endpoint. */
      if (form.getAttribute('action')) return;

      event.preventDefault();
      submitButton.disabled = true;

      try {
        var invalid = validate();
        if (invalid.length) {
          invalid[0].focus();
          return;
        }
        window.location.href = buildMailto();
        showSuccess();
      } finally {
        submitButton.disabled = false;
      }
    });

    form.addEventListener('input', function (event) {
      var target = event.target;
      if (target && target.id && errorElement(target)) clearError(target);
    });

    form.addEventListener('change', function (event) {
      if (event.target === fields.arrival) {
        updateDateLimits();
        if (fields.departure.value && parseDate(fields.departure.value) !== null) {
          clearError(fields.departure);
        }
      }
    });

    resetButton.addEventListener('click', resetForm);

    applyPreselectedCabin();
    updateDateLimits();
  }

  function init() {
    initReveal();
    initArsstripe();
    initScrollToTop();
    initBookingForm();
  }

  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, theme
   toggle) to their behaviour. Both branches are guarded by an
   element-existence check, so this block is inert on builds that
   don't opt in to controls.

   TRIGGERED-COMPONENT INVARIANT (see core/nav_scaffold.py docstring):
   • #ns-search-modal defaults to `hidden` + CSS `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, not the template.
   ============================================================ */
(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, not declarations. A `function foo() {}` inside an
    // `if` block is `no-inner-declarations`, and this template shipped three of
    // them into every static_site and nye_eventyr build — where the Developer
    // is forbidden to fix them, because this is the reserved `ns-` namespace.
    // Measured on marstrand-bokbinderi: all 6 of its eslint errors were in this
    // file's output, the build was filed `partial` for them, and a --bugfix run
    // then burned all three of its capped attempts on code it may not touch.
    // Both are declared before the listeners below that call them, so the
    // behaviour is identical.
    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 <html>; consumer CSS variables read from that
  // attribute (`:root[data-theme="light"] { ... }`) 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';

    // Expression, not a declaration — see the openSearch note below.
    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 (_) {
      /* storage blocked (private mode / 3rd-party cookies off) */
    }
    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 (_) {
        /* storage blocked: the theme still applies for this page */
      }
      applyTheme(next);
    });
  }
})();
/* END NAVIGATION SCAFFOLD */

/* ============================================================
   NAV PAGE-ACTIVE SCAFFOLD — DO NOT EDIT (managed by Orchestra)
   Multi-page builds: mark the current page's nav link .active by
   matching normalised pathnames. Works under file://, http(s)://,
   index/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 (_) {
      /* malformed %-escape: fall through with the raw path */
    }
    p = p.split('#')[0].split('?')[0].replace(/\\/g, '/');
    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; // in-page anchor, not a page link
    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; // 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 */
