/* css/motion.css — the motion layer, loaded last: press feedback, desktop hover, sheet / modal / toast motion, tab
   crossfade, count bumps, tap reactions, reward pops (coins, stamps, confetti, number pops).
   Visual pass 2 (2026-10-15, docs/DECISIONS.md): motion answers a tap or a reward — nothing loops at idle. The only
   infinite animations in the game are the teaching pointers, the packing guides, loading and Kim's blink (none of them
   live here). Durations and curves come from the tokens in css/main.css (--mo-fast / --mo-med / --mo-slow, --mo-out,
   --mo-spring = the one overshoot); reward pops that must stay readable keep their own lifetime. Hover never adds a
   glow or a blurred shadow: a pressable lifts 1 px and its hard edge grows by 1 px, lit from the top.
   Only transform / opacity / box-shadow (+ colours) move, so it stays on the compositor at 60 fps.
   JS glue: js/ui/motion.js (<html data-reduce-motion>, bump()), sheet.js (.is-entering), kit.js/orders.js
   (.is-switch), modals.js (.is-leaving), nav.js/hud.js (badge .is-bump).
   Reduced motion: js/ui/motion.js mirrors the OS prefers-reduced-motion into data-reduce-motion="true|false" on
   <html> (no in-game selector any more); the media query applies until it is set. See the end of the file. */

/* ------------------------------------------------------------------ base transitions (all devices) */
:where(button, a, input, select, textarea, [tabindex]) {
  outline-offset: 7px;
  transition: outline-offset var(--mo-med) var(--mo-out);
}

.hud-quick__btn,
.goal,
.prof,
.hud-pill,
.hud-gear,
.hud-pill__plus,
.hud-chip,
.round,
.sheet__close,
.scr-row,
.scr-tile,
.scr-card,
.scr-req,
.scr-ward__item,
.scr-weekly__card,
.scr-cat,
.scr-stat,
.scr-check,
.scr-cal__day,
.cal__day,
.card,
.naming__pick,
.chip--pick {
  transition:
    transform var(--mo-fast) var(--mo-out),
    box-shadow var(--mo-fast) var(--mo-out),
    background-color var(--mo-med) var(--mo-out),
    opacity var(--mo-med) var(--mo-out),
    outline-offset var(--mo-med) var(--mo-out);
}

/* chunky pass (2026-10-20): the press is instant (--mo-tap), the release springs back past its rest and settles */
.btn {
  transition:
    transform var(--mo-slow) var(--mo-spring),
    box-shadow var(--mo-slow) var(--mo-spring),
    filter var(--mo-fast) var(--mo-out),
    outline-offset var(--mo-med) var(--mo-out);
}
.btn:active,
.btn.is-pressed {
  transition-duration: var(--mo-tap), var(--mo-tap), var(--mo-fast), var(--mo-med);
  transition-timing-function: var(--mo-out);
}

.seg__btn,
.scr-seg__btn,
.hud-nav__tab {
  transition:
    color var(--mo-fast) var(--mo-out),
    background-color var(--mo-fast) var(--mo-out),
    box-shadow var(--mo-fast) var(--mo-out),
    transform var(--mo-fast) var(--mo-out),
    outline-offset var(--mo-med) var(--mo-out);
}

.hud-nav__icon,
.round .icon {
  transition: transform var(--mo-med) var(--mo-out);
}

.hud-nav__label { transition: color var(--mo-slow) var(--mo-out); }

/* ------------------------------------------------------------------ press feedback (all devices)
   .btn presses in css/main.css (2 px down, the edge 2 px shorter); every other pressable moves the same 2 px */
.hud-quick__btn:active,
.round:active,
.goal:active,
.prof:active,
.hud-gear:active,
.hud-chip:active,
.sheet__close:active,
.scr-row--tap:active,
.scr-tile:active,
.scr-ward__item:active,
.scr-weekly__pick:active .scr-weekly__card,
button.card:active,
.scr-req:active,
.naming__pick:active,
.chip--pick:active {
  transform: translateY(2px);
}

