/* ============================================================================
   emily-idle.css — the only new CSS in the idle feature.

   EVERY SELECTOR IN THIS FILE IS GATED ON [data-idle] ON .emily, and
   `data-idle` is set by emily-idle.js and by nothing else. So removing the
   <script> tag alone already makes this entire file inert; removing the
   <link> as well only removes the bytes. That is rollback level 2.

   emily.css is byte-identical to what shipped. Nothing here overrides it —
   .emily__idle is a class that does not exist in emily.css, and every rule is
   further scoped by an attribute emily.css never sets.
   ---------------------------------------------------------------------------- */

.emily[data-idle] {
  /* --------------------------------------------------------------- tuning --
     Three durations, all resolving to existing tbg.css tokens. Retuning the
     feel of the feature is a change to these three lines and nothing else —
     no JavaScript edit, no re-render, no redeploy of assets. emily-idle.js
     reads these back with getComputedStyle, so the CSS is the source of truth
     for timing, not a duplicated constant in the script. */

  /* idle clip -> idle clip. P2b §2.1 set 320ms as the default and pre-
     authorised var(--dur-slow) "if P2a's per-clip seam numbers come back
     materially above the P1 P95 target for a substantial share of the
     library". They did: 12 of 12 clips exceed both the P95 and the 0.60
     absolute guard, mean seam 1.491 against a 0.60 bar (P2a §c). So this
     ships at the long token, which is the retune P2b already sanctioned for
     exactly this measurement — not a new number invented here. */
  --emily-idle-fade: var(--dur-slow);   /* 560ms */

  /* idle -> speaking. Short by design: this is a swap between two moving
     representations of the same face, and anything past ~160ms reads as a
     fade rather than a handover. P2b §3.1. */
  --emily-idle-out:  var(--dur-fast);   /* 120ms */

  /* speaking -> idle. WAS 560ms, on the reasoning that a loose gap
     (measured 1.296 whole-frame) warranted a long fade. Shortened to 120ms
     after measuring what the long fade actually costs.

     Through this whole transition the speaking clip's frozen last frame stays
     painted underneath while the idle layer's opacity animates over it, so
     the viewer sees two faces in different poses superimposed. Building that
     exact composite and measuring it across six clips puts the
     sharpness loss at 5.7% at mid-blend. A longer fade does not reduce that
     loss; it only holds it on screen longer -- 14 frames at 560ms against 3
     at 120ms. That is this file's own idle-to-idle finding applied to the
     other direction: "a cross-fade cannot be made invisible, it can only be
     made slower."

     Now symmetric with --emily-idle-out, which is the same swap in reverse
     and has been at 120ms since it shipped without complaint. */
  --emily-idle-in:   var(--dur-fast);   /* 120ms */
}

/* --- geometry -----------------------------------------------------------
   DUPLICATED, DELIBERATELY, from emily.css:457-467. The idle layers must
   register pixel-for-pixel with the shipped speaking video or the cross-fade
   between them reads as a jump cut. They cannot simply be given the
   .emily__video class to inherit it, because emily.css:120 hides
   .emily__video under [data-video="off"] — which is the text-only state where
   idle is most wanted.

   THIS IS A REAL COUPLING. If anyone changes the hood geometry in emily.css,
   this block must change in the same commit. P1 §c.8.2, P2b §8.               */
.emily[data-idle] .emily__idle {
  position: absolute;
  top: 0; left: 50%;
  width: 150%;
  height: auto;
  min-height: 100%;
  transform: translateX(-50%);
  object-fit: cover;
  object-position: 50% 0;

  opacity: 0;
  pointer-events: none;          /* decoration: participates in nothing */
  background: transparent;       /* never paint a black box before frame 1 */

  /* LINEAR, non-negotiable. Both layers are the same face at close-to-neutral
     pose; an eased pair puts a luminance dip at the midpoint because both
     spend the middle of the fade below 50% opacity. P1 §c.4, P2b §2.2. */
  transition: opacity var(--emily-idle-fade) linear;
}

/* Fade kind. Set by emily-idle.js on the .emily root so all three durations
   stay retunable from CSS alone. */
.emily[data-idle][data-idle-fade="in"]  .emily__idle { transition-duration: var(--emily-idle-in); }
.emily[data-idle][data-idle-fade="out"] .emily__idle { transition-duration: var(--emily-idle-out); }

/* --- states -------------------------------------------------------------
   `yield` and `paused` are belt-and-braces: the script already drives opacity
   inline, and these make the intent declarative and inspectable. Show/hide is
   ALWAYS opacity, never display — dropping display would tear down the decode
   pipeline and cost a re-init on every handover. P1 §c.1, P2b §13.7.         */
.emily[data-idle="loading"] .emily__idle,
.emily[data-idle="yield"]   .emily__idle,
.emily[data-idle="paused"]  .emily__idle { opacity: 0; }

/* --- accessibility ------------------------------------------------------
   emily-idle.js never creates a layer at all when prefers-reduced-motion
   matches at boot, and tears down if it becomes true mid-session, so this
   block should be unreachable. It exists because an unreachable safety net
   costs two lines and a reachable one costs a complaint.                     */
@media (prefers-reduced-motion: reduce) {
  .emily[data-idle] .emily__idle { opacity: 0 !important; transition: none !important; }
}
