/* Snappy Swim (games/swim/swim.js). Everything is scoped to #play.sw-on, which pdraw() sets while a Snappy Swim game is on.
   Motion only animates transform and opacity. The reveal keyframes (sw-path, sw-shark) follow SWIM.SEQ in swim.js:
   flip 0 · swim 350 · shark 1150 · snap 1800 · hit 2000 · pan 2700 · end 3300 ms.
   The global prefers-reduced-motion rule in playlab.html turns every animation off; each element's resting style is its final state. */

/* ---- water ---- */
#play.sw-on .stage{position:relative;overflow:hidden;justify-content:center;background:linear-gradient(180deg,#CFF5FF 0%,#A3E4F7 48%,#76CBEB 100%);box-shadow:var(--shadow),inset 0 -40px 60px rgba(40,120,180,.18)}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) #play.sw-on .stage{background:linear-gradient(180deg,#2C6E9C 0%,#215A86 50%,#18476F 100%)}}
:root[data-theme="dark"] #play.sw-on .stage{background:linear-gradient(180deg,#2C6E9C 0%,#215A86 50%,#18476F 100%)}
/* bubbles drifting up and an occasional fish, behind the lanes; pseudo-elements, so they keep going when the lanes redraw */
#play.sw-on .stage::before{content:"";position:absolute;left:0;right:0;top:0;height:200%;pointer-events:none;opacity:.55;
 background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='260' height='300'%3E%3Cg fill='none' stroke='%23fff' stroke-width='2'%3E%3Ccircle cx='30' cy='250' r='6'/%3E%3Ccircle cx='44' cy='226' r='3'/%3E%3Ccircle cx='150' cy='120' r='5'/%3E%3Ccircle cx='160' cy='98' r='2.5'/%3E%3Ccircle cx='226' cy='200' r='4'/%3E%3Ccircle cx='96' cy='40' r='3.5'/%3E%3C/g%3E%3C/svg%3E") 0 0/260px 300px repeat;
 animation:sw-drift 22s linear infinite}
#play.sw-on .stage::after{content:"";position:absolute;left:0;top:14%;width:120px;height:60px;pointer-events:none;opacity:.32;
 background:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 120 60'%3E%3Cg fill='%232A7FB8'%3E%3Cpath d='M30 20c8-8 22-8 30 0-8 8-22 8-30 0zM30 20l-9-6v12z'/%3E%3Cpath d='M78 44c6-6 16-6 22 0-6 6-16 6-22 0zM78 44l-7-5v10z'/%3E%3C/g%3E%3C/svg%3E") center/contain no-repeat;
 animation:sw-fish 34s linear infinite}
#play.sw-on .stage>svg{position:relative;z-index:1}
@keyframes sw-drift{to{transform:translateY(-50%)}}
@keyframes sw-fish{0%{transform:translateX(-140px)}62%,100%{transform:translateX(calc(100vw + 40px))}}

/* ---- lanes and swimmers (SVG) ---- */
.sw-svg .sw-bob,.sw-svg .sw-act,.sw-svg .sw-sharkin,.sw-svg .sw-sharkbob,.sw-svg .sw-tail,.sw-svg .sw-fin,.sw-svg .sw-q,.sw-svg .sw-front,.sw-svg .sw-back,.sw-svg .sw-hpop,.sw-svg .sw-pop,.sw-svg .sw-chomp,.sw-svg .sw-ring,.sw-svg .sw-crown,.sw-svg .sw-spark2,.sw-svg .sw-rise{transform-box:fill-box;transform-origin:center}
.sw-svg .sw-bob{animation:sw-bob 2.6s ease-in-out infinite}
.sw-svg .sw-turn{animation:sw-eager 1.6s ease-in-out infinite}
.sw-svg .sw-rest{opacity:.85;filter:saturate(.55);animation:sw-sway 3.4s ease-in-out infinite}
.sw-svg .sw-lid{opacity:0;animation:sw-blink 4.6s linear infinite}
.sw-svg .sw-lash{opacity:0;animation:sw-lash 4.6s linear infinite}
.sw-svg .sw-trailg{opacity:0}
.sw-svg .sw-glow{animation:sw-glow 1.4s ease-in-out infinite}
.sw-svg .sw-q{animation:sw-qbounce 1.1s ease-in-out infinite}
.sw-svg .sw-winlane{animation:sw-glow 1.2s ease-in-out infinite}
.sw-svg .sw-pop,.sw-svg .sw-chomp{opacity:0;animation:sw-pop .9s ease-out}
.sw-svg .sw-chomp{animation-name:sw-chomp}
.sw-svg .sw-hpop{opacity:0;animation:sw-hpop .6s ease-in-out backwards}
.sw-svg .sw-spark{opacity:0;animation:sw-spark .45s ease-out}
.sw-svg .sw-out{opacity:0;animation:sw-flipout .15s ease-in backwards}
.sw-svg .sw-in{animation:sw-flipin .15s ease-out backwards}
.sw-svg .sw-bitlane[style],.sw-svg .sw-fadein{animation:sw-fadein .35s ease-out backwards}
.sw-svg .sw-ring[style]{animation:sw-popin .45s cubic-bezier(.3,1.6,.5,1) backwards}
.sw-svg .sw-dance{animation:sw-dance .7s ease-in-out .9s 4}
.sw-svg .sw-crown{animation:sw-popin .5s cubic-bezier(.3,1.6,.5,1) .9s backwards}
.sw-svg .sw-spark2{opacity:0;animation:sw-burst 1s ease-out}
.sw-svg .sw-rise{opacity:0;animation:sw-rise 2.4s ease-out 1s infinite}
.sw-svg .sw-sharkbob{animation:sw-bob 3s ease-in-out infinite}
.sw-svg .sw-tail{transform-origin:100% 50%;animation:sw-swish 1.3s ease-in-out infinite}
.sw-svg .sw-fin{transform-origin:20% 0;animation:sw-swish 1.6s ease-in-out infinite reverse}
.sw-svg .sw-mo{opacity:0}