.hud-pill__main:active .pill__num { display: inline-block; transform: translateY(1px); }
.hud-nav__tab:active .hud-nav__icon { transform: translateY(1px); }
.seg__btn:active,
.scr-seg__btn:active { transform: translateY(1px); }

/* ------------------------------------------------------------------ desktop hover (mouse only) */
@media (hover: hover) and (pointer: fine) {
  :where(button:not(:disabled), [role="button"], [role="tab"], a[href], label.scr-row--toggle, select, summary, .scr-switch__input) {
    cursor: var(--cur-hand);
  }

  /* HUD blocks (all carry --sh-edge-sm): lift 1 px, the edge grows by 1 px */
  .hud-quick__btn:hover,
  .goal:hover,
  .prof:hover,
  .hud-pill:hover,
  .hud-league:hover,
  .hud-sq:hover,
  .hud-gear:hover,
  .round:hover {
    transform: translateY(-1px);
    box-shadow: 0 calc(var(--edge-sm) + 1px) 0 var(--edge-c);
  }
  .goal.is-done:hover { box-shadow: var(--ring-go), 0 calc(var(--edge-sm) + 1px) 0 var(--edge-c); }
  .round.is-ready:hover { box-shadow: 0 calc(var(--edge-sm) + 1px) 0 var(--green-dark); }
  .hud-chip:hover,
  .hud-pill__plus:hover { transform: translateY(-1px); }
  /* pressed while hovered: the press wins (css/hud.css presses: 2 px down, a 1 px edge) */
  .hud-quick__btn:active,
  .goal:active,
  .prof:active,
  .hud-pill--gems:active,
  .hud-league:active,
  .hud-sq:active,
  .hud-gear:active,
  .round:active { transform: translateY(2px); box-shadow: 0 1px 0 var(--edge-c); }
  .goal.is-done:active { box-shadow: var(--ring-go), 0 1px 0 var(--edge-c); }
  .round.is-ready:active { box-shadow: 0 1px 0 var(--green-dark); }

  /* bottom nav: the label brightens */
  .hud-nav__tab:hover,
  .hud-nav__tab:hover .hud-nav__label { color: var(--text); }

  /* sheets & screens: only what a click does something on lifts (owner rule: only clickable things look clickable) —
     plain cards, stats, checks, calendar days and catalog entries stay still; a lifted card shows the hard edge */
  .scr-row--tap:hover,
  label.scr-row--toggle:hover,
  .scr-tile:hover,
  .scr-req:hover,
  .scr-ward__item:hover,
  .scr-weekly__card:hover,
  button.card:not(:disabled):hover {
    transform: translateY(-3px);
    box-shadow: var(--sh-edge-sm);
  }
  .scr-card :is(.scr-row--tap, .scr-row--toggle):hover { transform: none; box-shadow: none; }
  /* rows of a grouped list never leave their slot: the hover is the selected fill instead */
  .scr-list--group > :is(.scr-row--tap, .scr-row--toggle):hover { transform: none; box-shadow: none; background-color: var(--raised-2); }
  .scr-row--tap:active,
  .scr-tile:active,
  .scr-ward__item:active,
  button.card:active,
  .scr-req:active { transform: translateY(1px); box-shadow: none; }

  /* coloured buttons: lift 1 px with a 1 px taller edge; pressed 2 px down with a 2 px shorter edge */
  .btn:hover:not(:disabled):not([aria-disabled="true"]) {
    transform: translateY(-1px);
    filter: brightness(1.06);
    box-shadow: var(--btn-ring, 0 0 0 0 transparent), var(--btn-hi, var(--btn-shine)), 0 calc(var(--e, var(--btn-lip)) + 1px) 0 var(--btn-edge);
  }
  .btn:active:not(:disabled):not([aria-disabled="true"]),
  .btn.is-pressed:not(:disabled):not([aria-disabled="true"]) {
    transform: translateY(max(1px, calc(var(--e, var(--btn-lip)) - var(--btn-lip-pressed))));
    filter: none;
    box-shadow: var(--btn-ring, 0 0 0 0 transparent), var(--btn-hi, var(--btn-shine)), 0 min(var(--e, var(--btn-lip)), var(--btn-lip-pressed)) 0 var(--btn-edge);
  }

  .seg__btn:not(.is-active):hover,
  .scr-seg__btn:not(.is-active):hover { color: var(--text); }
  .naming__pick:hover,
  .chip--pick:hover { transform: translateY(-1px); }
}

