/* ============================================================
   Trace live-update stack ("tupdate")
   Replaces the baked fire/heart reaction lotties in the home hero.
   Two kinds of card cycle through each column:
     .tupdate--trace  a Trace push notification (game thumbnail + dots badge)
     .tupdate--chat   a message from a family member in the team chat
   A new card slides in from below while the previous one lifts and fades
   behind it, so the column reads as a feed rather than a slideshow.
   Pure CSS — every animation on a card reads its delay from --d.
   ============================================================ */

.tupdates,
.tupdates *,
.tupdates *::before,
.tupdates *::after { box-sizing: border-box; }

/* Sits inside the existing .updates-div, which owns the positioning. */
.tupdates {
  position: relative;
  width: 100%;
  height: 100%;
  font-family: Inter, sans-serif;
}

/* Narrower than the 310px slot .updates-div gives us. Shrinking the slot
   rather than the card keeps each column pinned to its own edge — the left one
   stays at left:19%, the right at right:19% — so both pull away from the phone
   instead of sliding sideways. */
.updates-div.tupdates-slot { width: 262px; }

/* A resting tilt that cradles the phone between the two columns — each card's
   inner edge lifts toward the centre. It lives on the column, not the card, so
   it never fights the cycle's transform the way animated rotation did; the
   cards also rise along the tilted axis, which keeps the motion consistent.
   Pivoting at the bottom keeps the cards where they were. */
.tupdates { transform-origin: 50% 100%; }
.tupdates--fire { transform: rotate(-3deg); }
.tupdates--hearts { transform: rotate(3deg); }

/* ---- Cycle ---------------------------------------------------
   Three cards, 15s loop, 5s each. --d staggers the card and every
   animated thing inside it from one place. */

.tupdate {
  --d: 0s;
  position: absolute;
  left: 0;
  bottom: 0;
  width: 100%;
  opacity: 0;
  will-change: transform, opacity;
  /* Curves live on the keyframes, not here — a spring applied to every
     segment made the card overshoot on the way out too. */
  animation: tupdate-cycle 15s linear infinite;
  animation-delay: var(--d);
}
.tupdate:nth-child(2) { --d: 5s; }
.tupdate:nth-child(3) { --d: 10s; }

/* The right column trails the left so updates don't land in lockstep. */
.tupdates--hearts .tupdate:nth-child(1) { --d: 2.4s; }
.tupdates--hearts .tupdate:nth-child(2) { --d: 7.4s; }
.tupdates--hearts .tupdate:nth-child(3) { --d: 12.4s; }

/* Arrive from below with a little overshoot, hold, then lift and fade behind
   the next card.

   The exit starts at 33.333% — exactly one 5s slot, which is the instant the
   next card starts entering. The two cross over rather than leaving a gap.
   z-index steps down on that same boundary so the outgoing card never paints
   over the fresh one; the pair of keyframes 0.01% apart makes the flip land
   exactly there instead of halfway through the segment. No rotation in here —
   the column's resting tilt handles that; a card that rotates as it moves
   reads as wrong. */
@keyframes tupdate-cycle {
  0%       { opacity: 0; transform: translateY(20px) scale(.96); z-index: 2;
             animation-timing-function: cubic-bezier(.22,1.12,.36,1); }
  6%       { opacity: 1; transform: translateY(0) scale(1); z-index: 2;
             animation-timing-function: linear; }
  33.32%   { opacity: 1; transform: translateY(0) scale(1); z-index: 2;
             animation-timing-function: step-end; }
  33.33%   { opacity: 1; transform: translateY(0) scale(1); z-index: 1;
             animation-timing-function: cubic-bezier(.36,0,.3,1); }
  41%      { opacity: 0; transform: translateY(-72px) scale(.94); z-index: 1;
             animation-timing-function: linear; }
  100%     { opacity: 0; transform: translateY(-72px) scale(.94); z-index: 1; }
}

/* ---- Card shell ----------------------------------------------
   Separate element from .tupdate so the idle bob has its own transform. */

.tupdate-inner {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 11px 13px;
  border-radius: 18px;
  background-color: #fff;
  border: 1px solid var(--light-stroke, #dfdfdf);
  box-shadow: 0 10px 28px rgba(31, 97, 109, .12), 0 2px 6px rgba(31, 97, 109, .06);
  /* 5s divides the 15s cycle, so a card bobs through the same phase on every
     loop. At 4.2s it drifted and the same card arrived differently each time. */
  animation: tupdate-bob 5s ease-in-out infinite;
  animation-delay: var(--d);
}

/* Barely-there drift, enough to stop the cards reading as static images. */
@keyframes tupdate-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-2px); }
}

/* Chat messages get the softer, message-bubble corner. */
.tupdate--chat .tupdate-inner { border-radius: 18px 18px 18px 6px; }

/* ---- Trace notification: game thumbnail + dots badge ---------- */

