/* PTR hero figure — RIGHT side (Roy).
   Purely additive: nothing in the existing hero is moved, restyled or hidden.
   Everything is namespaced .ptr-figure / #ptrRoy* so the LEFT-side robot lane
   can drop its own module in beside this one without a collision.

   Layout contract between the two lanes:
     left  slot  -> .ptr-figure--left   (robot, other session)
     right slot  -> .ptr-figure--right  (Roy, this file)
   Both sit above the hero background and BELOW the logo/CTA layer, and both
   are pointer-events:none so the existing buttons keep every click. */

.ptr-figure {
  position: absolute;
  bottom: 0;
  z-index: 0;                 /* DOM order puts it between #cinBg and #cinFlat */
  pointer-events: none;
  will-change: transform, opacity;
  /* Default VISIBLE. The fade-in is opt-in via .is-loading, which the JS removes
     once ready. Previously this defaulted to opacity:0 and relied on .is-ready
     plus a transition to reveal it - so any state where frames were not
     advancing when the class landed left the figure permanently invisible.
     Failure mode is now "visible without a fade" instead of "not there". */
  opacity: 1;
  transition: opacity .9s ease;
}
.ptr-figure.is-loading { opacity: 0; }
.ptr-figure.is-ready { opacity: 1; }

.ptr-figure--right {
  right: var(--ptr-roy-right, 0px);
  /* Taller: his head should reach the top of the PTR lockup rather than
     starting below it. --ptr-roy-h is overridable per-request for tuning.

     PROPORTIONAL TO THE SCREEN, which the old pair of px caps was not.
     His apparent size on desktop is set by the canvas HEIGHT and nothing else:
     the camera's vertical fov is fixed, so the visible world height is fixed
     and the model scales with the box. `min(112%, 1000px)` therefore stopped
     him growing at 893px of viewport height, and `clamp(..., 760px)` stopped
     the box widening at 1900px of viewport width. Measured against the
     deployed build: 1024x768 gave 410x860 (both live), 1440x900 gave 576x1000
     (height capped), 1920x1080 and 2560x1080 BOTH gave 760x1000 - two very
     different screens rendering an identical Roy, which is the defect.
     The caps that remain are sanity ceilings an order of magnitude out of the
     way, not layout: 1600px of height is 1429px of viewport, above any
     desktop display, and it exists so a mis-measured host cannot ask for a
     canvas buffer of unbounded size at devicePixelRatio 1.75. */
  width: var(--ptr-roy-w, clamp(340px, 44vw, 1400px));
  height: var(--ptr-roy-h, min(112%, 1600px));
  bottom: var(--ptr-roy-bottom, -6%);
}

/* Reserved for the robot lane. Declared here only so both sides share one
   contract; this file never renders into it. */
.ptr-figure--left {
  left: 0;
  width: clamp(280px, 30vw, 560px);
  height: min(80%, 700px);
}

.ptr-figure canvas,
.ptr-figure img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  object-position: bottom center;
}

/* No scrim. An earlier version painted a gradient panel behind the figure to
   protect the lockup; it read as a hard black box against the purple hero.
   The figure is transparent PNG/WebGL over the live background instead. */

/* Desktop shows the live 3D canvas; mobile shows the looping sprite. */
.ptr-figure__sprite { display: none; }
/* 699px, NOT an invented value: the page defines its own mobile as
   `this.mob = window.innerWidth < 700`. A laptop between 700 and 900 px was
   landing in our mobile band while the page ran its desktop path. */
@media (max-width: 699px) {
  /* Specificity note: the generic `.ptr-figure canvas` rule above is (0,1,1),
     so a bare `.ptr-figure__canvas` (0,1,0) loses to it and the canvas stays
     visible. Match the element too so this actually wins. */
  /* Mobile uses the SPRITE, not WebGL. Operator call: the model read poorly at
     phone size and the headset was hard to make out. The sprite is rendered
     with a tighter crop so the headset is legible. */
  .ptr-figure canvas.ptr-figure__canvas { display: none; }
  .ptr-figure__sprite { display: block; }

  /* Mobile is deliberately oversized and bleeds off the edge.
     - aspect-ratio drives the width from the height, so `contain` never
       letterboxes him into a narrow strip inside a too-wide box
     - translateX(50%) lands his CENTRE exactly on the viewport edge, so half
       of him is off-screen by construction rather than by a guessed offset
     - the page already sets overflow-x: clip on html/body, so the overhang
       cannot produce a horizontal scrollbar */
  .ptr-figure--right {
    height: var(--ptr-roy-h-m, 82%);
    /* matches the rendered sprite, 560x760 */
    aspect-ratio: var(--ptr-roy-ar-m, 560 / 760);
    width: auto;
    right: var(--ptr-roy-right-m, 0px);
    bottom: var(--ptr-roy-bottom-m, -2%);
    /* 20% of his width sits off the edge, so 80% stays in frame */
    transform: translateX(var(--ptr-roy-shift-m, 20%));
  }
  .ptr-figure--right .ptr-figure__sprite,
  .ptr-figure--right img {
    object-fit: contain;
    object-position: bottom center;
  }
}

/* Small phones: same treatment, a touch less height so he clears the CTA row. */
@media (max-width: 520px) {
  .ptr-figure--right {
    height: var(--ptr-roy-h-m, 76%);
    transform: translateX(var(--ptr-roy-shift-m, 18%));
  }
}

@media (prefers-reduced-motion: reduce) {
  .ptr-figure { transition: none; }
}