/* ------------------------------------------------------------------ sheets: slide in, backdrop fades */
.sheet { transition: transform var(--mo-med) var(--mo-out); }
.sheet.is-out { transition: transform 180ms ease-in; }
.sheet.is-dragging { transition: none; }
.sheet-scrim { animation: mo-fade-in var(--mo-med) var(--mo-out) backwards; transition: opacity 180ms ease-in; }
#layer-sheets > .sheet-scrim.is-out { opacity: 0; pointer-events: none; } /* id beats main.css layer rule */

/* ------------------------------------------------------------------ modals: pop in, shrink out */
.modal-backdrop { animation: mo-fade-in var(--mo-med) var(--mo-out) both; }
.modal { animation: mo-modal-pop var(--mo-slow) var(--mo-spring) both; }
#layer-modals > .modal-backdrop.is-leaving { animation: mo-fade-out var(--mo-fast) ease-in both; pointer-events: none; }
.modal-backdrop.is-leaving .modal { animation: mo-modal-out var(--mo-fast) ease-in both; }

@keyframes mo-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes mo-fade-out { from { opacity: 1; } to { opacity: 0; } }
@keyframes mo-modal-pop {
  from { opacity: 0; transform: translateY(8px) scale(0.94); }
  to { opacity: 1; transform: none; }
}
@keyframes mo-modal-out {
  from { opacity: 1; transform: none; }
  to { opacity: 0; transform: translateY(4px) scale(0.96); }
}

