/* The Midas Assistant's movement.
 *
 * The character is the rendered artwork and is never redrawn — what moves is
 * the whole figure. Everything below therefore transforms one image, pivoting
 * on the nib, the way you would move a pen held at its point. A puppet, not a
 * cartoon rig.
 *
 * The last block matters most: with reduced motion asked for, nothing moves at
 * all. That is a stop, not a softening.
 */

/* It stands in the window, not inside anything. Fixed to the viewport, moved
   by transform, and it is the control that opens the assistant — so it is a
   real button with a focus ring, not a picture that happens to be clickable. */
.mascot {
  position: fixed;
  left: 0;
  top: 0;
  width: 72px;
  height: 108px;
  padding: 0;
  border: 0;
  background: none;
  z-index: 60;
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  /* Gliding to a new spot. The gestures animate the picture inside; this moves
     the whole character, so the two never fight over one transform. */
  transition: transform 2.6s cubic-bezier(.45, .05, .25, 1);
  touch-action: none;   /* so dragging it on a touch screen does not scroll the page */
}

/* While being dragged it must follow the finger exactly, with no easing in the
   way, and it should look picked up rather than walking. */
.mascot.is-dragging {
  transition: none;
  cursor: grabbing;
  z-index: 61;
}
.mascot.is-dragging .m-body { animation: none; transform: scale(1.08) rotate(-6deg); }
.mascot:focus-visible {
  outline: 3px solid var(--brand-blue, #1f7fb8);
  outline-offset: 6px;
  border-radius: 14px;
}
.mascot.is-off { display: none; }

@media (max-width: 600px) {
  .mascot { width: 58px; height: 87px; }
}

.m-stage {
  position: relative;
  width: 100%;
  height: 100%;
  /* The figure leaves its box when it hops or stretches; nothing may clip it. */
  overflow: visible;
}

.m-body {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  user-select: none;
  -webkit-user-drag: none;
  /* The nib. Every rotation happens about the point the pen would rest on,
     which is what stops the character looking like it is being shaken. */
  transform-origin: 24% 97%;
  will-change: transform;
  animation: m-float 4.8s ease-in-out infinite;
}

.m-fx {
  position: absolute;
  inset: -30% -34% -10% -34%;
  width: 168%;
  height: 140%;
  pointer-events: none;
  overflow: visible;
}
.m-fx * { transform-box: fill-box; transform-origin: 50% 50%; }

/* ------------------------------------------------------------------ resting */

@keyframes m-float {
  0%, 100% { transform: translateY(0) rotate(0deg) scale(1, 1); }
  22%      { transform: translateY(-11%) rotate(-6deg) scale(0.97, 1.05); }
  46%      { transform: translateY(1%) rotate(1deg) scale(1.04, 0.97); }
  70%      { transform: translateY(-6%) rotate(5deg) scale(0.99, 1.02); }
  88%      { transform: translateY(1%) rotate(-1deg) scale(1.02, 0.99); }
}

/* The small, frequent idle beat. A rendered picture cannot blink, so this is a
   shift of weight: a lean and a settle, over about a second. */
.mascot.is-twitch .m-body { animation: m-twitch 0.9s ease-in-out; }
@keyframes m-twitch {
  0%, 100% { transform: rotate(0deg) translateY(0); }
  30%      { transform: rotate(9deg) translateY(-5%); }
  62%      { transform: rotate(-5deg) translateY(1%); }
  85%      { transform: rotate(2deg) translateY(0); }
}

/* Travelling: it rocks along rather than gliding like a cursor. */
.mascot.is-travelling .m-body { animation: m-tiptoe 0.62s ease-in-out infinite; }

/* -------------------------------------------------------------------- moods */

/* Greeting: a wide, friendly sway, as though leaning in to say hello. */
.mood-wave .m-body { animation: m-wave 0.62s ease-in-out 3; }
@keyframes m-wave {
  0%, 100% { transform: rotate(0deg) translateY(0) scale(1); }
  25%      { transform: rotate(-30deg) translateY(-12%) scale(1.08); }
  62%      { transform: rotate(20deg) translateY(-4%) scale(1.04); }
}

/* Pointing: leans towards what it means, twice, then returns. */
.mood-point .m-body { animation: m-point 0.95s ease-out 2; }
@keyframes m-point {
  0%, 100% { transform: rotate(0deg) translate(0, 0) scale(1); }
  20%      { transform: rotate(14deg) translate(-8%, 2%) scale(0.94); }
  48%      { transform: rotate(-34deg) translate(22%, -10%) scale(1.12); }
  74%      { transform: rotate(-28deg) translate(26%, -6%) scale(1.08); }
}

/* Thinking: tips back and rocks slowly, the way someone weighing something up
   goes still and starts moving less, not more. */
.mood-think .m-body { animation: m-think 2.4s ease-in-out infinite; }
@keyframes m-think {
  0%, 100% { transform: rotate(16deg) translateY(-4%) scale(1.02); }
  50%      { transform: rotate(-2deg) translateY(3%) scale(0.99); }
}

/* Delight: hops, and squashes on landing. */
.mood-cheer .m-body { animation: m-hop 0.46s cubic-bezier(.28,.84,.42,1) 5; }
@keyframes m-hop {
  0%, 100% { transform: translateY(0) scale(1, 1) rotate(0deg); }
  18%      { transform: translateY(4%) scale(1.14, 0.84) rotate(0deg); }
  46%      { transform: translateY(-34%) scale(0.86, 1.22) rotate(-10deg); }
  72%      { transform: translateY(0) scale(1.18, 0.84) rotate(4deg); }
  88%      { transform: translateY(-6%) scale(0.98, 1.04) rotate(-2deg); }
}

/* Something went wrong: pulls back, then shakes its head. */
.mood-concern .m-body { animation: m-recoil 1.35s ease-out 2; }
@keyframes m-recoil {
  0%    { transform: rotate(0deg) translate(0, 0) scale(1); }
  14%   { transform: rotate(32deg) translate(20%, 6%) scale(0.9); }
  34%   { transform: rotate(10deg) translate(-10%, -2%) scale(1.06); }
  48%   { transform: rotate(24deg) translate(10%, 2%) scale(1); }
  62%   { transform: rotate(8deg) translate(-8%, 0) scale(1.03); }
  78%   { transform: rotate(16deg) translate(4%, 0) scale(1); }
  100%  { transform: rotate(0deg) translate(0, 0) scale(1); }
}

/* Writing. The pivot is the nib, so this reads as the pen being used. */
.mood-write .m-body { animation: m-write 0.44s ease-in-out 6; }
@keyframes m-write {
  0%, 100% { transform: rotate(-16deg) translate(6%, -3%) scale(1.05); }
  50%      { transform: rotate(-32deg) translate(-16%, 4%) scale(1.05); }
}

/* Idle gestures: rare, small, and over quickly. */
.mood-stretch .m-body { animation: m-stretch 2.3s ease-in-out; }
@keyframes m-stretch {
  0%, 100% { transform: scale(1, 1) rotate(0deg) translateY(0); }
  20%      { transform: scale(1.1, 0.86) rotate(0deg) translateY(6%); }
  55%      { transform: scale(0.9, 1.26) rotate(-14deg) translateY(-14%); }
  80%      { transform: scale(1.04, 0.96) rotate(4deg) translateY(1%); }
}

.mood-glance .m-body { animation: m-glance 2.4s ease-in-out; }
@keyframes m-glance {
  0%, 100% { transform: rotate(0deg) translateX(0); }
  28%      { transform: rotate(-24deg) translateX(-10%); }
  64%      { transform: rotate(22deg) translateX(10%); }
}

.mood-hum .m-body { animation: m-hum 1.15s ease-in-out 2; }
@keyframes m-hum {
  0%, 100% { transform: rotate(-12deg) translateY(0) scale(1); }
  50%      { transform: rotate(13deg) translateY(-8%) scale(1.05); }
}

/* A couple of quick hops, for no reason at all. */
.mood-bounce .m-body { animation: m-bounce 0.5s cubic-bezier(.3,.8,.4,1) 3; }
@keyframes m-bounce {
  0%, 100% { transform: translateY(0) scale(1, 1) rotate(0deg); }
  22%      { transform: translateY(3%) scale(1.12, 0.86) rotate(0deg); }
  55%      { transform: translateY(-26%) scale(0.9, 1.16) rotate(-7deg); }
  82%      { transform: translateY(0) scale(1.1, 0.9) rotate(3deg); }
}

/* Jelly. The nib stays put and everything above it sloshes. */
.mood-wobble .m-body { animation: m-wobble 1.5s cubic-bezier(.36,.07,.19,.97); }
@keyframes m-wobble {
  0%, 100% { transform: rotate(0deg) skewX(0deg) scale(1, 1); }
  14%      { transform: rotate(-16deg) skewX(6deg) scale(0.95, 1.08); }
  32%      { transform: rotate(13deg) skewX(-5deg) scale(1.06, 0.95); }
  50%      { transform: rotate(-9deg) skewX(3deg) scale(0.98, 1.04); }
  68%      { transform: rotate(6deg) skewX(-2deg) scale(1.03, 0.98); }
  85%      { transform: rotate(-3deg) skewX(1deg) scale(1, 1); }
}

/* Rocking from foot to foot, if a quill had feet. */
.mood-tiptoe .m-body { animation: m-tiptoe 0.62s ease-in-out 4; }
@keyframes m-tiptoe {
  0%, 100% { transform: rotate(-14deg) translate(-7%, 0) scale(1, 1); }
  50%      { transform: rotate(14deg) translate(7%, -7%) scale(1, 1.04); }
}

/* A signature in the air: the big sweeping arc a fountain pen wants to make. */
.mood-flourish .m-body { animation: m-flourish 1.5s cubic-bezier(.5,0,.3,1); }
@keyframes m-flourish {
  0%   { transform: rotate(0deg) translate(0, 0) scale(1); }
  18%  { transform: rotate(26deg) translate(14%, 4%) scale(0.94); }
  46%  { transform: rotate(-38deg) translate(-20%, -16%) scale(1.14); }
  68%  { transform: rotate(22deg) translate(16%, -6%) scale(1.06); }
  86%  { transform: rotate(-8deg) translate(-5%, 1%) scale(1.01); }
  100% { transform: rotate(0deg) translate(0, 0) scale(1); }
}

/* A full pirouette on the nib. Rare, and worth waiting for. */
.mood-spin .m-body { animation: m-spin 1.35s cubic-bezier(.45,.05,.25,1); }
@keyframes m-spin {
  0%   { transform: rotate(0deg) translateY(0) scale(1, 1); }
  14%  { transform: rotate(-22deg) translateY(4%) scale(1.06, 0.9); }
  30%  { transform: rotate(60deg) translateY(-16%) scale(0.94, 1.12); }
  62%  { transform: rotate(300deg) translateY(-18%) scale(0.94, 1.1); }
  84%  { transform: rotate(372deg) translateY(2%) scale(1.08, 0.92); }
  100% { transform: rotate(360deg) translateY(0) scale(1, 1); }
}

/* Waking up, or arriving. */
.mood-peek .m-body { animation: m-peek 0.85s cubic-bezier(.2,.9,.3,1.2); }
@keyframes m-peek {
  0%   { transform: translateY(46%) scale(0.62) rotate(-16deg); }
  48%  { transform: translateY(-14%) scale(1.18) rotate(8deg); }
  74%  { transform: translateY(4%) scale(0.96) rotate(-4deg); }
  100% { transform: translateY(0) scale(1) rotate(0deg); }
}

/* Asleep: leans over and breathes slowly. */
.mood-sleep .m-body { animation: m-snooze 5.2s ease-in-out infinite; }
@keyframes m-snooze {
  0%, 100% { transform: rotate(26deg) translateY(8%) scale(1.02, 0.94); }
  50%      { transform: rotate(29deg) translateY(12%) scale(1.05, 1.02); }
}

/* ---------------------------------------------------------------- extra bits */

.m-sparkles, .m-zzz { opacity: 0; transition: opacity 220ms ease; }

.mood-cheer .m-sparkles { opacity: 1; }
.mood-cheer .m-sparkles path { animation: m-twinkle 0.78s ease-in-out 3; }
.mood-cheer .m-sparkles path:nth-child(2) { animation-delay: 0.16s; }
.mood-cheer .m-sparkles path:nth-child(3) { animation-delay: 0.32s; }
@keyframes m-twinkle {
  0%, 100% { opacity: 0.2; transform: scale(0.75) rotate(0deg); }
  50%      { opacity: 1; transform: scale(1.18) rotate(22deg); }
}

.mood-sleep .m-zzz { opacity: 1; }
.mood-sleep .m-z1 { animation: m-zdrift 3.6s ease-out infinite; }
.mood-sleep .m-z2 { animation: m-zdrift 3.6s ease-out 1.2s infinite; }
.mood-sleep .m-z3 { animation: m-zdrift 3.6s ease-out 2.4s infinite; }
@keyframes m-zdrift {
  0%   { opacity: 0; transform: translate(0, 8px) scale(0.7); }
  30%  { opacity: 0.9; }
  100% { opacity: 0; transform: translate(12px, -26px) scale(1.2); }
}

/* -------------------------------------------------------- reduced motion off */

/* Not a softening — a stop. Someone who has told their computer that movement
   makes them unwell should not have to find a setting in here as well. */
.mascot.is-still .m-body,
.mascot.is-still .m-sparkles,
.mascot.is-still .m-sparkles path,
.mascot.is-still .m-zzz text {
  animation: none !important;
  transform: none !important;
}

@media (prefers-reduced-motion: reduce) {
  .mascot .m-body,
  .mascot .m-sparkles,
  .mascot .m-sparkles path,
  .mascot .m-zzz text {
    animation: none !important;
    transform: none !important;
  }
}

/* ------------------------------------------------------------------- badge */

/* How many emails have arrived. It rides on the character, above the picture,
   and it is not affected by the gestures — a number that swings about with a
   pirouette is unreadable, which defeats the point of it. */
.m-badge {
  position: absolute;
  top: 4px;
  right: 4px;
  min-width: 24px;
  height: 24px;
  padding: 0 6px;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  border-radius: 999px;
  background: #d92c3d;
  color: #fff;
  font: 700 12px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
  letter-spacing: 0.2px;
  box-shadow: 0 2px 8px #6b0a1466, 0 0 0 2.5px #fff;
  pointer-events: none;
  z-index: 2;
}
.m-badge[hidden] { display: none; }

/* The message bell.
 *
 * Deliberately a different colour and a different shoulder from the mail
 * count. Red on the right means a student has written; teal on the left with a
 * bell means a colleague has. Two numbers in the same colour on the same corner
 * would be two numbers nobody could tell apart at a glance, which is the whole
 * value of a badge. */
.m-bell {
  position: absolute;
  top: 4px;
  left: 4px;
  min-width: 24px;
  height: 24px;
  padding: 0 6px 0 4px;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 2px;
  box-sizing: border-box;
  border-radius: 999px;
  background: #0d6551;
  color: #fff;
  font: 700 12px/1 system-ui, -apple-system, "Segoe UI", sans-serif;
  letter-spacing: 0.2px;
  box-shadow: 0 2px 8px #04332866, 0 0 0 2.5px #fff;
  pointer-events: none;
  z-index: 2;
}
.m-bell[hidden] { display: none; }
.m-bell svg { width: 13px; height: 13px; fill: currentColor; flex: none; }

/* A bell rings by rocking, not by pulsing — so an arriving message reads
   differently from arriving mail even out of the corner of an eye. */
.mascot.is-messaging .m-bell { animation: m-bell-ring 0.7s ease-in-out 3; transform-origin: 50% 20%; }
@keyframes m-bell-ring {
  0%, 100% { transform: rotate(0deg); }
  15%      { transform: rotate(-16deg); }
  35%      { transform: rotate(13deg); }
  55%      { transform: rotate(-8deg); }
  75%      { transform: rotate(4deg); }
}

/* Something has just arrived. Two seconds of obvious, then it stops: a badge
   that pulses for ever becomes wallpaper and is no longer a signal. */
.mascot.is-alerting .m-badge { animation: m-badge-pop 0.55s cubic-bezier(.3,1.4,.5,1) 4; }
@keyframes m-badge-pop {
  0%, 100% { transform: scale(1); }
  35%      { transform: scale(1.35); }
  70%      { transform: scale(0.95); }
}

/* A ring going out from the character, so the arrival is visible even to
   someone glancing at the far side of the screen. */
.mascot.is-alerting .m-stage::after {
  content: "";
  position: absolute;
  inset: 12% 6% 4% 6%;
  border-radius: 50%;
  border: 3px solid #35bce9;
  animation: m-ripple 1.1s ease-out 2;
  pointer-events: none;
}
@keyframes m-ripple {
  0%   { opacity: 0.75; transform: scale(0.55); }
  100% { opacity: 0; transform: scale(1.9); }
}

/* Both of those are movement, so both stop when movement is unwelcome. The
   badge itself stays: it is information, not decoration. */
.mascot.is-still.is-alerting .m-badge,
.mascot.is-still .m-stage::after { animation: none; }
@media (prefers-reduced-motion: reduce) {
  .mascot.is-alerting .m-badge { animation: none; }
  .mascot.is-alerting .m-stage::after { animation: none; opacity: 0; }
  /* The bell still appears and still counts; it just does not rock. */
  .mascot.is-messaging .m-bell { animation: none; }
}
