/* =========================================================================
   Wonderpop friends (js/core/friend.js): Ollie, Benny, Fifi and Remy.
   Size:   set --friend-size on the friend or any ancestor (default 120px wide).
   State:  .friend[data-kind] is the skill slug; .friend[data-mood] is one of
           idle ask think happy cheer oops sleep; [data-mouth="open"] opens the mouth;
           [data-look] means the pupils follow lookAt(); [data-chomp] is mid-chomp.
   Motion: everything that loops or jumps lives in the no-preference block at the
           bottom and is skipped for .friend[data-quiet] and prefers-reduced-motion.
           Moods still change the face and hold a small static pose in that case.
   The friend needs about 15% of its height free above it for jumps and sparkles.
   ========================================================================= */

.friend {
  position: relative;
  display: inline-block;
  flex: none;
  width: var(--friend-size, 120px);
  aspect-ratio: 200 / 230;
  line-height: 0;
  pointer-events: none;
  user-select: none;
  -webkit-user-select: none;
}
.friend .friend-svg { display: block; width: 100%; height: 100%; overflow: visible; }

/* ---- pivots: everything turns where a real body would --------------------- */
.friend .rig,
.friend .body-wrap { transform-box: view-box; transform-origin: 100px 216px; }  /* the feet */
.friend .head { transform-box: view-box; transform-origin: 100px 128px; }        /* the neck */
.friend .arm-l,
.friend .arm-r,
.friend .arm-fx { transform-box: fill-box; transform-origin: 50% 8%; }           /* the shoulder */
.friend .ear-l,
.friend .ear-r { transform-box: fill-box; transform-origin: 50% 90%; }           /* the ear's root */
.friend .tail { transform-box: fill-box; transform-origin: 8% 80%; }
.friend .shadow { transform-box: fill-box; transform-origin: 50% 50%; }
.friend .eye,
.friend .cheek { transform-box: fill-box; transform-origin: 50% 50%; }
.friend .lid { transform-box: fill-box; transform-origin: 50% 0; transform: scaleY(0); opacity: 0; }  /* hidden while the eye is open: a collapsed stroke still draws a hairline */
.friend .spark { transform-box: fill-box; transform-origin: 50% 50%; }
.friend .think-mark,
.friend .oops-wobble,
.friend .z1, .friend .z2, .friend .z3 { transform-box: fill-box; transform-origin: 50% 100%; }
.friend .mouth-open { transform-box: fill-box; transform-origin: 50% 0; }

/* ---- the face: one set of rules, so a quiet friend keeps every expression ---- */
.friend .eye-happy,
.friend .mouth-open,
.friend .spark,
.friend .think-mark,
.friend .oops-mark,
.friend .zzz { opacity: 0; }
.friend .mouth-open { transform: scaleY(.4); }
.friend .mouth-closed,
.friend .mouth-open,
.friend .eye-happy,
.friend .sclera,
.friend .pupil,
.friend .spark,
.friend .think-mark,
.friend .oops-mark,
.friend .zzz { transition: opacity .16s ease; }

/* pupils: where the mood looks; lookAt() only steers them in idle and ask (think and oops keep their own gaze) */
.friend .pupil {
  transform: translate(calc(var(--gx, 0) * 1px), calc(var(--gy, 0) * 1px));
  transition: opacity .16s ease, transform .16s ease-out;
}
.friend[data-look]:is([data-mood="idle"], [data-mood="ask"]) .eye-l .pupil { transform: translate(calc(var(--lxl, 0) * 1px), calc(var(--lyl, 0) * 1px)); }
.friend[data-look]:is([data-mood="idle"], [data-mood="ask"]) .eye-r .pupil { transform: translate(calc(var(--lxr, 0) * 1px), calc(var(--lyr, 0) * 1px)); }
.friend[data-mood="think"] { --gx: 3.6; --gy: -3.8; }   /* up and to the right, where the question is */
.friend[data-mood="oops"] { --gx: 3.2; --gy: 3.8; }     /* down and to the right, back at the choices */

/* open mouth: asked for, mid-chomp, or celebrating */
.friend[data-mouth="open"] .mouth-open,
.friend:is([data-mood="happy"], [data-mood="cheer"]) .mouth-open { opacity: 1; transform: scaleY(1); }
.friend[data-mouth="open"] .mouth-closed,
.friend:is([data-mood="happy"], [data-mood="cheer"]) .mouth-closed { opacity: 0; }