@keyframes sw-bob{0%,100%{transform:translateY(0) rotate(0)}50%{transform:translateY(-3px) rotate(-2deg)}}
@keyframes sw-eager{0%,100%{transform:translateY(0) rotate(0)}30%{transform:translateY(-4px) rotate(3deg)}60%{transform:translateY(0) rotate(-2deg)}}
@keyframes sw-sway{0%,100%{transform:rotate(-3deg)}50%{transform:rotate(3deg) translateY(-2px)}}
@keyframes sw-blink{0%,93%{opacity:0;transform:translateY(0)}95.500%{opacity:1;transform:translateY(var(--lid))}97%{opacity:1;transform:translateY(var(--lid))}99.500%,100%{opacity:0;transform:translateY(0)}}
@keyframes sw-lash{0%,95%{opacity:0}95.500%,97%{opacity:1}97.500%,100%{opacity:0}}
@keyframes sw-glow{0%,100%{opacity:.45}50%{opacity:1}}
@keyframes sw-qbounce{0%,60%,100%{transform:translateY(0) scale(1)}20%{transform:translateY(-5px) scale(1.06)}40%{transform:translateY(0) scale(.97)}}
@keyframes sw-trail{0%{opacity:0;transform:translateX(0)}15%{opacity:1}100%{opacity:0;transform:translateX(-18px) translateY(-8px)}}
@keyframes sw-paddle{0%,100%{transform:rotate(0) translateY(0)}25%{transform:rotate(7deg) translateY(-2px)}75%{transform:rotate(-5deg) translateY(1px)}}
@keyframes sw-bonk{0%,100%{transform:translateX(0)}20%{transform:translateX(5px) rotate(4deg)}45%{transform:translateX(-5px) rotate(-4deg)}70%{transform:translateX(3px)}}
@keyframes sw-hop{0%{transform:translateY(0) scale(1)}15%{transform:translateY(2px) scale(1.08,.9)}45%{transform:translateY(-14px) rotate(-8deg) scale(.96,1.06)}75%{transform:translateY(-2px) rotate(4deg)}100%{transform:translateY(0) rotate(0)}}
@keyframes sw-pop{0%{opacity:0;transform:translateY(6px) scale(.6)}20%{opacity:1;transform:translateY(0) scale(1.1)}75%{opacity:1;transform:translateY(-4px) scale(1)}100%{opacity:0;transform:translateY(-10px)}}
@keyframes sw-chomp{0%{opacity:0;transform:scale(.4) rotate(-10deg)}25%{opacity:1;transform:scale(1.2) rotate(4deg)}70%{opacity:1;transform:scale(1) rotate(-2deg)}100%{opacity:0;transform:scale(1.05) translateY(-8px)}}
@keyframes sw-hpop{0%{opacity:1;transform:rotate(0) scale(1)}18%{opacity:1;transform:rotate(-16deg)}36%{opacity:1;transform:rotate(13deg)}54%{opacity:1;transform:rotate(-7deg) scale(1.08)}78%{opacity:.9;transform:scale(1.45)}100%{opacity:0;transform:scale(1.75)}}
@keyframes sw-spark{0%{opacity:1;transform:translate(0,0) scale(1)}100%{opacity:0;transform:translate(var(--dx),var(--dy)) scale(.4)}}
@keyframes sw-flipout{0%{opacity:1;transform:scaleX(1)}100%{opacity:1;transform:scaleX(0)}}
@keyframes sw-flipin{0%{transform:scaleX(0)}100%{transform:scaleX(1)}}
@keyframes sw-fadein{0%{opacity:0}}
@keyframes sw-popin{0%{opacity:0;transform:scale(.3)}}
@keyframes sw-show{0%,100%{opacity:1}}
@keyframes sw-hide{0%,100%{opacity:0}}
@keyframes sw-dance{0%,100%{transform:translateY(0) rotate(0)}25%{transform:translateY(-10px) rotate(-10deg)}50%{transform:translateY(0) rotate(0) scale(1.06,.94)}75%{transform:translateY(-10px) rotate(10deg)}}
@keyframes sw-burst{0%{opacity:1;transform:translate(0,0) scale(.4)}70%{opacity:1}100%{opacity:0;transform:translate(var(--dx),var(--dy)) scale(1.1)}}
@keyframes sw-rise{0%{opacity:0;transform:translateY(0)}20%{opacity:1}100%{opacity:0;transform:translateY(-46px)}}
@keyframes sw-swish{0%,100%{transform:rotate(-7deg)}50%{transform:rotate(7deg)}}
/* the reveal: one path per swimmer (start, after the number, after a bite, after everyone slides back) */
@keyframes sw-path{0%,10.600%{transform:translate(var(--x0),0);animation-timing-function:ease-in-out}34.800%,62.100%{transform:translate(var(--x1),0);animation-timing-function:ease-out}80.300%,81.800%{transform:translate(var(--x2),0);animation-timing-function:ease-in-out}100%{transform:translate(var(--x3),0)}}
/* the shark: rest, swim over to the last lane, pull back, snap, then back to rest */
@keyframes sw-shark{0%,42.600%{transform:translate(var(--ax0),var(--ay0));animation-timing-function:ease-in-out}57.400%{transform:translate(var(--ax1),var(--ay1));animation-timing-function:ease-in-out}66.700%{transform:translate(var(--ax2),var(--ay1));animation-timing-function:cubic-bezier(.5,0,.8,.4)}74.100%,77.800%{transform:translate(var(--ax3),var(--ay1));animation-timing-function:ease-in-out}100%{transform:translate(var(--ax4),var(--ay4))}}
@keyframes sw-ready{0%,100%{transform:scale(1)}50%{transform:scale(.92,1.06) rotate(-4deg)}}
@keyframes sw-snap{0%{transform:scale(1.08,.94)}50%{transform:scale(.96,1.04)}100%{transform:scale(1)}}
@keyframes sw-puzzled{0%,100%{transform:rotate(0)}25%{transform:rotate(-10deg)}50%{transform:rotate(8deg) translateY(-3px)}75%{transform:rotate(-5deg)}}
@keyframes sw-flip{0%{transform:rotate(0)}60%{transform:rotate(-360deg) translateY(-6px)}100%{transform:rotate(-360deg)}}
@keyframes sw-in{0%{opacity:0;transform:translate(calc(var(--x3) - 120px),0)}30%{opacity:1}100%{transform:translate(var(--x3),0)}}
@keyframes sw-go{0%{transform:translate(var(--x0),0)}100%{transform:translate(var(--x3),0)}}
@keyframes sw-sharkin{0%{opacity:0;transform:translate(-110px,var(--ay4))}100%{opacity:1;transform:translate(var(--ax4),var(--ay4))}}

