/*
 * The Tresi bunny — state machine.
 *
 * Pairs with `bunny.svg`, which must be inlined into the DOM (an <img> cannot
 * see this stylesheet). Set exactly one `bunny--<state>` class on the root.
 *
 * Design rules this file enforces mechanically:
 *
 *  - Pose is *information*, animation is decoration. Under
 *    `prefers-reduced-motion` the keyframe loops stop but every pose still
 *    resolves, so a reduced-motion user loses the bounce and keeps the status.
 *  - The bunny never scolds. `attentive` (approaching budget) tilts and lowers
 *    the ears; there is deliberately no frowning, no red, and no "over budget"
 *    angry state. Peach, not red — see tokens.css.
 *  - Colours come from tokens. The fallbacks let the SVG render standalone in a
 *    browser tab for design review without the token sheet loaded.
 */

.bunny {
  width: 100%;
  height: auto;
  max-width: 160px;
  display: block;
  overflow: visible;
}

/* `fill-box` makes transform-origin resolve against each shape's own bounding
 * box. Without it, SVG transform-origins resolve against the viewport and every
 * rotation below swings wildly off-centre. */
.bunny__ear,
.bunny__head,
.bunny__all,
.bunny__eye,
.bunny__eyes,
.bunny__nose,
.bunny__bit {
  transform-box: fill-box;
}

/* ---- Fills --------------------------------------------------------------- */

/* Uses --c-bunny-fill rather than --c-surface: the bunny is nearly always drawn
 * on a surface, and sharing that token leaves only a hairline stroke between
 * the two — fine on white, near-invisible in dark mode. */