/* happy eyes: the round eye closes into a smiling arc */
.friend:is([data-mood="happy"], [data-mood="cheer"]) .eye-happy { opacity: 1; }
.friend:is([data-mood="happy"], [data-mood="cheer"]) :is(.sclera, .pupil) { opacity: 0; }
.friend:is([data-mood="happy"], [data-mood="cheer"]) .spark { opacity: 1; }

/* sleep: lids down, z z z */
.friend[data-mood="sleep"] .lid { transform: scaleY(1); opacity: 1; }
.friend[data-mood="sleep"] .zzz { opacity: 1; }

.friend[data-mood="think"] .think-mark { opacity: 1; }
.friend[data-mood="oops"] .oops-mark { opacity: 1; }

/* ---- small static poses (kept when quiet) ------------------------------------ */
.friend[data-mood="ask"] .rig { transform: rotate(5deg); }
.friend[data-mood="ask"] .head { transform: rotate(2.5deg); }
.friend[data-mood="ask"] .arm-r { transform: rotate(-26deg); }
.friend[data-mood="ask"] .eye { transform: scale(1.1); }
.friend[data-mood="think"] .head { transform: rotate(-7deg); }
.friend[data-mood="think"] .arm-r { transform: rotate(-8deg); }
.friend:is([data-mood="happy"], [data-mood="cheer"]) .arm-l { transform: rotate(138deg); }
.friend:is([data-mood="happy"], [data-mood="cheer"]) .arm-r { transform: rotate(-138deg); }
.friend[data-mood="oops"] .head { transform: rotate(8deg); }
.friend[data-mood="sleep"] .head { transform: rotate(4deg) translateY(3px); }
.friend:is([data-mood="happy"], [data-mood="cheer"]) .cheek { transform: translateY(-2px) scale(1.2); }   /* a smile lifts the cheeks */

