/* Live — "one session, one key": the leader steps the key, the change hops
   down a wire from avatar to avatar, and each player's row re-spells in turn.
   Built by motion/live.js inside #live .mock; everything here is scoped under
   .lv and only ever moves transform, opacity and (for the row wipe) clip-path.
   Every added piece is absolutely positioned, so nothing reflows. */

.lv { position: relative; }
.lv .lv-row { position: relative; isolation: isolate; }
.lv .avatar { position: relative; }

/* ---------- a letter that rolls: new one in, old one out, in one grid cell ---------- */
/* Beat site.css's `.mock-row .who span { display: block }`, which would
   otherwise catch these spans too. */
.lv .lv-row .lv-roll { position: relative; display: inline-grid; justify-items: start; vertical-align: baseline; }
.lv .lv-row .lv-roll > .lv-v {
  display: block; grid-area: 1 / 1; white-space: nowrap;
  opacity: 0; transform: translateY(0.55em);
  transition: opacity 160ms var(--ease-out), transform 160ms var(--ease-out);
}
.lv .lv-row .lv-roll > .lv-v.on { opacity: 1; transform: none; transition-duration: 300ms; }
.lv .lv-row .lv-roll > .lv-v.lv-below { transform: translateY(0.55em); transition: none; }
.lv .lv-row .lv-roll > .lv-v.lv-above { transform: translateY(-0.55em); transition: none; }
.lv .lv-row .lv-roll > .lv-v.lv-out-up { transform: translateY(-0.55em); }
.lv .lv-row .lv-roll > .lv-v.lv-out-down { transform: translateY(0.55em); }
/* In running text only the letter showing takes room, so at rest the line
   reads exactly as written ("F shapes", never "F  shapes"). */
.lv .lv-row .lv-roll.lv-tok > .lv-v:not(.on) { position: absolute; top: 0; left: 0; }
/* …and the leaving letter stays against the word before it, which in he/ar
   is on its right. */
[dir="rtl"] .lv .lv-row .lv-roll.lv-tok > .lv-v:not(.on) { left: auto; right: 0; }
.lv .lv-btxt { display: inline; }

/* ---------- a row receiving the change: a soft wipe from the wire outward ---------- */
.lv .lv-tint {
  position: absolute; inset: 0; z-index: -1; pointer-events: none;
  background: var(--brand-50);
  opacity: 0; clip-path: inset(0 100% 0 0);
  /* Fade out first; only then put the wipe back to its start. */
  transition: opacity 700ms ease, clip-path 0s linear 700ms;
}
[dir="rtl"] .lv .lv-tint { clip-path: inset(0 0 0 100%); }
.lv .lv-hit > .lv-tint {
  opacity: 1; clip-path: inset(0 0 0 0);
  transition: opacity 120ms ease, clip-path 460ms var(--ease-out);
}

/* ---------- presence: each player's dot pings as their screen follows ---------- */
.lv .lv-dot {
  position: absolute; inset-inline-end: -2px; bottom: -2px;
  width: 12px; height: 12px; border-radius: 50%;
  background: var(--success-500); box-shadow: 0 0 0 2px #fff;
}
.lv .lv-dot::after {
  content: ""; position: absolute; inset: 0; border-radius: 50%;
  background: var(--success-500); opacity: 0;
}
.lv .lv-got .lv-dot::after { animation: lv-ping 900ms var(--ease-out); }
@keyframes lv-ping { from { opacity: .55; transform: scale(1); } to { opacity: 0; transform: scale(2.6); } }

/* ---------- the leader: a ring while choosing, released as a wave ---------- */
.lv .lv-ripple {
  position: absolute; inset: -1px; border-radius: 50%; pointer-events: none;
  box-shadow: 0 0 0 2px var(--brand-400);
  opacity: 0; transform: scale(1);
  transition: opacity 200ms var(--ease-out), transform 200ms var(--ease-out);
}
.lv .lv-picking .lv-ripple { opacity: 1; transform: scale(1.12); transition-duration: 240ms; }
.lv .lv-send .lv-ripple { animation: lv-wave 900ms var(--ease-out); }
@keyframes lv-wave { from { opacity: 1; transform: scale(1.12); } to { opacity: 0; transform: scale(1.9); } }

/* ---------- the leader's key stepper (the hero's − key + control) ----------
   Compact, and it takes the place of the row's "Leading" label (same end
   edge; the label fades while the stepper is up), so it covers no words.
   live.js sets --lv-s below 1 when a language leaves less free room than the
   stepper needs, so it still never reaches the leader's description. */
.lv .lv-pop {
  --lv-s: 1;
  position: absolute; inset-inline-end: 20px; top: 50%; z-index: 2;
  display: flex; align-items: stretch;
  background: #fff; border-radius: var(--radius-md);
  box-shadow: inset 0 0 0 1px var(--gray-300), var(--shadow-lg);
  font-size: var(--text-xs); line-height: 16px; font-weight: 600; color: var(--gray-700);
  pointer-events: none;
  opacity: 0; transform: translateY(-50%) scale(calc(var(--lv-s) * 0.96)); transform-origin: 100% 50%;
  /* Closing is quicker than opening. */
  transition: opacity 140ms ease-out, transform 140ms var(--ease-out);
}
[dir="rtl"] .lv .lv-pop { transform-origin: 0 50%; }
.lv .lv-picking .lv-pop { opacity: 1; transform: translateY(-50%) scale(var(--lv-s)); transition-duration: 220ms; }
.lv .lv-row .state { transition: opacity 200ms ease; }
.lv .lv-picking .state { opacity: 0; transition-duration: 120ms; }
.lv .lv-step { display: flex; align-items: stretch; }
.lv .lv-btn, .lv .lv-key { position: relative; padding: 4px 8px; }
.lv .lv-btn + .lv-key, .lv .lv-key + .lv-btn { box-shadow: inset 1px 0 0 var(--gray-300); }
.lv .lv-key { color: var(--brand-700); }
.lv .lv-row .lv-roll.lv-pk { justify-items: center; min-width: 1.6em; }
.lv .lv-btn { isolation: isolate; transition: transform 160ms var(--ease-out); }
.lv .lv-btn::before {
  content: ""; position: absolute; inset: 3px; z-index: -1; border-radius: var(--radius-sm);
  background: var(--brand-50); opacity: 0; transition: opacity 160ms ease;
}
.lv .lv-btn.lv-press { transform: scale(0.9); }
.lv .lv-btn.lv-press::before { opacity: 1; }

/* ---------- the wire between avatars ---------- */
.lv .lv-wire {
  position: absolute; width: 2px; border-radius: 1px; overflow: hidden; pointer-events: none;
  background: var(--gray-200);
}
.lv .lv-fill {
  position: absolute; inset: 0; background: var(--brand-500);
  transform: scaleY(0); transform-origin: 50% 0;
}
/* Constant travel reads as linear. */
.lv .lv-wire.lv-on .lv-fill { transform: scaleY(1); transition: transform 150ms linear; }
.lv .lv-wire.lv-off .lv-fill { transform: scaleY(1); opacity: 0; transition: opacity 600ms ease; }

/* Snapping to a resting frame (off-screen only). */
.lv.lv-snap *, .lv.lv-snap *::before, .lv.lv-snap *::after { transition: none !important; animation: none !important; }

@media (prefers-reduced-motion: reduce) {
  .lv *, .lv *::before, .lv *::after { transition: none !important; animation: none !important; }
}
