/* roby-island (10 Nov): the polish layer (src/polish.js). Motion only: no layout, no colours of record.
   Everything here is off under prefers-reduced-motion. */

/* ---- things leaving the screen (ghostOut): the copy fades while the real element is already hidden */
.ghost { pointer-events: none !important; }
.ghost.ld-out { position: fixed; inset: 0; z-index: 99; background: #2f9be9; animation: polFade .26s ease-out forwards; }
.modal.ghost.card-out { animation: polFade .16s ease-in forwards; }
.modal.ghost.card-out .card { animation: polCardOut .16s cubic-bezier(.4, 0, 1, 1) forwards; }
@keyframes polFade { to { opacity: 0; } }
@keyframes polCardOut { to { transform: scale(.92) translateY(10px); opacity: 0; } }

/* ---- going back slides the other way (app.js go('back')) */
.screen.enter.back { animation-name: polScrBack; }
@keyframes polScrBack { from { opacity: 0; transform: translateY(-12px); } }

/* ---- toasts: tap to dismiss; the newest pushes the others down smoothly */
.toast.bye { animation: polToastBye .16s ease-in forwards; }
@keyframes polToastBye { to { opacity: 0; transform: translateY(-120%); } }

/* ---- every tappable answers the finger. .btn already sinks 5 px; the rest (icon buttons, tabs, slots, pills)
        dim a touch. filter, not transform: many of them are positioned with transforms. */
button:not(.btn):not(:disabled):active, [role="button"]:active, [role="tab"]:active { filter: brightness(.9) saturate(1.1); }
.btn:disabled { transition: filter .15s, opacity .15s; }

/* ---- keyboard focus is visible everywhere (it was the browser default, lost on dark panels) */
:focus-visible { outline: 3px solid #ffd23f; outline-offset: 2px; }
canvas:focus-visible, #stage:focus-visible { outline: none; }

/* ---- state-driven styling (src/fsm.js mirrors each machine on <body>) */
body[data-fsm-boot="failed"] #loading .ld-ring::after { animation-play-state: paused; }
body[data-fsm-raid="attack"] .raid-target { pointer-events: none; }   /* no second pick while the attack plays */

/* ---- a waiting button shows it (add .busy while a server call is out; pairs with disabled) */
.btn.busy { position: relative; color: transparent !important; -webkit-text-stroke: 0 !important; pointer-events: none; }
.btn.busy::after { content: ""; position: absolute; left: 50%; top: 50%; width: 18px; height: 18px; margin: -11px 0 0 -11px; border: 3px solid #fff; border-right-color: transparent; border-radius: 50%; animation: polSpin .7s linear infinite; }
@keyframes polSpin { to { transform: rotate(360deg); } }

/* ---- art that arrives late fades in instead of popping (opt in: class="pol-img") */
img.pol-img { opacity: 0; transition: opacity .2s ease-out; }
img.pol-img.in { opacity: 1; }

@media (prefers-reduced-motion: reduce) {
  .ghost { display: none !important; }
  .screen.enter.back, .toast.bye, .btn.busy::after { animation: none; }
  img.pol-img { transition: none; opacity: 1; }
}

/* ---- 10 Nov (2): the proposed items ---- */
/* busy(btn, work): a spinner over any button while its server call is out */
.pol-rel { position: relative; }
.pol-busy { pointer-events: none; }
.pol-busy > :not(.pol-spin) { opacity: .35; }
.pol-spin { position: absolute; left: 50%; top: 50%; width: 22px; height: 22px; margin: -14px 0 0 -14px; border: 4px solid #fff; border-right-color: transparent; border-radius: 50%; box-shadow: 0 0 0 1px #14161f33; animation: polSpin .7s linear infinite; z-index: 2; }

/* a READY chest: a hard-edged glint crosses it every 3.6 s (a solid band, no soft scrim) */
.fh-slot.ready .fh-sart::after { content: ""; position: absolute; inset: 0; pointer-events: none; background: linear-gradient(105deg, transparent 0 44%, rgba(255, 255, 255, .55) 44% 52%, transparent 52% 56%, rgba(255, 255, 255, .3) 56% 59%, transparent 59%); background-size: 320% 100%; background-position: 130% 0; -webkit-mask: var(--pol-mask, none) center / contain no-repeat; mask: var(--pol-mask, none) center / contain no-repeat; animation: polGlint 3.6s ease-in-out infinite; mix-blend-mode: screen; }
@keyframes polGlint { 0%, 72% { background-position: 130% 0; } 100% { background-position: -30% 0; } }

/* the chest timer rolls when the minute changes */
.fh-time.roll { animation: polRoll .32s cubic-bezier(.2, 1.4, .4, 1); }
@keyframes polRoll { 0% { transform: translateY(-45%); opacity: 0; } 100% { transform: none; opacity: 1; } }

/* the home bird: tapped → a short hop; idle → a blink and a look around (home.js drives both) */
.fh-bird { cursor: pointer; }
.fh-bird.hop > * { animation: polHop .42s cubic-bezier(.3, 1.6, .5, 1); }
@keyframes polHop { 0% { translate: 0 0; scale: 1 1; } 25% { translate: 0 6%; scale: 1.08 .9; } 60% { translate: 0 -22%; scale: .96 1.06; } 100% { translate: 0 0; scale: 1 1; } }
.fh-bird.look > * { animation: polLook 1.1s ease-in-out; }
@keyframes polLook { 0%, 100% { rotate: 0deg; } 30% { rotate: -7deg; } 65% { rotate: 5deg; } }
.pol-blink { position: absolute; pointer-events: none; background: #14161f; border-radius: 40%; transform-origin: center; animation: polBlink .16s ease-in-out; }
@keyframes polBlink { 0%, 100% { transform: scaleY(0); } 50% { transform: scaleY(1); } }

/* a landmark stage goes up: dust, and the island takes the weight */
.pol-puff { position: fixed; z-index: 60; border-radius: 50%; pointer-events: none; box-shadow: 0 0 0 2px #14161f22; }
.pol-nudge { animation: polNudge .38s cubic-bezier(.3, .7, .3, 1); }
@keyframes polNudge { 0% { translate: 0 0; scale: 1; } 30% { translate: 0 5px; scale: 1.012; } 62% { translate: 0 -2px; scale: 1.004; } 100% { translate: 0 0; scale: 1; } }

@media (prefers-reduced-motion: reduce) {
  .fh-slot.ready .fh-sart::after, .pol-spin { animation: none; }
  .fh-slot.ready .fh-sart::after { display: none; }
  .fh-time.roll, .fh-bird.hop > *, .fh-bird.look > *, .pol-nudge { animation: none; }
  .pol-blink, .pol-puff { display: none; }
}