/* ---- motion: only for a friend that is not quiet, and only if the visitor allows it ---- */
@media (prefers-reduced-motion: no-preference) {
  /* poses change with a little spring, and the mouth opens quickly rather than popping */
  .friend:not([data-quiet]) :is(.rig, .head, .arm-l, .arm-r, .eye, .cheek) { transition: transform .4s cubic-bezier(.34, 1.45, .5, 1); }
  .friend:not([data-quiet]) .lid { transition: transform .32s ease; }
  .friend:not([data-quiet]) .mouth-open { transition: opacity .06s ease, transform .1s ease-out; }

  /* idle: slow breathing, a blink every ~4s, an ear that flicks now and then, a fox tail that sways */
  .friend:not([data-quiet]):not([data-mood="happy"], [data-mood="cheer"]) .body-wrap { animation: friend-breathe 4.2s ease-in-out var(--breathe-delay, 0s) infinite; }
  .friend:not([data-quiet])[data-mood="sleep"] .body-wrap { animation-duration: 6.4s; }
  .friend:not([data-quiet]):is([data-mood="idle"], [data-mood="ask"], [data-mood="think"], [data-mood="oops"]) .lid { animation: friend-blink 4.4s linear var(--blink-delay, 1.5s) infinite; }
  .friend:not([data-quiet]):is([data-mood="idle"], [data-mood="think"]) .ear-l { animation: friend-twitch-l 6.6s ease-in-out var(--twitch-delay, 2s) infinite; }
  .friend:not([data-quiet]):is([data-mood="idle"], [data-mood="think"]) .ear-r { animation: friend-twitch-r 6.6s ease-in-out calc(var(--twitch-delay, 2s) + 3.3s) infinite; }
  .friend:not([data-quiet]):not([data-mood="happy"], [data-mood="cheer"]) .tail { animation: friend-wag 2.8s ease-in-out infinite; }

  /* ask: leaning toward the question, one arm beckoning */
  .friend:not([data-quiet])[data-mood="ask"] .rig { animation: friend-ask 2s ease-in-out infinite; }
  .friend:not([data-quiet])[data-mood="ask"] .arm-r .arm-fx { animation: friend-beckon 2s ease-in-out infinite; }

  /* think: head sways a little, the puff bobs */
  .friend:not([data-quiet])[data-mood="think"] .head { animation: friend-think 2.4s ease-in-out infinite; }
  .friend:not([data-quiet])[data-mood="think"] .think-mark { animation: friend-bob 1.6s ease-in-out infinite; }

  /* happy: one jump with squash and stretch, sparkles pop; cheer: three bigger, faster jumps */
  .friend:not([data-quiet])[data-mood="happy"] { --jump: 24; }
  .friend:not([data-quiet])[data-mood="cheer"] { --jump: 32; }
  .friend:not([data-quiet])[data-mood="happy"] .rig { animation: friend-jump 1.1s both; }
  .friend:not([data-quiet])[data-mood="cheer"] .rig { animation: friend-jump .4s 3 both; }
  .friend:not([data-quiet])[data-mood="happy"] .shadow { animation: friend-shade 1.1s both; }
  .friend:not([data-quiet])[data-mood="cheer"] .shadow { animation: friend-shade .4s 3 both; }
  .friend:not([data-quiet])[data-mood="happy"] .arm-fx { animation: friend-flap 1.1s both; }
  .friend:not([data-quiet])[data-mood="cheer"] .arm-fx { animation: friend-wave .4s ease-in-out 3 both; }
  .friend:not([data-quiet])[data-mood="cheer"] .arm-r .arm-fx { animation-delay: .1s; }
  .friend:not([data-quiet]):is([data-mood="happy"], [data-mood="cheer"]) .tail { animation: friend-wag-fast .34s ease-in-out infinite; }
  .friend:not([data-quiet])[data-mood="happy"] .head { animation: friend-nod 1.1s both; }
  .friend:not([data-quiet])[data-mood="cheer"] .head { animation: friend-nod-cheer .4s ease-in-out 3 both; }
  .friend:not([data-quiet])[data-mood="happy"] .spark { animation: friend-pop 1.2s ease-out both; }
  .friend:not([data-quiet])[data-mood="cheer"] .spark { animation: friend-pop .8s ease-out 2 both; }
  .friend:not([data-quiet]):is([data-mood="happy"], [data-mood="cheer"]) .sparks > :nth-child(2) .spark { animation-delay: .08s; }
  .friend:not([data-quiet]):is([data-mood="happy"], [data-mood="cheer"]) .sparks > :nth-child(3) .spark { animation-delay: .16s; }
  .friend:not([data-quiet]):is([data-mood="happy"], [data-mood="cheer"]) .sparks > :nth-child(4) .spark { animation-delay: .24s; }
  .friend:not([data-quiet]):is([data-mood="happy"], [data-mood="cheer"]) .sparks > :nth-child(5) .spark { animation-delay: .32s; }

  /* oops: a slow head tilt and a wobble mark, no bounce */
  .friend:not([data-quiet])[data-mood="oops"] .head { animation: friend-oops 1s ease-out both; }
  .friend:not([data-quiet])[data-mood="oops"] .oops-wobble { animation: friend-wobble .7s ease-in-out 2; }

  /* sleep: z z z drift up in turn */
  .friend:not([data-quiet])[data-mood="sleep"] .z1 { animation: friend-zzz 3.4s ease-in-out infinite both; }
  .friend:not([data-quiet])[data-mood="sleep"] .z2 { animation: friend-zzz 3.4s ease-in-out 1.1s infinite both; }
  .friend:not([data-quiet])[data-mood="sleep"] .z3 { animation: friend-zzz 3.4s ease-in-out 2.2s infinite both; }

  /* chomp: a squash on each bite (the mouth itself is switched by friend.js timers) */
  .friend:not([data-quiet])[data-chomp] .body-wrap { animation: friend-chomp .5s ease-in-out both; }
}

