/* ==========================================================================
   Augusta Pediatric Associates — cursor-reactive card depth

   Give any element `data-apa-depth` and it answers the cursor: a soft
   highlight follows the pointer across it and the card tilts slightly toward
   it, so it reads as a surface rather than a rectangle that lights up.

   This file writes four custom properties and nothing else — `--apa-mx`,
   `--apa-my`, `--apa-rx`, `--apa-ry`. All of the visual work is in the page's
   own stylesheet, which means a card renders correctly if the script never
   runs, and two pages can share the behaviour without sharing a look.

   `offsetLeft`/`offsetTop`, NOT `getBoundingClientRect`. The handler puts a 3D
   rotation on the very element it is measuring, and a client rect includes
   that rotation — so each move would be measured against a box the previous
   move had already tilted, and the tilt would chase itself. Offsets are layout
   values that no transform touches.
   ========================================================================== */

(function () {
  'use strict';

  var TILT = 2.2;   /* degrees at the far edge — past ~3 it stops being subtle */

  function pageOffset(el) {
    var x = 0, y = 0;
    for (var n = el; n; n = n.offsetParent) {
      x += n.offsetLeft;
      y += n.offsetTop;
    }
    return { x: x, y: y };
  }

  function track(card) {
    var frame = 0, px = 0, py = 0;
    var tilt = card.getAttribute('data-apa-depth');
    tilt = tilt ? parseFloat(tilt) : TILT;

    function paint() {
      frame = 0;

      var o = pageOffset(card);
      var w = card.offsetWidth;
      var h = card.offsetHeight;
      if (!w || !h) return;

      var x = px - o.x;
      var y = py - o.y;

      var nx = (x / w) * 2 - 1;
      var ny = (y / h) * 2 - 1;
      nx = nx < -1 ? -1 : nx > 1 ? 1 : nx;
      ny = ny < -1 ? -1 : ny > 1 ? 1 : ny;

      /* pixels, not percentages, so the highlight stays put if the card is
         resized mid-gesture; rotateX is negated because a pointer low on the
         card should tip the FAR edge away, not the near one */
      card.style.setProperty('--apa-mx', Math.round(x) + 'px');
      card.style.setProperty('--apa-my', Math.round(y) + 'px');
      card.style.setProperty('--apa-ry', (nx * tilt).toFixed(2) + 'deg');
      card.style.setProperty('--apa-rx', (-ny * tilt).toFixed(2) + 'deg');
    }

    card.addEventListener('pointermove', function (e) {
      px = e.pageX;
      py = e.pageY;
      /* one write per frame: pointermove fires far more often than the screen
         refreshes, and every write here invalidates style */
      if (!frame) frame = requestAnimationFrame(paint);
    });

    /* Settle flat on the way out rather than holding the last angle, which
       would leave the card crooked until the next visit. */
    card.addEventListener('pointerleave', function () {
      if (frame) { cancelAnimationFrame(frame); frame = 0; }
      card.style.setProperty('--apa-rx', '0deg');
      card.style.setProperty('--apa-ry', '0deg');
    });
  }

  function boot() {
    /* A tilt that answers a pointer has nothing to say to a finger, and the
       reduced-motion rules in the stylesheets switch the effect off — so there
       is no reason to bind any of this in either case. */
    if (!window.matchMedia) return;
    if (!matchMedia('(hover: hover)').matches) return;
    if (matchMedia('(prefers-reduced-motion: reduce)').matches) return;

    var cards = document.querySelectorAll('[data-apa-depth]');
    for (var i = 0; i < cards.length; i++) track(cards[i]);
  }

  if (document.readyState === 'loading') {
    document.addEventListener('DOMContentLoaded', boot);
  } else {
    boot();
  }
})();
