/* Uses the canonical mark generated from resources/img/mark.svg.
   Set --gianna-spinner-size, --gianna-spinner-duration and color on the host.
   Wrap in a labelled role="status"; hide the visual from assistive technology. */
.gianna-spinner {
  --gianna-spinner-size: 24px;
  --gianna-spinner-duration: 2.4s;
  display: inline-block;
  width: var(--gianna-spinner-size);
  height: var(--gianna-spinner-size);
  flex: none;
  vertical-align: middle;
  color: var(--gold, #c8a24b);
  perspective: calc(var(--gianna-spinner-size) * 4);
}

.gianna-spinner__orbit,
.gianna-spinner__tilt,
.gianna-spinner__rotor,
.gianna-spinner__mark {
  display: block;
  width: 100%;
  height: 100%;
  transform-origin: 50% 50%;
}

.gianna-spinner__orbit,
.gianna-spinner__tilt,
.gianna-spinner__rotor {
  transform-style: preserve-3d;
}

.gianna-spinner__mark {
  background: currentColor;
  -webkit-mask: url('/static/img/mark-gold.svg') center / contain no-repeat;
  mask: url('/static/img/mark-gold.svg') center / contain no-repeat;
  /* Both sides of the mark remain visible during a turn. */
  backface-visibility: visible;
}

.gianna-spinner__rotor {
  animation: gianna-spinner-turn var(--gianna-spinner-duration) linear infinite;
}

/* A fixed inclined plane: each spoke travels from the back to the front. */
.gianna-spinner--tilted .gianna-spinner__tilt {
  transform: rotateX(58deg);
}

/* The normal to the plane describes a cone while the mark spins within it.
   The outer turn and inner counter-turn keep precession distinct from spin. */
.gianna-spinner--precessing .gianna-spinner__orbit {
  animation: gianna-spinner-turn calc(var(--gianna-spinner-duration) * 3) linear infinite;
}

.gianna-spinner--precessing .gianna-spinner__tilt {
  transform: rotateX(46deg);
}

.gianna-spinner--precessing .gianna-spinner__rotor {
  animation-name: gianna-spinner-counterturn;
}

/* Pure rotating tilt: Rz(phi) Rx(tilt) Rz(-phi). Equal and opposite
   turns cancel the in-plane spin; only the tilt axis travels around the rim.
   Both animations start together and use exactly the same period. */
.gianna-spinner--wobble .gianna-spinner__orbit {
  animation: gianna-spinner-turn calc(var(--gianna-spinner-duration) * 1.5) linear infinite;
}

.gianna-spinner--wobble .gianna-spinner__tilt {
  transform: rotateX(48deg);
}

.gianna-spinner--wobble .gianna-spinner__rotor {
  animation: gianna-spinner-counterturn calc(var(--gianna-spinner-duration) * 1.5) linear infinite;
}

/* The vertical iota is the axle; the other crossed forms turn around it. */
.gianna-spinner--axial .gianna-spinner__tilt {
  transform: rotateZ(-30deg) rotateX(22deg);
}

.gianna-spinner--axial .gianna-spinner__rotor {
  animation-name: gianna-spinner-axial;
}

/* Brief holds between spring-like sixth turns. */
.gianna-spinner--ratchet .gianna-spinner__rotor {
  animation-name: gianna-spinner-ratchet;
  animation-timing-function: cubic-bezier(.2, .8, .25, 1.2);
  animation-duration: calc(var(--gianna-spinner-duration) * 1.5);
}

/* Spin in the mark's own plane while the plane tumbles around two axes. */
.gianna-spinner--gyroscope .gianna-spinner__tilt {
  animation: gianna-spinner-tumble calc(var(--gianna-spinner-duration) * 2) linear infinite;
}

/* The whole mark approaches and recedes while spinning in a tilted plane. */
.gianna-spinner--spiral .gianna-spinner__orbit {
  animation: gianna-spinner-depth calc(var(--gianna-spinner-duration) * 1.5) ease-in-out infinite;
}

.gianna-spinner--spiral .gianna-spinner__tilt {
  transform: rotateX(38deg) rotateY(-26deg);
}

/* Two crossing loops, with a gentle bank as the mark changes direction. */
.gianna-spinner--eight .gianna-spinner__orbit {
  animation: gianna-spinner-eight calc(var(--gianna-spinner-duration) * 2) linear infinite;
}

.gianna-spinner--eight .gianna-spinner__tilt {
  animation: gianna-spinner-bank var(--gianna-spinner-duration) ease-in-out infinite;
}

.gianna-spinner--eight .gianna-spinner__rotor {
  animation-name: gianna-spinner-counterturn;
  animation-duration: calc(var(--gianna-spinner-duration) * 2);
}

/* A rocking axle with two quick flips and a short settling motion. */
.gianna-spinner--rocking .gianna-spinner__tilt {
  animation: gianna-spinner-rock calc(var(--gianna-spinner-duration) * 1.5) ease-in-out infinite;
}

.gianna-spinner--rocking .gianna-spinner__rotor {
  animation: gianna-spinner-flip calc(var(--gianna-spinner-duration) * 1.5) cubic-bezier(.4, 0, .2, 1) infinite;
}

.gianna-spinner[data-paused] .gianna-spinner__orbit,
.gianna-spinner[data-paused] .gianna-spinner__tilt,
.gianna-spinner[data-paused] .gianna-spinner__rotor {
  animation-play-state: paused;
}

@keyframes gianna-spinner-turn {
  from { transform: rotateZ(0deg); }
  to { transform: rotateZ(360deg); }
}

@keyframes gianna-spinner-counterturn {
  from { transform: rotateZ(0deg); }
  to { transform: rotateZ(-360deg); }
}

@keyframes gianna-spinner-axial {
  from { transform: rotateY(0deg); }
  to { transform: rotateY(360deg); }
}

@keyframes gianna-spinner-ratchet {
  0%, 8% { transform: rotateZ(0deg); }
  16%, 24% { transform: rotateZ(60deg); }
  32%, 40% { transform: rotateZ(120deg); }
  48%, 56% { transform: rotateZ(180deg); }
  64%, 72% { transform: rotateZ(240deg); }
  80%, 88% { transform: rotateZ(300deg); }
  96%, 100% { transform: rotateZ(360deg); }
}

@keyframes gianna-spinner-tumble {
  0% { transform: rotateX(0deg) rotateY(0deg); }
  25% { transform: rotateX(90deg) rotateY(-45deg); }
  50% { transform: rotateX(180deg) rotateY(0deg); }
  75% { transform: rotateX(270deg) rotateY(45deg); }
  100% { transform: rotateX(360deg) rotateY(0deg); }
}

@keyframes gianna-spinner-depth {
  0%, 100% { transform: translateZ(calc(var(--gianna-spinner-size) * -.65)); }
  50% { transform: translateZ(calc(var(--gianna-spinner-size) * .45)); }
}

@keyframes gianna-spinner-eight {
  0%, 50%, 100% { transform: translate(0, 0); }
  12.5% { transform: translate(10%, -12%); }
  25% { transform: translate(16%, 0); }
  37.5% { transform: translate(10%, 12%); }
  62.5% { transform: translate(-10%, -12%); }
  75% { transform: translate(-16%, 0); }
  87.5% { transform: translate(-10%, 12%); }
}

@keyframes gianna-spinner-bank {
  0%, 100% { transform: rotateX(22deg) rotateY(-20deg); }
  50% { transform: rotateX(-22deg) rotateY(20deg); }
}

@keyframes gianna-spinner-rock {
  0%, 100% { transform: rotateZ(-22deg) rotateX(15deg); }
  50% { transform: rotateZ(22deg) rotateX(-15deg); }
}

@keyframes gianna-spinner-flip {
  0%, 12% { transform: rotateY(0deg); }
  20% { transform: rotateY(-16deg); }
  42% { transform: rotateY(196deg); }
  50%, 62% { transform: rotateY(180deg); }
  70% { transform: rotateY(164deg); }
  92% { transform: rotateY(376deg); }
  100% { transform: rotateY(360deg); }
}

@media (prefers-reduced-motion: reduce) {
  .gianna-spinner .gianna-spinner__orbit,
  .gianna-spinner .gianna-spinner__tilt,
  .gianna-spinner .gianna-spinner__rotor {
    animation: none;
    transform: none;
  }
}