.bunny__body,
.bunny__face,
.bunny__ear-outer,
.bunny__foot {
  fill: var(--c-bunny-fill, #fffdfa);
  stroke: var(--c-bunny-line, #cdbfae);
  stroke-width: 2;
}

.bunny__ear-inner {
  fill: var(--c-rose-soft, #fae7eb);
}

.bunny__eye {
  fill: var(--c-text, #2e2a26);
}

.bunny__eye-line {
  fill: none;
  stroke: var(--c-text, #2e2a26);
  stroke-width: 2.2;
  stroke-linecap: round;
}

.bunny__cheek {
  fill: var(--c-rose, #d47a8c);
  opacity: 0.35;
}

.bunny__nose {
  fill: var(--c-rose, #d47a8c);
  transform-origin: center;
}

.bunny__mouth {
  fill: none;
  stroke: var(--c-text-muted, #6f665d);
  stroke-width: 1.6;
  stroke-linecap: round;
}

.bunny__z,
.bunny__query {
  fill: var(--c-text-faint, #9c9188);
  font-family: var(--font-sans, system-ui);
  font-weight: 700;
  font-size: 13px;
}

.bunny__query {
  fill: var(--c-accent, #7c6bd4);
  font-size: 18px;
}

.bunny__bit:nth-child(odd) {
  fill: var(--c-accent, #7c6bd4);
}
.bunny__bit:nth-child(even) {
  fill: var(--c-butter, #e0b357);
}

/* ---- Default visibility --------------------------------------------------
 * Accessory groups are hidden by default and revealed per state. Opacity
 * rather than `display` so they can cross-fade. */
.bunny__zzz,
.bunny__query,
.bunny__confetti,
.bunny__eyes-closed {
  opacity: 0;
  transition: opacity var(--dur-base, 240ms) var(--ease-out, ease);
}

.bunny__eyes,
.bunny__ear,
.bunny__head,
.bunny__all {
  transition:
    transform var(--dur-slow, 420ms) var(--ease-spring, ease-out),
    opacity var(--dur-base, 240ms) var(--ease-out, ease);
}

/* ---- idle ----------------------------------------------------------------
 * Alive but not demanding. A slow blink and a nose twitch; nothing that pulls
 * the eye away from the numbers next to it. */
.bunny--idle .bunny__eyes {
  animation: bunny-blink 5.5s var(--ease-out, ease) infinite;
}
.bunny--idle .bunny__nose {
  animation: bunny-twitch 4s var(--ease-out, ease) infinite;
}

@keyframes bunny-blink {
  0%,
  92%,
  100% {
    transform: scaleY(1);
  }
  95% {
    transform: scaleY(0.1);
  }
}

@keyframes bunny-twitch {
  0%,
  88%,
  100% {
    transform: translateY(0) scale(1);
  }
  92% {
    transform: translateY(-0.6px) scale(1.12);
  }
}

/* ---- syncing -------------------------------------------------------------
 * Ears perked and a gentle bob: "I'm working". Loops, so it reads as ongoing
 * rather than as a completed event. */
.bunny--syncing .bunny__ear--left {
  transform: rotate(-7deg);
}
.bunny--syncing .bunny__ear--right {
  transform: rotate(7deg);
}
.bunny--syncing .bunny__all {
  animation: bunny-bob 1.4s var(--ease-out, ease) infinite;
}

@keyframes bunny-bob {
  0%,
  100% {
    transform: translateY(0);
  }
  50% {
    transform: translateY(-3px);
  }
}

/* ---- sleeping ------------------------------------------------------------
 * Offline. Eyes closed, ears folded down, slow breathing, drifting z's. */
.bunny--sleeping .bunny__eyes {
  opacity: 0;
}
.bunny--sleeping .bunny__eyes-closed {
  opacity: 1;
}
.bunny--sleeping .bunny__ear--left {
  transform: rotate(-32deg);
}
.bunny--sleeping .bunny__ear--right {
  transform: rotate(32deg);
}
.bunny--sleeping .bunny__all {
  animation: bunny-breathe 4s ease-in-out infinite;
}
.bunny--sleeping .bunny__zzz {
  opacity: 1;
}
.bunny--sleeping .bunny__z--1 {
  animation: bunny-drift 3s ease-out infinite;
}
.bunny--sleeping .bunny__z--2 {
  animation: bunny-drift 3s ease-out infinite 1.5s;
}

@keyframes bunny-breathe {
  0%,
  100% {
    transform: scale(1);
  }
  50% {
    transform: scale(1.025);
  }
}

@keyframes bunny-drift {
  0% {
    opacity: 0;
    transform: translate(0, 0);
  }
  30% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translate(5px, -10px);
  }
}

/* ---- pleased -------------------------------------------------------------
 * Under budget, or a receipt just matched. Happy closed-arc eyes and one hop.
 * Deliberately not looped: it marks a moment, then settles. */
.bunny--pleased .bunny__eyes {
  opacity: 0;
}
.bunny--pleased .bunny__eyes-closed {
  opacity: 1;
}
.bunny--pleased .bunny__ear--left {
  transform: rotate(-10deg);
}
.bunny--pleased .bunny__ear--right {
  transform: rotate(10deg);
}
.bunny--pleased .bunny__cheek {
  opacity: 0.55;
}
.bunny--pleased .bunny__all {
  animation: bunny-hop 0.6s var(--ease-spring, ease-out) 1;
}

@keyframes bunny-hop {
  0%,
  100% {
    transform: translateY(0);
  }
  40% {
    transform: translateY(-9px);
  }
  70% {
    transform: translateY(1px);
  }
}

/* ---- attentive -----------------------------------------------------------
 * Approaching a budget threshold. Head tilt, ears half-down — curious and a
 * little careful. NOT sad, NOT alarmed: this is the state a user sees most
 * often when they are slightly overspending, and making it feel like a
 * reprimand is how a finance app gets deleted. */
.bunny--attentive .bunny__head {
  transform: rotate(-7deg);
  transform-origin: 50% 90%;
}
.bunny--attentive .bunny__ear--left {
  transform: rotate(-20deg);
}
.bunny--attentive .bunny__ear--right {
  transform: rotate(4deg);
}

/* ---- curious -------------------------------------------------------------
 * Items waiting in the review queue. Head tilt plus a question mark. */
.bunny--curious .bunny__head {
  transform: rotate(8deg);
  transform-origin: 50% 90%;
}
.bunny--curious .bunny__ear--left {
  transform: rotate(6deg);
}
.bunny--curious .bunny__ear--right {
  transform: rotate(22deg);
}
.bunny--curious .bunny__query {
  opacity: 1;
  animation: bunny-pulse 2.4s var(--ease-out, ease) infinite;
}

@keyframes bunny-pulse {
  0%,
  100% {
    opacity: 0.55;
  }
  50% {
    opacity: 1;
  }
}

/* ---- celebrating ---------------------------------------------------------
 * Month closed under budget. Rare on purpose — if this fires weekly it stops
 * meaning anything. */
.bunny--celebrating .bunny__eyes {
  opacity: 0;
}
.bunny--celebrating .bunny__eyes-closed {
  opacity: 1;
}
.bunny--celebrating .bunny__cheek {
  opacity: 0.6;
}
.bunny--celebrating .bunny__ear--left {
  transform: rotate(-14deg);
}
.bunny--celebrating .bunny__ear--right {
  transform: rotate(14deg);
}
.bunny--celebrating .bunny__all {
  animation: bunny-hop 0.62s var(--ease-spring, ease-out) 3;
}
.bunny--celebrating .bunny__confetti {
  opacity: 1;
}
.bunny--celebrating .bunny__bit {
  animation: bunny-fall 1.6s ease-in infinite;
}
.bunny--celebrating .bunny__bit--2 {
  animation-delay: 0.2s;
}
.bunny--celebrating .bunny__bit--3 {
  animation-delay: 0.45s;
}
.bunny--celebrating .bunny__bit--4 {
  animation-delay: 0.7s;
}

@keyframes bunny-fall {
  0% {
    opacity: 0;
    transform: translateY(0) rotate(0deg);
  }
  20% {
    opacity: 1;
  }
  100% {
    opacity: 0;
    transform: translateY(26px) rotate(220deg);
  }
}

/* ---- Reduced motion ------------------------------------------------------
 * Stop the loops; keep every pose. The static transforms above are plain
 * declarations, not keyframes, so they survive this block untouched — the
 * bunny still reads as sleeping, attentive, or curious, it just stops moving.
 * Accessory opacity is preserved too, so z's and the question mark remain. */
@media (prefers-reduced-motion: reduce) {
  .bunny *,
  .bunny {
    animation: none !important;
  }
  /* Without the blink loop the eyes must be explicitly open. */
  .bunny--idle .bunny__eyes,
  .bunny--syncing .bunny__eyes {
    opacity: 1;
    transform: none;
  }
  .bunny--sleeping .bunny__zzz,
  .bunny--curious .bunny__query,
  .bunny--celebrating .bunny__confetti {
    opacity: 1;
  }
}
