/* ── Emoji reactions ────────────────────────────────────────────────────────
   Paired with reactions.js (REACT). Loaded on index.html, trivia.html and
   versus.html, because the picker rides the player sheet and the sheet appears
   on every board.

   Minimum font size in this app is 0.82rem (~13pt). Nothing here goes under it;
   the emoji sizes are px and well above.                                      */

/* ── The picker, inside the player sheet ─────────────────────────────────── */
.rx-row {
  display: flex; gap: 6px; margin: 0 0 10px;
}
.rx-e {
  flex: 1 1 0; min-width: 0; height: 46px;
  display: flex; align-items: center; justify-content: center;
  font-size: 22px; line-height: 1;
  background: var(--surface); border: 1.5px solid var(--rule); border-radius: 12px;
  cursor: pointer; padding: 0;
  transition: transform .12s ease, border-color .12s ease, background .12s ease, opacity .2s ease;
  -webkit-tap-highlight-color: transparent;
}
.rx-e:active { transform: translateY(1px); }
.rx-e.on {
  border-color: var(--accent); background: rgba(31, 138, 117, 0.10);
  transform: translateY(-1px);
}
/* Dimmed, never disabled — a tap still has to reach the handler so the goat can
   refuse out loud. `pointer-events` stays on for exactly that reason. */
.rx-e.dim { opacity: 0.35; }

/* The refusal. Three quick shakes, then it settles and greys. */
.rx-e.wobble { animation: rxWobble .45s cubic-bezier(.36,.07,.19,.97); }
@keyframes rxWobble {
  10%, 90% { transform: translateX(-2px); }
  20%, 80% { transform: translateX(3px); }
  30%, 50%, 70% { transform: translateX(-4px); }
  40%, 60% { transform: translateX(4px); }
}

/* NOTHING ABOUT THE REVEAL IS TRANSITIONED, and the space is reserved rather
   than animated open. Both halves of that are the same lesson.

   A CSS transition only advances on paint, and an idle or backgrounded WKWebView
   does not paint — the challenge spotlight shipped stuck at opacity 0.088 for
   exactly this reason (CLAUDE.md). This line is the ONLY place the one-goat-a-day
   rule ever explains itself, so if it stalls, the joke silently never lands. A
   transitioned HEIGHT fails the same way and is worse: the text can reach full
   opacity inside a zero-height overflow:hidden box and still be invisible, which
   is precisely what the e2e case caught here.

   So the row always occupies its 20px — the sheet opens at its final height and
   nothing moves when the note arrives, matching how the head-to-head chips above
   it already behave. Only the fade OUT is animated, because that one is safe to
   stall: the worst case is a message that lingers. */
.rx-note {
  font-size: 0.85rem; font-weight: 600; color: var(--muted);
  text-align: center; height: 20px; margin-bottom: 8px; overflow: hidden;
  opacity: 0; transition: opacity .3s ease;
}
.rx-note.show { opacity: 1; transition: none; }

/* ── The cluster under a board row ───────────────────────────────────────── */
.rx-cluster {
  display: inline-flex; align-items: center; gap: 2px;
  padding: 2px 7px 2px 5px; margin-left: 6px;
  background: var(--surface); border: 1px solid var(--rule); border-radius: 999px;
  cursor: pointer; vertical-align: middle;
  -webkit-tap-highlight-color: transparent;
}
.rx-cluster:active { background: var(--rule); }

/* The bloom used to live in this wrapper and was hidden here when a cluster was
   present. Both are gone: LB._youMark now renders the word "(you)" on every
   board, unconditionally, so there is nothing to hide and nothing that appears
   or disappears depending on whether anyone has reacted. See LB._youMark. */
.rx-ce { font-size: 14px; line-height: 1; }
.rx-cn { font-size: 0.82rem; font-weight: 700; color: var(--muted); margin-left: 2px; }

/* ── The float ───────────────────────────────────────────────────────────────
   ABOVE EVERYTHING, and never in the way of anything.

   `pointer-events: none` is load-bearing: this layer covers the whole viewport
   while it is playing, and without it the today card's CTA is unreachable for
   the two-and-a-bit seconds the waft lasts.

   `will-change: transform` is ALSO load-bearing, and not for performance. In a
   WKWebView, GPU-promoted layers on Home (the score count-up, the CTA's press
   transform) can paint above any z-index regardless of stacking order — the same
   compositing behaviour behind the "hide <body> when an overlay opens" rule. We
   cannot hide the body here, because watching these drift over the card is the
   entire point, so the layer promotes itself and competes on the same terms.   */
.rx-float {
  position: fixed; inset: 0;
  pointer-events: none;
  z-index: 2147483000;
  overflow: hidden;
  will-change: transform;
}
.rx-f {
  position: absolute; bottom: 38%;
  line-height: 1;
  animation-name: rxWaft;
  animation-timing-function: cubic-bezier(.33,.1,.5,1);
  animation-fill-mode: both;
  will-change: transform, opacity;
}
/* The rise and the sway in one transform, because two animations cannot both
   drive `transform` on one element. The sway alternates sides across the climb
   so it reads as drift rather than as a straight line. */