/* ---- side panel ---- */
#play.sw-on .pcard{padding:16px}
.sw-card{display:flex;flex-direction:column;gap:12px;align-items:stretch;text-align:center}
.sw-head{display:flex;align-items:center;gap:12px;text-align:left;background:linear-gradient(135deg,#E3F7FF,#C9EEFB);border:3px solid #16111F;border-radius:20px;padding:8px 14px 8px 10px;color:#26244F}
.sw-av{display:flex;justify-content:center;line-height:0}.sw-av svg{display:block}
.sw-head .sw-av{animation:sw-hbob 2.6s ease-in-out infinite}
.sw-av.big{animation:sw-hbob 2.6s ease-in-out infinite}
.sw-av.win{animation:sw-hdance .7s ease-in-out .2s 4}
.sw-kick{font:800 .78rem Nunito,sans-serif;letter-spacing:.06em;text-transform:uppercase;color:#2B7FB3}
.sw-title{font:800 1.45rem/1.1 "Baloo 2",Nunito,sans-serif;color:var(--ink)}.sw-head .sw-title{color:#26244F}
.sw-title.big{font-size:1.8rem}
.sw-ask{font:800 1.05rem Nunito,sans-serif;color:var(--ink)}
.sw-nums{display:grid;grid-template-columns:repeat(5,1fr);gap:8px;padding-bottom:5px}
.sw-num{appearance:none;border:3px solid #16111F;border-radius:16px;background:#FFB6D2;color:#26244F;font:800 1.65rem/1 "Baloo 2",Nunito,sans-serif;aspect-ratio:1/1.05;min-height:48px;cursor:pointer;
 box-shadow:0 5px 0 #16111F;transition:transform .08s ease-out,box-shadow .08s ease-out,background-color .15s}
.sw-num:hover:not([disabled]){background:#FF9EC4;transform:translateY(-2px);box-shadow:0 7px 0 #16111F}
.sw-num:active:not([disabled]){transform:translateY(4px);box-shadow:0 1px 0 #16111F;background:#7FE0C4}
.sw-num:focus-visible{outline:4px solid #4DA3FF;outline-offset:3px}
.sw-num.used{background:transparent;color:var(--mute);border-style:dashed;border-color:var(--mute);box-shadow:none;cursor:default;opacity:.7;text-decoration:line-through;text-decoration-thickness:3px}
.sw-num[disabled]:not(.used){opacity:.55;cursor:not-allowed}
.sw-note{font:700 .85rem/1.35 Nunito,sans-serif;color:var(--mute);margin:0}
.sw-rules{display:flex;flex-direction:column;gap:8px;background:#E8F8FF;border:2px solid #A9DDF2;border-radius:18px;padding:10px 12px;text-align:left;color:#26244F}
.sw-rules>div{display:flex;align-items:center;gap:10px;font:700 .9rem/1.3 Nunito,sans-serif}
.sw-rules svg{flex:0 0 34px;width:34px;height:26px}
@media (prefers-color-scheme:dark){:root:not([data-theme="light"]) .sw-rules{background:#25507A;border-color:#3A6E9C;color:#F2FAFF}}
:root[data-theme="dark"] .sw-rules{background:#25507A;border-color:#3A6E9C;color:#F2FAFF}
.sw-picks{display:flex;flex-wrap:wrap;justify-content:center;gap:6px}
.sw-pk{display:inline-flex;align-items:center;gap:4px;background:#fff;border:3px solid #16111F;border-radius:14px;padding:2px 8px 2px 4px;color:#26244F;font:800 1.2rem "Baloo 2",Nunito,sans-serif;box-shadow:0 3px 0 #16111F}
.sw-pk.bump{background:#FFE3C2;border-color:#E8730C;box-shadow:0 3px 0 #E8730C}
.sw-pk.bit{background:#FFE0E6}
.sw-msg{font:700 1rem/1.4 Nunito,sans-serif;color:#26244F;background:#FFF6C9;border:3px solid #16111F;border-radius:18px;padding:10px 12px;margin:0;position:relative;animation:sw-msgin .35s ease-out both}
.sw-go{animation:sw-msgin .35s ease-out both}
.sw-end .sw-msg{animation:none}
.sw-card .btn{width:100%}
@keyframes sw-msgin{0%{opacity:0;transform:translateY(6px)}}
@keyframes sw-hbob{0%,100%{transform:translateY(0)}50%{transform:translateY(-3px) rotate(-2deg)}}
@keyframes sw-hdance{0%,100%{transform:translateY(0) rotate(0)}25%{transform:translateY(-8px) rotate(-8deg)}75%{transform:translateY(-8px) rotate(8deg)}}
@media(max-width:760px){#play.sw-on .pcard{padding:10px}.sw-card{gap:8px}.sw-head{padding:6px 10px 6px 8px}.sw-head .sw-av svg{width:42px;height:auto}.sw-title{font-size:1.2rem}.sw-num{font-size:1.4rem;min-height:44px;aspect-ratio:auto;padding:6px 0}.sw-rules{padding:8px 10px;gap:5px}.sw-rules>div{font-size:.82rem}}
.sw-msgs{display:grid}.sw-msgs>*{grid-area:1/1}
.sw-watch{align-self:center;margin:0;font:800 1rem Nunito,sans-serif;color:#2B7FB3;opacity:0;animation:sw-show 1s linear}

/* Toy pieces: the piece that pops off flies up and away with a spin (its resting style is gone). */
.sw-limb{opacity:0;transform-box:view-box;animation:sw-limbpop 1.1s cubic-bezier(.3,.6,.5,1) both}
@keyframes sw-limbpop{0%{opacity:1;transform:none}12%{opacity:1;transform:translate(calc(var(--lx)*.2),-14px) rotate(calc(var(--lr)*.15))}45%{opacity:1;transform:translate(calc(var(--lx)*.6),-26px) rotate(calc(var(--lr)*.55))}100%{opacity:0;transform:translate(var(--lx),30px) rotate(var(--lr))}}

/* The line is the order of the rows: rows slide to their new place (in step with the reveal, or when a snapped swimmer zooms to the top). */
@keyframes sw-row{0%,10.600%{transform:translate(0,var(--y0));animation-timing-function:ease-in-out}34.800%,100%{transform:translate(0,var(--y1))}}
@keyframes sw-rowshift{0%{transform:translate(0,var(--y0))}100%{transform:translate(0,var(--y1))}}
