/* ============================================================================
 * The Passion Table — depth & motion layer (pt-depth).
 *
 * Three axes of motion, layered:
 *
 *   3D  real depth      perspective, per-layer Z separation, pointer tilt, and
 *                       a shadow that stays anchored to a fixed light while the
 *                       surface rotates under it.
 *   4D  time            motion is a function of scroll position and scroll
 *                       velocity, plus a slow ambient drift, so the scene keeps
 *                       evolving instead of firing once and freezing.
 *   5D  the viewer      pointer, gyroscope, scroll speed and idleness feed back
 *                       into the scene, so it reacts to the person looking at it.
 *
 * Every value below is driven by a custom property that pt-depth.js writes with
 * full units. Nothing here composes a transform out of unitless numbers, which
 * keeps calc() out of the hot path and makes each property independently
 * animatable.
 *
 * ONE OWNER OF `transform`. Reveal, parallax, tilt and press all fold into the
 * single declaration on .pt-depth. An earlier build ran Motion One's animate()
 * over .pt-card, which writes an inline transform and silently clobbered any
 * other transform on the same element -- the same failure already documented in
 * menu.html. Custom properties compose; inline transforms do not.
 *
 * prefers-reduced-motion is honoured twice over: the block at the end of this
 * file flattens every transform, and pt-depth.js never starts its loop.
 * ========================================================================== */

/* Typed so the browser can interpolate them and so an unset value has a sane
 * resting state before the first frame lands. */
@property --d-rx     { syntax: '<angle>';      inherits: false; initial-value: 0deg; }
@property --d-ry     { syntax: '<angle>';      inherits: false; initial-value: 0deg; }
@property --d-ty     { syntax: '<length>';     inherits: false; initial-value: 0px;  }
@property --d-scale  { syntax: '<number>';     inherits: false; initial-value: 1;    }
@property --d-fade   { syntax: '<number>';     inherits: false; initial-value: 1;    }
@property --d-glare  { syntax: '<number>';     inherits: false; initial-value: 0;    }
@property --d-gx     { syntax: '<percentage>'; inherits: false; initial-value: 50%;  }
@property --d-gy     { syntax: '<percentage>'; inherits: false; initial-value: 50%;  }
@property --d-sx     { syntax: '<length>';     inherits: false; initial-value: 0px;  }
@property --d-sy     { syntax: '<length>';     inherits: false; initial-value: 18px; }

/* A scene establishes the vanishing point its children rotate against. Without
 * a shared perspective each card gets its own, and the group reads as a set of
 * unrelated flat rectangles rather than objects on one surface. */
.pt-scene {
  perspective: 1400px;
  perspective-origin: 50% 40%;
}

.pt-depth {
  transform-style: preserve-3d;
  /* The single transform. Order matters: rise/parallax first in world space,
   * then rotation about the element's own centre, then scale. */
  transform:
    translate3d(0, var(--d-ty), 0)
    rotateX(var(--d-rx))
    rotateY(var(--d-ry))
    scale(var(--d-scale));
  opacity: var(--d-fade);
  will-change: auto;
}

/* The light source stays put while the surface turns, so the shadow swings the
 * opposite way to the tilt. That contrast is most of what sells the depth.
 *
 * Opt-in, because it only makes sense on things that read as a physical
 * surface. Applying it to every .pt-depth drew a floating-card shadow around
 * the hero's text block -- a plain layout div that is parallaxed but is not a
 * card -- which looked like a stray border. Cards ask for it; containers don't. */
.pt-depth[data-depth-shadow] {
  box-shadow: var(--d-sx) var(--d-sy) 42px -18px rgba(33, 27, 23, 0.34);
}

/* Promoted only while the element is actually in view and moving; a permanent
 * will-change on every card costs memory for no benefit. */
.pt-depth.is-live {
  will-change: transform, opacity;
}

/* Specular highlight. Tracks the pointer across the surface, which gives the
 * eye a second cue for the surface angle beyond the silhouette change. */
.pt-depth::after {
  content: '';
  position: absolute;
  inset: 0;
  z-index: 2;
  border-radius: inherit;
  pointer-events: none;
  opacity: var(--d-glare);
  background: radial-gradient(
    38% 44% at var(--d-gx) var(--d-gy),
    rgba(255, 255, 255, 0.5),
    rgba(255, 255, 255, 0.14) 42%,
    transparent 72%
  );
  transition: opacity 260ms ease;
}

/* Content lifted off the card face. Real parallax between a card and its own
 * contents is what separates this from a flat rectangle being rotated.
 *
 * Opt-in via [data-lift] rather than a blanket `.pt-depth > *`. A descendant
 * rule would set `transform` on every child and silently win against, or lose
 * to, transforms those children already carry -- .way-image's hover scale on
 * the home page is exactly that collision. Only elements that ask get lifted. */
.pt-depth [data-lift] {
  transform: translateZ(var(--d-lift, 0));
  transform-style: preserve-3d;
}

.pt-depth [data-lift='1'] { --d-lift: 16px; }
.pt-depth [data-lift='2'] { --d-lift: 34px; }
.pt-depth [data-lift='3'] { --d-lift: 58px; }

/* The hero is the one place worth a deeper stack and a longer throw. */
.pt-hero-scene { perspective: 1800px; }

.pt-parallax {
  transform: translate3d(0, var(--d-ty), 0);
  will-change: transform;
}

/* Scroll velocity, published on :root by the driver and consumed here. Fast
 * scrolling leans the section headers into the direction of travel and lets
 * them settle -- weight and inertia, on elements that are otherwise static. */
.pt-inertia {
  transform: skewY(var(--d-skew, 0deg));
  transition: transform 420ms cubic-bezier(0.16, 1, 0.3, 1);
}

/* ---------------------------------------------------------------------------
 * Reduced motion. Everything above collapses to a flat, still layout: no
 * rotation, no parallax, no glare, no scroll coupling. The driver also refuses
 * to start, so these rules are a belt-and-braces guarantee rather than the only
 * line of defence -- CLAUDE.md requires the guard on every animation.
 * ------------------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .pt-scene,
  .pt-hero-scene {
    perspective: none;
  }

  .pt-depth,
  .pt-parallax,
  .pt-inertia {
    transform: none;
    opacity: 1;
    transition: none;
    will-change: auto;
  }

  .pt-depth[data-depth-shadow] {
    box-shadow: var(--pt-shadow);
  }

  .pt-depth::after {
    opacity: 0;
    transition: none;
  }

  .pt-depth > * {
    transform: none;
  }
}