@keyframes friend-breathe { 0%, 100% { transform: scale(1, 1); } 50% { transform: scale(.992, 1.02); } }
@keyframes friend-blink {
  0%, 89.8% { transform: scaleY(0); opacity: 0; }
  90% { transform: scaleY(0); opacity: 1; }
  93% { transform: scaleY(1); opacity: 1; }
  94.5% { transform: scaleY(1); opacity: 1; }
  99% { transform: scaleY(.04); opacity: 1; }
  99.2%, 100% { transform: scaleY(0); opacity: 0; }
}
@keyframes friend-twitch-l { 0%, 86%, 100% { transform: rotate(0); } 90% { transform: rotate(-7deg); } 94% { transform: rotate(3deg); } 97% { transform: rotate(-2deg); } }
@keyframes friend-twitch-r { 0%, 86%, 100% { transform: rotate(0); } 90% { transform: rotate(7deg); } 94% { transform: rotate(-3deg); } 97% { transform: rotate(2deg); } }
@keyframes friend-wag { 0%, 100% { transform: rotate(-3deg); } 50% { transform: rotate(5deg); } }
@keyframes friend-wag-fast { 0%, 100% { transform: rotate(-7deg); } 50% { transform: rotate(10deg); } }

@keyframes friend-ask { 0%, 100% { transform: rotate(4.5deg); } 50% { transform: rotate(6.2deg) translateX(1px); } }
@keyframes friend-beckon { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(9deg); } }
@keyframes friend-think { 0%, 100% { transform: rotate(-7deg); } 50% { transform: rotate(-4.5deg) translateY(-1px); } }
@keyframes friend-bob { 0%, 100% { transform: translateY(0) scale(1); } 50% { transform: translateY(-3px) scale(1.07); } }

@keyframes friend-jump {
  0% { transform: scale(1, 1); animation-timing-function: ease-out; }
  14% { transform: scale(1.07, .9); animation-timing-function: cubic-bezier(.2, .8, .3, 1); }
  34% { transform: translateY(calc(var(--jump, 24) * -1px)) scale(.94, 1.08); animation-timing-function: ease-out; }
  50% { transform: translateY(calc(var(--jump, 24) * -1px - 3px)) scale(.97, 1.03); animation-timing-function: ease-in; }
  68% { transform: translateY(0) scale(1.1, .86); animation-timing-function: ease-out; }
  82% { transform: scale(.97, 1.04); animation-timing-function: ease-in-out; }
  100% { transform: scale(1, 1); }
}
/* the head lags the body a little: down with the crouch, up and back with the launch, down again on landing */
@keyframes friend-nod { 0%, 100% { transform: translateY(0) rotate(0); } 14% { transform: translateY(4px) rotate(2deg); } 34% { transform: translateY(-3px) rotate(-3deg); } 68% { transform: translateY(4px) rotate(2deg); } 82% { transform: translateY(-1px) rotate(0); } }
@keyframes friend-nod-cheer { 0%, 100% { transform: translateY(0) rotate(0); } 35% { transform: translateY(-3px) rotate(-4deg); } 70% { transform: translateY(3px) rotate(3deg); } }
@keyframes friend-shade { 0%, 100% { transform: scale(1); opacity: 1; } 34%, 50% { transform: scale(.7); opacity: .55; } 68% { transform: scale(1.1); opacity: 1; } }
/* .arm-fx sits inside the mirrored right-hand group, so one keyframe set lifts both arms symmetrically */
@keyframes friend-flap { 0%, 100% { transform: rotate(0); } 14% { transform: rotate(-6deg); } 34% { transform: rotate(16deg); } 68% { transform: rotate(-8deg); } }
@keyframes friend-wave { 0%, 100% { transform: rotate(0); } 50% { transform: rotate(20deg); } }
@keyframes friend-pop {
  0% { transform: scale(0) rotate(-30deg); opacity: 0; }
  30% { transform: scale(1.25) rotate(10deg); opacity: 1; }
  55% { transform: scale(1) rotate(0); opacity: 1; }
  100% { transform: scale(.8) rotate(20deg); opacity: 0; }
}

@keyframes friend-oops { 0% { transform: rotate(0); } 30% { transform: rotate(9.5deg); } 55% { transform: rotate(6.5deg); } 100% { transform: rotate(8deg); } }
@keyframes friend-wobble { 0%, 100% { transform: rotate(-7deg); } 50% { transform: rotate(7deg); } }
@keyframes friend-zzz {
  0% { transform: translate(0, 6px) scale(.7); opacity: 0; }
  25%, 75% { opacity: 1; }
  100% { transform: translate(4px, -12px) scale(1.05); opacity: 0; }
}
@keyframes friend-chomp { 0%, 52%, 100% { transform: scale(1, 1); } 26%, 76% { transform: scale(1.05, .94); } }
