/* Effects the app already has (SPEC §5), driven by effects.js. Their cyan→pink is MOQI's own colour.
   Sources: ui/MainActivity.kt PulsingHeart, ui/Practice.kt Hearts, TypingBubble, InputBar caret, Skeleton. */

.fx-heart,
.fx-hearts,
.fx-typing,
.fx-caret,
.fx-skeleton {
  --fx-cyan: var(--moqi-cyan);    /* app accent (tokens.css) */
  --fx-pink: var(--moqi-pink);    /* app secondary */
  --fx-cyan-ink: var(--color-cyan-ink);
  --fx-muted: var(--color-muted);
  --fx-bubble: var(--color-card);
  --fx-rule: var(--color-rule);
}

.fx-paused,
.fx-paused * { animation-play-state: paused !important; }

/* ---------------------------------------------------------------- closeness heart */

.fx-heart {
  position: relative;
  display: inline-grid;
  place-items: center;
  inline-size: var(--fx-heart-size, 22px);
  block-size: var(--fx-heart-size, 22px);
  flex: none;
  vertical-align: middle;
}

.fx-heart__ring {
  position: absolute;
  inset: 0;
  border-radius: 50%;
  background: currentColor;
  opacity: 0;
  pointer-events: none;
}

.fx-heart__icon {
  position: relative;
  inline-size: 100%;
  block-size: 100%;
  fill: currentColor;
  overflow: visible;
}

/* ---------------------------------------------------------------- little hearts drifting up */

.fx-hearts {
  position: absolute;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

.fx-hearts__track {
  position: absolute;
  inset: 0;
  animation: fx-rise 3.6s linear infinite;
}

.fx-hearts__heart {
  position: absolute;
  top: 0;
  fill: currentColor;
  opacity: 0;
  animation: fx-heart-fade 3.6s linear infinite;
}

@keyframes fx-rise {
  from { transform: translate3d(0, 100%, 0); }
  to { transform: translate3d(0, 0, 0); }
}

/* sin(k·π) · .85 */
@keyframes fx-heart-fade {
  0% { opacity: 0; animation-timing-function: cubic-bezier(.61, 1, .88, 1); }
  50% { opacity: .85; animation-timing-function: cubic-bezier(.12, 0, .39, 0); }
  100% { opacity: 0; }
}

/* ---------------------------------------------------------------- typing bubble */

.fx-typing {
  display: inline-flex;
  align-items: center;
  gap: 5px;
}

.fx-typing--bubble {
  padding: 14px 16px;
  border-radius: 20px 20px 20px 6px;
  background: var(--fx-bubble);
  box-shadow: inset 0 0 0 1px var(--fx-rule);
}

.fx-typing__dot {
  inline-size: 7px;
  block-size: 7px;
  border-radius: 50%;
  background: var(--fx-muted);
  animation: fx-dot 900ms infinite;
}

/* k = (sin((phase - i·.18)·2π) + 1) / 2: up 4px and from half to full colour */
@keyframes fx-dot {
  0% { transform: translate3d(0, -2px, 0); opacity: .75; animation-timing-function: cubic-bezier(.61, 1, .88, 1); }
  25% { transform: translate3d(0, -4px, 0); opacity: 1; animation-timing-function: cubic-bezier(.37, 0, .63, 1); }
  75% { transform: translate3d(0, 0, 0); opacity: .5; animation-timing-function: cubic-bezier(.12, 0, .39, 0); }
  100% { transform: translate3d(0, -2px, 0); opacity: .75; }
}

.fx-typing__dot:nth-child(2) { animation-delay: -738ms; }
.fx-typing__dot:nth-child(3) { animation-delay: -576ms; }

/* ---------------------------------------------------------------- caret */

.fx-caret {
  display: inline-block;
  inline-size: 2px;
  block-size: 1.15em;
  margin-inline-start: 1px;
  vertical-align: text-bottom;
  border-radius: 1px;
  background: var(--fx-cyan-ink);
  animation: fx-caret 530ms cubic-bezier(.4, 0, .2, 1) infinite alternate;
}

@keyframes fx-caret {
  from { opacity: 1; }
  to { opacity: 0; }
}

/* ---------------------------------------------------------------- skeleton */

.fx-skeleton {
  display: grid;
  gap: 8px;
}

.fx-skeleton__row {
  block-size: 44px;
  border-radius: 12px;
  background: var(--fx-skeleton-fill, var(--color-paper-3));
  animation: fx-skel 650ms cubic-bezier(.4, 0, .2, 1) infinite alternate;
}

.fx-skeleton__row:first-child { block-size: 56px; }

@keyframes fx-skel {
  from { opacity: .45; }
  to { opacity: 1; }
}