/* ------------------------------------------------------------------ toasts: an 8 px slide + fade */
.toast { animation: mo-toast-in var(--mo-med) var(--mo-out) backwards; } /* no forwards fill: .is-leaving must win */
.toast.is-leaving {
  opacity: 0;
  transform: translateY(-8px);
  transition: opacity var(--mo-med) ease-in, transform var(--mo-med) ease-in;
}
@keyframes mo-toast-in {
  from { opacity: 0; transform: translateY(-8px); }
  to { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------ tab switch: the new body fades in
   (.is-switch sits on a tab body for 0.9 s after a tab switch; no per-card stagger — the sheet slide is the entrance) */
.scr-tabs__body.is-switch,
.orders__body.is-switch { animation: mo-tab-in var(--mo-med) var(--mo-out) both; }

@keyframes mo-tab-in {
  from { opacity: 0; transform: translateX(8px); }
  to { opacity: 1; transform: none; }
}

/* ------------------------------------------------------------------ count badges: pop in once, bump on change */
.hud-quick__badge,
.hud-nav__badge,
.seg__count,
.scr-seg__count { animation: mo-badge-pop var(--mo-slow) var(--mo-spring) both; }
.hud-quick__badge.is-bump,
.hud-nav__badge.is-bump { animation-name: mo-badge-bump; }
.hud-nav__tab.has-dot::after { animation: mo-badge-pop var(--mo-slow) var(--mo-spring) both; }

@keyframes mo-badge-pop {
  from { transform: scale(0); }
  to { transform: scale(1); }
}
@keyframes mo-badge-bump {
  from { transform: scale(1.3); }
  to { transform: scale(1); }
}

/* ------------------------------------------------------------------ keyboard focus ring (snaps in) */
:focus-visible {
  outline: 3px solid var(--yellow);
  outline-offset: 3px;
}
.hud-nav__tab:focus-visible,
.seg__btn:focus-visible,
.scr-seg__btn:focus-visible { outline-offset: 1px; }

/* ------------------------------------------------------------------ tap reactions (BACKLOG item 8)
   Phones have no hover: js/ui/motion.js initTapFx() adds these one-shot classes on pointerdown. */
.hud-gear > .icon {
  rotate: var(--gear-rot, 0deg); /* +120° per tap (motion.js), springs past and settles */
  transition: transform var(--mo-med) var(--mo-out), rotate var(--mo-slow) var(--mo-spring);
}

.hud-nav__icon.is-pop { animation: mo-nav-pop var(--mo-slow) var(--mo-spring); }
.hud-pill__plus.is-pop,
.scr-seg__btn.is-pop { animation: mo-pop var(--mo-slow) var(--mo-spring); }
:is(.hud-quick__btn, .scr-tile, .hud-chip, .round).is-squish { animation: mo-squish var(--mo-slow) var(--mo-out); }
.scr-switch.is-boing { animation: mo-boing var(--mo-slow) var(--mo-out); }
.hud-pill.is-bump { animation: mo-pill-bump var(--mo-slow) var(--mo-spring); }

@keyframes mo-nav-pop {
  from { transform: translateY(-4px) scale(1.12); }
  to { transform: none; }
}

@keyframes mo-pop {
  from { transform: scale(1.2); }
  to { transform: scale(1); }
}

@keyframes mo-squish {
  0% { transform: scale(1, 1); }
  35% { transform: scale(1.04, 0.94); }
  70% { transform: scale(0.98, 1.02); }
  100% { transform: scale(1, 1); }
}

@keyframes mo-boing {
  0% { transform: scale(1); }
  40% { transform: scale(0.94, 1.04); }
  100% { transform: scale(1); }
}

@keyframes mo-pill-bump {
  from { transform: scale(1.08); }
  to { transform: scale(1); }
}

/* coin particles (motion.js burst / flyCoins): DOM spans moved by the Web Animations API. Flat discs with a darker
   rim, in the role colours: cash = green (the HUD's bill icon), gems = purple. */
.mo-fx {
  position: fixed;
  inset: 0;
  z-index: 80;
  pointer-events: none;
  overflow: hidden;
}

.mo-coin {
  position: absolute;
  left: -8px;
  top: -8px;
  width: 16px;
  height: 16px;
  border: 2px solid var(--green-dark);
  border-radius: 50%;
  background: var(--green);
  will-change: transform, opacity;
}

.mo-coin--gem {
  border-color: var(--gem-dark);
  border-radius: var(--r-xs);
  rotate: 45deg;
  background: var(--gem);
}

/* ------------------------------------------------------------------ chunky reward pops (2026-10-20, „jak Roblox”)
   One shot each, on what was just earned: a modal bounces in (past full size, settles), its big claim button pops
   in a beat later, a reward toast boings in with its icon, the crate's „Odbierz” bounces in, the reveal's item
   (js/ui/unlock-reveal.js) boings. Reduced motion: the global rules below cut them to an instant. */
.modal { animation: mo-modal-bounce var(--mo-pop) var(--mo-spring) both; }
.modal .btn--lg:not(:disabled),
.modal .btn--xl:not(:disabled) { animation: mo-claim-in var(--mo-pop) var(--mo-spring) var(--mo-fast) backwards; }
.toast-stack > :is(.toast--good, .toast--gem, .toast--achievement) { animation: mo-toast-boing var(--mo-pop) var(--mo-spring) backwards; }
.toast-stack > :is(.toast--good, .toast--gem, .toast--achievement) .toast__icon { animation: mo-icon-boing var(--mo-pop) var(--mo-spring) var(--mo-fast) backwards; }
.scr-crate__done:not([hidden]) { animation: mo-claim-in var(--mo-pop) var(--mo-spring) backwards; }
/* juice pass 2026-10-21 (the crate rework's feel on the other rewards, transform / opacity only): a gem toast slams in
   with a white flash over it and its gem spins in; the unlock reveal flashes once as it opens and its item slams
   harder (from big, past small, settles) */
.toast-stack > .toast--gem { position: relative; overflow: hidden; animation: mo-slam-in var(--mo-pop) var(--mo-spring) backwards; }
.toast-stack > .toast--gem::after { content: ''; position: absolute; inset: 0; background: #ffffff; opacity: 0; pointer-events: none; animation: mo-flash 420ms ease-out both; }
.toast-stack > .toast--gem .toast__icon { animation: mo-gem-slam 640ms var(--mo-spring) 60ms backwards; }
.modal.modal--unlock::after { content: ''; position: absolute; inset: 0; z-index: 5; border-radius: inherit; background: #ffffff; opacity: 0; pointer-events: none; animation: mo-flash 480ms ease-out 80ms both; }
.modal--unlock .unl__model { animation: mo-unl-slam 620ms var(--mo-spring) backwards; }

@keyframes mo-flash { 0% { opacity: 0.75; } 100% { opacity: 0; } }
@keyframes mo-slam-in {
  0% { opacity: 0; transform: translateY(-24px) scale(1.25); }
  50% { opacity: 1; transform: translateY(3px) scale(0.95); }
  100% { opacity: 1; transform: none; }
}
@keyframes mo-gem-slam {
  0% { opacity: 0; transform: scale(2.4) rotate(-30deg); }
  50% { opacity: 1; transform: scale(0.85) rotate(8deg); }
  75% { transform: scale(1.12) rotate(-2deg); }
  100% { opacity: 1; transform: none; }
}
@keyframes mo-unl-slam {
  0% { opacity: 0; transform: scale(2) rotate(-12deg); }
  45% { opacity: 1; transform: scale(0.88) rotate(4deg); }
  70% { transform: scale(1.06) rotate(-1deg); }
  100% { opacity: 1; transform: none; }
}

@keyframes mo-modal-bounce {
  0% { opacity: 0; transform: translateY(18px) scale(0.8); }
  55% { opacity: 1; transform: translateY(-2px) scale(1.03); }
  100% { opacity: 1; transform: none; }
}
@keyframes mo-claim-in {
  0% { transform: scale(0.6); }
  60% { transform: scale(1.06); }
  100% { transform: none; }
}
@keyframes mo-toast-boing {
  0% { opacity: 0; transform: translateY(-12px) scale(0.7); }
  55% { opacity: 1; transform: translateY(2px) scale(1.05); }
  100% { opacity: 1; transform: none; }
}
@keyframes mo-icon-boing {
  0% { transform: scale(0.4) rotate(-20deg); }
  60% { transform: scale(1.25) rotate(8deg); }
  100% { transform: none; }
}
@keyframes mo-reward-boing {
  0% { transform: scale(1); }
  35% { transform: scale(1.12) rotate(-2deg); }
  70% { transform: scale(0.97) rotate(1deg); }
  100% { transform: none; }
}

/* ------------------------------------------------------------------ reduced motion: setting "on", or OS + "auto" */
:root[data-reduce-motion="true"] *,
:root[data-reduce-motion="true"] *::before,
:root[data-reduce-motion="true"] *::after {
  animation-duration: 0.01ms !important;
  animation-delay: 0s !important;
  animation-iteration-count: 1 !important;
  transition-duration: 0.01ms !important;
  transition-delay: 0s !important;
}

@media (prefers-reduced-motion: reduce) {
  :root:not([data-reduce-motion="false"]) *,
  :root:not([data-reduce-motion="false"]) *::before,
  :root:not([data-reduce-motion="false"]) *::after {
    animation-delay: 0s !important;
    transition-delay: 0s !important;
  }
}

/* no lifting on hover either — colour only */
:root[data-reduce-motion="true"] :is(.hud-quick__btn, .goal, .prof, .hud-pill, .hud-gear, .round, .hud-chip, .scr-row--tap, .scr-row--toggle, .scr-tile, .scr-req, .scr-ward__item, .scr-weekly__card, button.card, .btn, .sheet__close):hover,
:root[data-reduce-motion="true"] :is(.hud-quick__btn, .hud-gear, .round) .icon {
  transform: none !important;
}

@media (prefers-reduced-motion: reduce) {
  :root:not([data-reduce-motion="false"]) :is(.hud-quick__btn, .goal, .prof, .hud-pill, .hud-gear, .round, .hud-chip, .scr-row--tap, .scr-row--toggle, .scr-tile, .scr-req, .scr-ward__item, .scr-weekly__card, button.card, .btn, .sheet__close):hover,
  :root:not([data-reduce-motion="false"]) :is(.hud-quick__btn, .hud-gear, .round) .icon {
    transform: none !important;
  }
}

/* bottom nav micro-animations stay on even with reduced motion (owner 2026-10-11): pill slide, icon pop, label fade */
:root[data-reduce-motion="true"] .hud-nav .hud-nav__pill {
  transition-duration: var(--mo-slow) !important;
}
:root[data-reduce-motion="true"] .hud-nav .hud-nav__label,
:root[data-reduce-motion="true"] .hud-nav .hud-nav__tab::before {
  transition-duration: var(--mo-slow) !important;
}
:root[data-reduce-motion="true"] .hud-nav .hud-nav__icon.is-pop {
  animation-duration: var(--mo-slow) !important;
}

/* Cash pill deltas (owner 2026-10-11): „−¥30” drops out on a purchase, „+¥30” pops in on a payout. */
.hud-pill--cash { position: relative; }
.cash-delta {
  position: absolute; left: 50%; top: 100%; z-index: 3;
  transform: translate(-50%, 0); white-space: nowrap; pointer-events: none;
  font-weight: var(--fw-num); font-size: var(--fs-14); font-variant-numeric: tabular-nums;
  text-shadow: var(--num-shadow);
}
.cash-delta.is-neg { color: var(--red); animation: cash-out 1s var(--mo-out) forwards; }
.cash-delta.is-pos { color: var(--green); animation: cash-in 1s var(--mo-out) forwards; }
@keyframes cash-out {
  0% { opacity: 0; transform: translate(-50%, -14px); }
  15% { opacity: 1; transform: translate(-50%, -4px); }
  100% { opacity: 0; transform: translate(-50%, 26px); }
}
@keyframes cash-in {
  0% { opacity: 0; transform: translate(-50%, 30px); }
  25% { opacity: 1; transform: translate(-50%, 14px); }
  100% { opacity: 0; transform: translate(-50%, -10px); }
}

/* ================================================================== rewards (BACKLOG 10)
   Idle/tycoon feedback for what the player just did: progress fills slide, a level-up rings the profile card,
   DOM stamps / confetti / number pops above sheets and modals. One shot each — nothing here loops. */

/* progress fills slide to the new value (values change at the 5 Hz HUD tick) */
.prof__xpfill,
.goal__fill,
.scr-bar__fill,
.scr-stn__msfill,
.short__fill {
  transition: width var(--mo-slow) var(--mo-out);
}

/* level-up (motion.js initFeel → .is-lvlup for 1.6 s): a yellow ring flashes on the card, „Lv N” pops, a ring leaves
   the logo, the XP bar snaps to the new level instead of sliding backwards */
.prof.is-lvlup { animation: mo-prof-ring 1.2s ease-out; }
.prof.is-lvlup .prof__lvl { animation: mo-lvl-pop var(--mo-slow) var(--mo-spring); }
.prof.is-lvlup .prof__xpfill { transition: none; }
.prof__logo { position: relative; }
.prof.is-lvlup .prof__logo::after {
  content: '';
  position: absolute;
  inset: -2px;
  border: 3px solid var(--yellow);
  border-radius: 50%;
  pointer-events: none;
  animation: mo-ring 0.9s ease-out forwards;
}

@keyframes mo-prof-ring {
  0%, 60% { box-shadow: 0 0 0 2px var(--yellow), var(--sh-edge-sm); }
  100% { box-shadow: 0 0 0 2px transparent, var(--sh-edge-sm); }
}
@keyframes mo-lvl-pop {
  from { transform: scale(1.4); color: var(--yellow); }
  to { transform: scale(1); }
}
@keyframes mo-ring {
  0% { transform: scale(0.85); opacity: 1; }
  100% { transform: scale(1.9); opacity: 0; }
}

/* DOM confetti / stamp / number pop (motion.js confettiAt / stampAt / popAt) on the #mo-fx layer */
.mo-conf {
  position: absolute;
  left: -4px;
  top: -3px;
  width: 8px;
  height: 6px;
  border-radius: 0;
  will-change: transform, opacity;
}
.mo-conf--dot { width: 7px; height: 7px; border-radius: 50%; }

/* the rubber stamp: ink on paper, like the title stamp and the QC stamps */
.mo-stamp {
  position: absolute;
  left: 0;
  top: 0;
  padding: var(--sp-1) var(--sp-3);
  border: 3px solid currentColor;
  border-radius: var(--r-sm);
  background: var(--paper);
  font: var(--fw-num) var(--fs-20)/var(--lh-tight) var(--font);
  white-space: nowrap;
  will-change: transform, opacity;
}
.mo-stamp--green { color: var(--green-dark); }
.mo-stamp--gold { color: var(--yellow-dark); }
.mo-stamp--red { color: var(--red); }

/* a floater: white 900 with an ink outline (= render/fx.js floaters over the hall) */
.mo-pop {
  position: absolute;
  left: 0;
  top: 0;
  font: var(--fw-num) var(--fs-20)/1 var(--font);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: #ffffff;
  text-shadow: 0 2px 0 var(--ink), 0 -1px 0 var(--ink), 1px 0 0 var(--ink), -1px 0 0 var(--ink);
  will-change: transform, opacity;
}
.mo-pop--gems { color: var(--gem-light); }

/* ------------------------------------------------------------------ small gem drops (BACKLOG 26) + Szef Kot (29)
   „+N [gem]” pops, the „+N [gem]” suffix of a gem toast, and the gem that peeks out of the „Gem w kartonie” carton in
   the Stół QC unpack stage (js/ui/motion.js sparkleBox) — a static mark, it flies off once when the carton is opened. */
.mo-pop--gems { display: inline-flex; align-items: center; gap: var(--sp-1); }
.mo-pop--gems svg { flex: none; filter: drop-shadow(0 2px 0 var(--ink)); }
.toast__gems {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: 2px;
  color: var(--gem-light);
  font-weight: var(--fw-num);
}
.mo-heart {
  position: absolute;
  left: 0;
  top: 0;
  pointer-events: none;
  will-change: transform, opacity;
  filter: drop-shadow(0 2px 0 var(--ink));
}
.mo-gem-spark { position: absolute; inset: 0; pointer-events: none; }
.mo-gem-spark > svg {
  position: absolute;
  right: -10px;
  top: -14px;
  transform: rotate(-8deg);
  filter: drop-shadow(0 2px 0 var(--ink));
}
.mo-gem-spark.is-found > svg { animation: mo-gem-found var(--mo-slow) ease-out forwards; }
@keyframes mo-gem-found {
  to { opacity: 0; transform: translateY(-40px) scale(1.4); }
}

/* crates + Paczka dnia (screens/crate.js): the rotating light rays behind the box are gone (an idle loop + a
   gradient) — crate.js no longer creates the element */
