/*
 * A black page with a moving rainbow aurora and a yard of yellow and white paw
 * prints. Nothing else: no text, no menu. Tap, click or hold and the paws
 * nearby grow; a paw that grows too big pops into a shower of little ones.
 */

:root {
  --yellow: #ffd23f;
  --white: #ffffff;
  --ink: #000000;
  /* The rainbow runs twice so that, at double width, all of it is on screen
     and a sideways slide loops without a seam. */
  --rainbow: linear-gradient(
    115deg,
    #e40303,
    #ff8c00,
    #ffed00,
    #00a651,
    #2d7bff,
    #a04be0,
    #ff5fa2,
    #e40303,
    #ff8c00,
    #ffed00,
    #00a651,
    #2d7bff,
    #a04be0,
    #ff5fa2,
    #e40303
  );
  --wheel: conic-gradient(
    from 0deg,
    #e40303,
    #ff8c00,
    #ffed00,
    #00a651,
    #2d7bff,
    #a04be0,
    #ff5fa2,
    #e40303
  );
  --spring: cubic-bezier(0.34, 1.56, 0.64, 1);
  color-scheme: dark;
}

*,
*::before,
*::after {
  box-sizing: border-box;
}

html,
body {
  height: 100%;
  margin: 0;
  overflow: hidden;
  background: var(--ink);
}

body {
  touch-action: manipulation;
  user-select: none;
  -webkit-user-select: none;
  -webkit-tap-highlight-color: transparent;
  overscroll-behavior: none;
}

.sprite {
  position: absolute;
  width: 0;
  height: 0;
  overflow: hidden;
}