.tupdate-thumb {
  position: relative;
  flex: none;
  width: 42px;
  height: 42px;
  border-radius: 11px;
}
.tupdate-thumb img {
  width: 100%;
  height: 100%;
  border-radius: 11px;
  object-fit: cover;
  display: block;
}

.tupdate-badge {
  position: absolute;
  right: -5px;
  bottom: -5px;
  width: 19px;
  height: 19px;
  padding: 4px;
  border-radius: 50%;
  background-color: #fff;
  box-shadow: 0 1px 3px rgba(0, 0, 0, .16);
  color: var(--teal-50, #1f616d);
}
.tupdate-badge svg { width: 100%; height: 100%; display: block; }

/* ---- Team chat: initials avatar ------------------------------- */

/* Initials rather than a photo — it reads as chat instantly and doesn't
   attach an invented name to a real person's face. */
.tupdate-avatar {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 38px;
  height: 38px;
  border-radius: 50%;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: .01em;
  color: #fff;
  background-color: var(--teal-40, #18929a);
}
.tupdate-avatar.is-red { background-color: var(--red, #f35c2b); }
.tupdate-avatar.is-deep { background-color: var(--teal-50, #1f616d); }

/* ---- Text ----------------------------------------------------- */

.tupdate-body { min-width: 0; flex: 1; }

.tupdate-head {
  display: flex;
  align-items: baseline;
  gap: 6px;
  margin-bottom: 1px;
}

.tupdate-app {
  font-size: 12px;
  font-weight: 600;
  letter-spacing: -.01em;
  color: var(--charcoal-50, #393c3c);
  white-space: nowrap;
}

.tupdate-room {
  font-size: 10px;
  font-weight: 400;
  color: var(--charcoal-30, #7b7d7d);
  white-space: nowrap;
}

.tupdate-time {
  margin-left: auto;
  font-size: 11px;
  font-weight: 400;
  color: var(--charcoal-30, #7b7d7d);
}

/* Clamped so every card stays the same height however the copy is edited. */
.tupdate-text {
  margin: 0;
  font-size: 12px;
  font-weight: 400;
  letter-spacing: -.01em;
  color: var(--charcoal-40, #5a5c5c);
  line-height: 1.3;
  display: -webkit-box;
  -webkit-box-orient: vertical;
  -webkit-line-clamp: 2;
  line-clamp: 2;
  overflow: hidden;
}

/* ---- Reaction chip -------------------------------------------- */

/* Lands on the card the way a reaction lands on a message, then takes a
   second bump partway through as if another one came in. */
.tupdate-react {
  position: absolute;
  right: 12px;
  bottom: -11px;
  display: flex;
  align-items: center;
  gap: 3px;
  padding: 3px 8px;
  border-radius: 50px;
  background-color: #fff;
  border: 1px solid var(--light-stroke, #dfdfdf);
  box-shadow: 0 2px 8px rgba(31, 97, 109, .14);
  font-size: 11px;
  line-height: 1;
  opacity: 0;
  transform-origin: 70% 60%;
  animation: tupdate-react 15s linear infinite;
  animation-delay: var(--d);
}

.tupdate-react-count {
  font-family: Inter, sans-serif;
  font-size: 10px;
  font-weight: 500;
  color: var(--charcoal-30, #7b7d7d);
}

/* Holds at full opacity to the end of the loop — the card's own fade takes the
   chip out. Fading it on its own timeline double-faded it on exit. The reset
   back to 0% happens while the card is already invisible. */
@keyframes tupdate-react {
  0%, 8%   { opacity: 0; transform: scale(.3);
             animation-timing-function: cubic-bezier(.34,1.7,.5,1); }
  13%      { opacity: 1; transform: scale(1); animation-timing-function: linear; }
  20%      { opacity: 1; transform: scale(1); animation-timing-function: ease-out; }
  22%      { opacity: 1; transform: scale(1.1); animation-timing-function: ease-in; }
  24%      { opacity: 1; transform: scale(1); animation-timing-function: linear; }
  100%     { opacity: 1; transform: scale(1); }
}

/* ---- Reaction burst ------------------------------------------- */

/* Three emoji off the chip on separate arcs. They start outside the card's
   bottom-right corner and travel further out, never across the text. */
.tupdate-float {
  position: absolute;
  right: -4px;
  bottom: -12px;
  font-size: 13px;
  line-height: 1;
  opacity: 0;
  pointer-events: none;
  animation: tupdate-float-a 15s ease-out infinite;
  animation-delay: var(--d);
}
.tupdate-float:nth-of-type(2) {
  right: -15px;
  font-size: 11px;
  animation-name: tupdate-float-b;
}
.tupdate-float:nth-of-type(3) {
  right: 6px;
  font-size: 10px;
  animation-name: tupdate-float-c;
}

/* All three finish before 33.33%, so nothing is still drifting when the card
   starts to leave. Pop out fast, drift away slow. */
@keyframes tupdate-float-a {
  0%, 11%   { opacity: 0; transform: translate(0, 0) scale(.4) rotate(0deg);
              animation-timing-function: cubic-bezier(.2,.9,.3,1); }
  15%       { opacity: 1; transform: translate(7px, -14px) scale(1.05) rotate(6deg);
              animation-timing-function: cubic-bezier(.4,0,.7,.5); }
  26%       { opacity: 0; transform: translate(18px, -40px) scale(.85) rotate(13deg); }
  100%      { opacity: 0; transform: translate(18px, -40px) scale(.85) rotate(13deg); }
}
@keyframes tupdate-float-b {
  0%, 15%   { opacity: 0; transform: translate(0, 0) scale(.4) rotate(0deg);
              animation-timing-function: cubic-bezier(.2,.9,.3,1); }
  19%       { opacity: 1; transform: translate(15px, -17px) scale(1) rotate(-7deg);
              animation-timing-function: cubic-bezier(.4,0,.7,.5); }
  30%       { opacity: 0; transform: translate(31px, -46px) scale(.8) rotate(-14deg); }
  100%      { opacity: 0; transform: translate(31px, -46px) scale(.8) rotate(-14deg); }
}
@keyframes tupdate-float-c {
  0%, 18%   { opacity: 0; transform: translate(0, 0) scale(.4) rotate(0deg);
              animation-timing-function: cubic-bezier(.2,.9,.3,1); }
  22%       { opacity: 1; transform: translate(-6px, -15px) scale(.95) rotate(9deg);
              animation-timing-function: cubic-bezier(.4,0,.7,.5); }
  32%       { opacity: 0; transform: translate(-14px, -38px) scale(.75) rotate(17deg); }
  100%      { opacity: 0; transform: translate(-14px, -38px) scale(.75) rotate(17deg); }
}

/* ---- Narrow screens ------------------------------------------- */

@media screen and (max-width: 479px) {
  /* At 375px the two 200px columns overlap by 55px horizontally. The old
     lotties were mostly transparent particles so it never showed; solid cards
     do. Drop the right column clear of the left one — far enough that the
     lifting card at handover still clears it. Scoped to .tupdates-slot so the
     live-updates page keeps its own placement. */
  .updates-div.right.tupdates-slot { top: 58vh; }

  /* Narrower again on a phone-width screen — at 200px the pair sat right over
     the video. 158px plus a tighter inset on each side keeps the cards to the
     margins so the phone screen stays mostly clear. */
  .updates-div.tupdates-slot { width: 158px; }
  .updates-div.left.tupdates-slot { left: 10px; }
  .updates-div.right.tupdates-slot { right: 4px; }

  .tupdate-inner { gap: 7px; padding: 8px 9px; border-radius: 14px; }
  .tupdate--chat .tupdate-inner { border-radius: 14px 14px 14px 5px; }
  .tupdate-thumb { width: 30px; height: 30px; border-radius: 8px; }
  .tupdate-thumb img { border-radius: 8px; }
  .tupdate-avatar { width: 28px; height: 28px; font-size: 10px; }
  .tupdate-badge { width: 14px; height: 14px; padding: 3px; right: -4px; bottom: -4px; }
  .tupdate-app { font-size: 11px; }
  .tupdate-room { display: none; }
  .tupdate-time { font-size: 10px; }
  /* A 200px card can't hold the copy in two lines without truncating. */
  .tupdate-text { font-size: 11px; -webkit-line-clamp: 3; line-clamp: 3; }

  /* Same shape as the desktop cycle, lifting less so cards stay in their slot. */
  @keyframes tupdate-cycle {
    0%       { opacity: 0; transform: translateY(16px) scale(.96); z-index: 2;
               animation-timing-function: cubic-bezier(.22,1.12,.36,1); }
    6%       { opacity: 1; transform: translateY(0) scale(1); z-index: 2;
               animation-timing-function: linear; }
    33.32%   { opacity: 1; transform: translateY(0) scale(1); z-index: 2;
               animation-timing-function: step-end; }
    33.33%   { opacity: 1; transform: translateY(0) scale(1); z-index: 1;
               animation-timing-function: cubic-bezier(.36,0,.3,1); }
    41%      { opacity: 0; transform: translateY(-48px) scale(.94); z-index: 1;
               animation-timing-function: linear; }
    100%     { opacity: 0; transform: translateY(-48px) scale(.94); z-index: 1; }
  }
}

/* ---- Reduced motion ------------------------------------------- */

/* Hold the first card of each column, reaction shown, nothing moving. */
@media (prefers-reduced-motion: reduce) {
  .tupdate,
  .tupdate-inner,
  .tupdate-react,
  .tupdate-float { animation: none; }
  .tupdate { opacity: 0; transform: none; }
  .tupdate:nth-child(1) { opacity: 1; }
  .tupdate:nth-child(1) .tupdate-react { opacity: 1; transform: none; }
  .tupdate-float { opacity: 0; }
}