@keyframes rxWaft {
  0%   { transform: translate3d(0, 0, 0) scale(.7); opacity: 0; }
  12%  { opacity: 1; }
  25%  { transform: translate3d(var(--rx-sway, 0px), calc(var(--rx-rise, -210px) * .25), 0) scale(1); }
  50%  { transform: translate3d(calc(var(--rx-sway, 0px) * -1), calc(var(--rx-rise, -210px) * .5), 0) scale(1); }
  75%  { transform: translate3d(var(--rx-sway, 0px), calc(var(--rx-rise, -210px) * .75), 0) scale(.96); }
  100% { transform: translate3d(0, var(--rx-rise, -210px), 0) scale(.92); opacity: 0; }
}

/* ── The pill ────────────────────────────────────────────────────────────────
   Sits in a slot Home leaves for it, below the top banner and above the today
   card, so it pushes the card down rather than covering it.                   */
.rx-slot:empty { display: none; }
.rx-pill {
  display: flex; align-items: center; gap: 8px;
  margin: 0 0 10px; padding: 10px 12px;
  background: var(--surface); border: 1.5px solid var(--rule); border-radius: 14px;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
}
.rx-pill:active { background: var(--rule); }
.rx-pill-t {
  flex: 1 1 auto; min-width: 0;
  font-size: 0.9rem; font-weight: 700; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rx-pill-c { color: var(--faint); font-size: 1.1rem; line-height: 1; }

/* ── Reactions received, on a profile ─────────────────────────────────────────
   A row of emoji with lifetime counts. Wraps rather than scrolls — there are at
   most five, so it can never need a scroller. */
.prof-rx {
  display: flex; flex-wrap: wrap; gap: 8px;
  grid-column: 1 / -1;                 /* it lives inside .prof-stat-grid */
}
.prof-rx-item {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 8px 12px;
  background: var(--surface); border: 1.5px solid var(--rule); border-radius: 999px;
}
.prof-rx-e { font-size: 20px; line-height: 1; }
.prof-rx-n {
  font-family: var(--font-display); font-size: 1rem; font-weight: 700; color: var(--text);
}

/* ── The list sheet (who reacted / the inbox) ────────────────────────────── */
.rx-sheet {
  position: fixed; inset: 0; z-index: 2147482000;
  background: rgba(31, 26, 20, 0.45);
  display: flex; align-items: flex-end; justify-content: center;
  padding: 0 10px calc(10px + env(safe-area-inset-bottom));
}
.rx-sheet-card {
  position: relative; width: 100%; max-width: 460px;
  max-height: 70vh; overflow-y: auto;
  background: var(--card); border-radius: 18px; padding: 16px 14px 12px;
}
.rx-sheet-x {
  position: absolute; top: 10px; right: 10px;
  width: 30px; height: 30px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: transparent; border: none; color: var(--muted); cursor: pointer;
}
.rx-sheet-x:active { background: var(--rule); }
.rx-sheet-t {
  font-family: var(--font-display); font-size: 1.05rem; font-weight: 700;
  color: var(--text); margin-bottom: 10px; padding-right: 34px;
}
.rx-grp {
  font-size: 0.82rem; font-weight: 800; letter-spacing: 0.06em;
  color: var(--faint); text-transform: uppercase;
  margin: 12px 0 4px;
}
.rx-grp:first-child { margin-top: 0; }
.rx-who {
  display: flex; align-items: center; gap: 9px;
  padding: 7px 0; border-bottom: 1px solid var(--rule);
}
.rx-who:last-child { border-bottom: none; }
.rx-who.tap { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.rx-who.tap:active { background: var(--rule); }
.rx-who-n {
  flex: 1 1 auto; min-width: 0;
  font-size: 0.95rem; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.rx-who-e { font-size: 18px; line-height: 1; }
.rx-empty { font-size: 0.9rem; color: var(--muted); padding: 8px 0; }

/* An initial is a FILLED DISC, a photo has NO RING — the app's avatar rule
   (CLAUDE.md), matched here rather than reinvented. box-sizing so both are the
   same 28px circle. */
.rx-av {
  width: 28px; height: 28px; border-radius: 50%; flex: 0 0 28px;
  box-sizing: border-box; overflow: hidden;
  display: flex; align-items: center; justify-content: center;
  background: var(--rule); color: #fff;
  font-size: 0.82rem; font-weight: 800;
}
.rx-av img { width: 100%; height: 100%; object-fit: cover; }

/* Someone who has asked for less motion gets the pill and the modal, and none of
   the waft — the information is all in the pill, so nothing is lost. */
@media (prefers-reduced-motion: reduce) {
  .rx-f { display: none; }
  .rx-e.wobble { animation: none; }
}