.layer {
  position: fixed;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* ── the sky ────────────────────────────────────────────────────────────── */

.sky {
  position: fixed;
  inset: 0;
  overflow: hidden;
}

/* Three soft rainbow wheels that wander and turn on their own clocks. Where
   they overlap the colors add up, so the aurora never repeats itself. */
.blob {
  position: absolute;
  width: 78vmax;
  height: 78vmax;
  border-radius: 50%;
  background: var(--wheel);
  filter: blur(72px) saturate(1.3);
  mix-blend-mode: screen;
  opacity: 0.9;
  will-change: transform;
}

.blob-a {
  left: -22vmax;
  top: -30vmax;
}

.blob-b {
  right: -30vmax;
  bottom: -34vmax;
  opacity: 0.8;
}

.blob-c {
  left: 24vmax;
  top: 18vmax;
  width: 52vmax;
  height: 52vmax;
  opacity: 0.65;
}

/* Black pulls in from the corners, so it stays a rainbow on black and never
   turns into a wall of color. */
.sky::after {
  content: '';
  position: absolute;
  inset: 0;
  background:
    radial-gradient(ellipse at 50% 50%, transparent 0%, rgb(0 0 0 / 0.18) 50%, rgb(0 0 0 / 0.92) 96%),
    radial-gradient(circle at 18% 24%, rgb(0 0 0 / 0.5) 0%, transparent 30%),
    radial-gradient(circle at 82% 72%, rgb(0 0 0 / 0.5) 0%, transparent 32%);
}

/* A scatter of faint stars, drawn with gradients so nothing is downloaded. */
.stars {
  position: absolute;
  inset: 0;
  z-index: 1;
  opacity: 0.7;
  background-image:
    radial-gradient(1.5px 1.5px at 12% 18%, #fff, transparent),
    radial-gradient(1px 1px at 27% 64%, #fff, transparent),
    radial-gradient(1.5px 1.5px at 41% 31%, var(--yellow), transparent),
    radial-gradient(1px 1px at 58% 79%, #fff, transparent),
    radial-gradient(1.5px 1.5px at 69% 12%, #fff, transparent),
    radial-gradient(1px 1px at 83% 47%, var(--yellow), transparent),
    radial-gradient(1.5px 1.5px at 92% 86%, #fff, transparent),
    radial-gradient(1px 1px at 6% 88%, #fff, transparent),
    radial-gradient(1px 1px at 49% 8%, #fff, transparent),
    radial-gradient(1.5px 1.5px at 74% 93%, #fff, transparent);
}

/* A thin rainbow line around the whole window. */
body::after {
  content: '';
  position: fixed;
  inset: 10px;
  border: 3px solid transparent;
  border-radius: 26px;
  background: var(--rainbow) border-box;
  background-size: 200% 100%;
  mask:
    linear-gradient(#000 0 0) padding-box,
    linear-gradient(#000 0 0);
  mask-composite: exclude;
  pointer-events: none;
  z-index: 5;
}

/* ── the yard ───────────────────────────────────────────────────────────── */

.yard {
  position: fixed;
  inset: 0;
  cursor: pointer;
  /* How far the pointer is from the middle, -1 to 1. Paws lean toward it. */
  --px: 0;
  --py: 0;
}

.yard:focus-visible {
  outline: 3px solid var(--yellow);
  outline-offset: -6px;
}

/* The wrapper bobs so the paw's own transform stays free for growing. */
.step {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

.paw {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--size);
  height: var(--size);
  margin: calc(var(--size) / -2) 0 0 calc(var(--size) / -2);
  fill: var(--white);
  /* A dark edge keeps white and yellow readable over the bright aurora. */
  stroke: var(--ink);
  stroke-width: 0.9;
  paint-order: stroke;
  filter: drop-shadow(0 6px 10px rgb(0 0 0 / 0.6));
  /* `translate` is its own property, so leaning and growing do not fight. */
  translate: calc(var(--px) * var(--depth) * 1px) calc(var(--py) * var(--depth) * 1px);
  transform: rotate(var(--turn)) scale(var(--grow, 1));
  transition:
    transform 520ms var(--spring),
    translate 500ms ease-out,
    filter 260ms ease;
  will-change: transform;
}

.paw.yellow {
  fill: var(--yellow);
}

/* A paw that has grown, or that the pointer is near, lights up in its own
   color from the rainbow. */
.paw.lit {
  filter: drop-shadow(0 0 10px hsl(var(--hue) 100% 62%))
    drop-shadow(0 0 26px hsl(var(--hue) 100% 55% / 0.75)) drop-shadow(0 6px 10px rgb(0 0 0 / 0.6));
}

.paw.pop {
  animation: pop 300ms ease-out forwards;
}

/* ── walking trails ─────────────────────────────────────────────────────── */

.print {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: 34px;
  height: 34px;
  margin: -17px 0 0 -17px;
  stroke: var(--ink);
  stroke-width: 0.9;
  paint-order: stroke;
  fill: var(--white);
  opacity: 0;
  transform: rotate(var(--turn));
  animation: stamp 2600ms ease-out var(--delay) forwards;
}

.print.yellow {
  fill: var(--yellow);
}

/* ── tap ripple ─────────────────────────────────────────────────────────── */

.ring {
  position: absolute;
  left: var(--x);
  top: var(--y);
  width: var(--reach);
  height: var(--reach);
  margin: calc(var(--reach) / -2) 0 0 calc(var(--reach) / -2);
  border-radius: 50%;
  background: var(--wheel);
  /* Hollow it out: only a band of rainbow is left. */
  mask: radial-gradient(circle, transparent 62%, #000 64%, #000 69%, transparent 71%);
  animation: ripple 760ms ease-out forwards;
}

/* ── motion ─────────────────────────────────────────────────────────────── */

@keyframes flow {
  to {
    background-position: 100% 0;
  }
}

@keyframes wander-a {
  0% {
    transform: translate(0, 0) rotate(0deg);
  }
  50% {
    transform: translate(26vmax, 22vmax) rotate(180deg);
  }
  100% {
    transform: translate(0, 0) rotate(360deg);
  }
}

@keyframes wander-b {
  0% {
    transform: translate(0, 0) rotate(0deg);
  }
  50% {
    transform: translate(-30vmax, -18vmax) rotate(-180deg);
  }
  100% {
    transform: translate(0, 0) rotate(-360deg);
  }
}

@keyframes wander-c {
  0% {
    transform: translate(0, 0) rotate(0deg) scale(1);
  }
  33% {
    transform: translate(-24vmax, 14vmax) rotate(120deg) scale(1.3);
  }
  66% {
    transform: translate(20vmax, -16vmax) rotate(240deg) scale(0.8);
  }
  100% {
    transform: translate(0, 0) rotate(360deg) scale(1);
  }
}

@keyframes twinkle {
  50% {
    opacity: 0.25;
  }
}

@keyframes bob {
  50% {
    transform: translateY(var(--lift));
  }
}

@keyframes land {
  from {
    opacity: 0;
    transform: scale(0.2);
  }
}

@keyframes pop {
  to {
    opacity: 0;
    transform: rotate(var(--turn)) scale(calc(var(--grow, 1) * 1.7));
  }
}

@keyframes stamp {
  0% {
    opacity: 0;
    transform: rotate(var(--turn)) scale(1.5);
  }
  12% {
    opacity: 0.85;
    transform: rotate(var(--turn)) scale(1);
  }
  60% {
    opacity: 0.5;
  }
  100% {
    opacity: 0;
    transform: rotate(var(--turn)) scale(1);
  }
}

@keyframes ripple {
  from {
    opacity: 0.95;
    transform: scale(0.08);
  }
  to {
    opacity: 0;
    transform: scale(1);
  }
}

@media (prefers-reduced-motion: no-preference) {
  body::after {
    animation: flow 22s linear infinite;
  }

  .blob-a {
    animation: wander-a 38s ease-in-out infinite;
  }

  .blob-b {
    animation: wander-b 47s ease-in-out infinite;
  }

  .blob-c {
    animation: wander-c 31s ease-in-out infinite;
  }

  .stars {
    animation: twinkle 5s ease-in-out infinite;
  }

  .step {
    animation:
      land 600ms var(--spring) both,
      bob var(--pace) ease-in-out infinite;
    animation-delay: var(--delay), calc(var(--delay) + 600ms);
  }
}

/* Still works, just stands still: taps grow paws with no travel or sparks. */
@media (prefers-reduced-motion: reduce) {
  .paw {
    transition-duration: 1ms;
    translate: none;
  }

  .ring,
  .print {
    display: none;
  }
}
