/* CallPilot Human Assistant player — local motion packs, no runtime face API. */

.human-assistant {
  position: relative;
  display: block;
  overflow: hidden;
  aspect-ratio: 9 / 16;
  width: 100%;
  background: transparent;
  isolation: isolate;
}

.human-assistant__poster,
.human-assistant__layer {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
}

/* The still portrait is the first paint and the autoplay-blocked fallback. */
.human-assistant__poster {
  z-index: 0;
  opacity: 1;
  transition: opacity 240ms ease;
  transform-origin: 50% 72%;
  will-change: transform, filter;
}

/* Silent liveness without fake speech: gently move the whole portrait rather
   than playing a generated clip whose lips may articulate incidentally. */
.human-assistant--resting .human-assistant__poster {
  animation: humanAssistantSilentPresence 11s cubic-bezier(0.45, 0, 0.55, 1) infinite;
}

/* A resting portrait is the ONLY thing that can guarantee a closed mouth, so
   the liveness has to come from here. The previous curve moved 3.5px over
   4.4s at scale(1.012) - below the threshold most people can see, which is
   why a "breathing" portrait still read as a dead photograph.

   Two things make this read as a person rather than a loop:
   - enough travel to actually perceive (~9px rise, 2.2% scale),
   - an 11s period with asymmetric, non-mirrored keyframes, so the eye does
     not lock onto a 4-second sine and call it mechanical. */
@keyframes humanAssistantSilentPresence {
  0%   { transform: translate3d(0, 0, 0) scale(1) rotate(0deg); filter: brightness(1); }
  14%  { transform: translate3d(-1.1px, -4.4px, 0) scale(1.009) rotate(-0.12deg); filter: brightness(1.011); }
  31%  { transform: translate3d(-1.6px, -9px, 0) scale(1.022) rotate(-0.22deg); filter: brightness(1.026); }
  46%  { transform: translate3d(-0.6px, -6.2px, 0) scale(1.016) rotate(-0.08deg); filter: brightness(1.018); }
  59%  { transform: translate3d(1.3px, -2.4px, 0) scale(1.007) rotate(0.14deg); filter: brightness(1.008); }
  73%  { transform: translate3d(1.7px, -5.1px, 0) scale(1.013) rotate(0.21deg); filter: brightness(1.015); }
  87%  { transform: translate3d(0.5px, -1.6px, 0) scale(1.004) rotate(0.06deg); filter: brightness(1.004); }
  100% { transform: translate3d(0, 0, 0) scale(1) rotate(0deg); filter: brightness(1); }
}

.human-assistant__poster.is-hidden {
  opacity: 0;
}

/* Two stacked layers cross-fade so motion changes never visibly snap. */
.human-assistant__layer {
  z-index: 1;
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
}

.human-assistant__layer.is-visible {
  opacity: 1;
  z-index: 2;
}

/* Audio drain is a hard visual boundary, not a cross-fade. A stale rendered
   video frame must disappear in the same task that enters resting mode. */
.human-assistant--resting .human-assistant__layer,
.human-assistant--resting .human-assistant__layer.is-visible {
  opacity: 0;
  transition: none;
}

@media (prefers-reduced-motion: reduce) {
  .human-assistant--resting .human-assistant__poster {
    /* Drop the movement, keep the presence. Freezing to a flat photograph
       reads as broken; a slow luminance breath carries no vestibular risk. */
    animation: humanAssistantSilentBreath 9s ease-in-out infinite;
  }

  @keyframes humanAssistantSilentBreath {
    0%, 100% { filter: brightness(1); }
    50% { filter: brightness(1.035); }
  }
  .human-assistant__layer {
    display: none;
  }

  .human-assistant__poster.is-hidden {
    opacity: 1;
  }
}
