/* Bell loader. One stylesheet for the website, sign-in, request, admin and portal.
   Bell's drawing lives once, in /loader/bell-run.svg (traced from bell-full-big.png), and every
   copy points at it with <use>. Her three legs swing at the hip inside the SVG. Everything else
   (the bob, the dashed ground, the motion lines, the fade-in) moves HTML boxes, so it stays on
   the compositor even where a browser runs SVG transforms on the main thread.
   CSP: no inline styles or scripts are needed, so it works under style-src 'self'.
   Still state: the base rules below ARE the still frame (Bell exactly as drawn, hooves on the
   line, three motion lines resting behind her). Motion is declared only for
   prefers-reduced-motion: no-preference, so neither a reduced-motion setting nor the site-wide
   0.01ms rule can leave her stuck mid-stride.
   Switches on the root: is-running (moves; /loader/bell-loader.js adds it once the drawing has
   loaded, so page and inline markup ship with data-bl-start instead),
   bell-loader--sm (40px inline), bell-loader--night, bell-loader--flip (faces left),
   bell-loader--screen (fills the viewport), bell-loader--bare (an existing Bell, no stage). */

.bell-loader {
  --bl-w: 160px;                          /* Bell's width; every size below is a multiple of it */
  --bl-t: 640ms;                          /* one stride */
  --bl-run-delay: 0s;
  --bl-x: calc(var(--bl-w) * .3);         /* Bell's left edge inside the stage */
  --bl-y: calc(var(--bl-w) * .0471);      /* Bell's top edge inside the stage (headroom for the bob) */
  --bl-g: calc(var(--bl-w) * .03);        /* ground line, measured up from the stage bottom */
  --bl-ground: #D9D1C3;                   /* --line */
  --bl-lines: #17202A;                    /* --ink */
  --bl-lines-a: .35;                      /* the board's motion-line strength */
  --bl-text: #17202A;
  --bl-bg: #F6F3EC;                       /* only used by --screen */
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: calc(var(--bl-w) * .09);
  color: var(--bl-text);
  animation: bl-in 400ms cubic-bezier(.16, 1, .3, 1) 300ms both;   /* fast loads never flash */
}
.bell-loader--night {
  --bl-ground: rgba(239, 233, 221, .28);
  --bl-lines: #EFE9DD;
  --bl-lines-a: .42;
  --bl-text: #EFE9DD;
  --bl-bg: #0F1820;
}
.bell-loader--screen {
  position: fixed;
  inset: 0;
  z-index: 1;
  justify-content: center;
  padding: 16px;
  background: var(--bl-bg);
}

.bl-stage {
  position: relative;
  display: block;
  flex: none;
  width: calc(var(--bl-w) * 1.3);
  height: calc(var(--bl-w) * .8);
  overflow: hidden;
  pointer-events: none;
}
.bell-loader--flip .bl-stage { transform: scaleX(-1); }

/* Bell. The sprite's bottom edge is her lowest hoof, so bottom: --bl-g puts her on the line. */
.bl-bell {
  position: absolute;
  left: var(--bl-x);
  bottom: var(--bl-g);
  z-index: 2;
  display: block;
  width: var(--bl-w);
  aspect-ratio: 1133 / 819;
  transform-origin: 50% 83.76%;
}
.bl-rig { display: block; width: 100%; height: 100%; overflow: visible; }
.bl-leg { transform-box: view-box; }
.bl-leg-b { transform-origin: 318px 559px; }
.bl-leg-m { transform-origin: 417px 609px; }
.bl-leg-f { transform-origin: 779px 578px; }

/* Ground: a dashed line longer than the stage by exactly three dash periods. */
.bl-ground {
  position: absolute;
  left: 0;
  bottom: calc(var(--bl-g) - var(--bl-w) * .01765);
  z-index: 1;
  display: block;
  width: calc(var(--bl-w) * 1.6011);
  height: calc(var(--bl-w) * .0353);
  overflow: visible;
}
.bl-ground path { fill: none; stroke: var(--bl-ground); stroke-width: 20; stroke-linecap: round; stroke-dasharray: 55 55; }
.bell-loader--sm .bl-ground path { stroke-width: 44; stroke-dasharray: 100 230; }

/* Motion lines behind her tail. */
.bl-lines { position: absolute; inset: 0; z-index: 1; opacity: var(--bl-lines-a); }
.bl-lines i {
  position: absolute;
  height: max(1.5px, calc(var(--bl-w) * .0194));
  border-radius: 999px;
  background: var(--bl-lines);
}
.bl-lines i:nth-child(1) { left: calc(var(--bl-x) - var(--bl-w) * .241); top: calc(var(--bl-y) + var(--bl-w) * .2692); width: calc(var(--bl-w) * .1853); }
.bl-lines i:nth-child(2) { left: calc(var(--bl-x) - var(--bl-w) * .2586); top: calc(var(--bl-y) + var(--bl-w) * .4193); width: calc(var(--bl-w) * .2295); }
.bl-lines i:nth-child(3) { left: calc(var(--bl-x) - var(--bl-w) * .241); top: calc(var(--bl-y) + var(--bl-w) * .5693); width: calc(var(--bl-w) * .1677); }

.bl-text {
  display: block;
  max-width: 30ch;
  margin: 0;
  font-family: var(--bl-font, 'Newsreader', Georgia, 'Times New Roman', serif);
  font-size: var(--bl-size, 20px);
  line-height: 1.3;
  letter-spacing: -.01em;
  text-align: center;
  text-wrap: balance;
}

/* Small, inline beside a line of text (chat replies). The words carry the status. */
.bell-loader--sm {
  --bl-w: 40px;
  display: inline-flex;
  flex-direction: row;
  align-items: center;
  gap: 10px;
  color: inherit;
  vertical-align: middle;
}
.bell-loader--sm .bl-lines i:nth-child(2) { display: none; }
.bell-loader--sm .bl-text { max-width: none; font: inherit; letter-spacing: inherit; text-align: left; }

/* Bare: a Bell that is already on the page (the sign-in and request night panels).
   No stage and no fade; the host's own class sizes her. At rest she is the drawing; add
   is-running (BellLoader.run) and she trots in place with motion lines behind her. */
.bell-loader--bare {
  position: relative;
  display: block;
  container-type: inline-size;
  animation: none;
  --bl-run-delay: 300ms;
}
.bell-loader--bare > * { --bl-w: 100cqw; --bl-x: 0px; --bl-y: 0px; }
.bell-loader--bare .bl-bell { position: relative; left: auto; bottom: auto; width: 100%; }
/* Her lines fade in after the run delay with an animation, not a transition: a transition never
   starts when is-running lands before the lines' first style pass (a fast first load), so they
   would pop on at once. When she stops they go with her strides, instead of jumping back to rest. */
.bell-loader--bare .bl-lines { inset: 0 auto auto 0; width: 100%; height: 100%; opacity: 0; }
.bell-loader--bare.is-running .bl-lines { opacity: var(--bl-lines-a); animation: bl-in 300ms cubic-bezier(.16, 1, .3, 1) var(--bl-run-delay) both; }

@keyframes bl-in { from { opacity: 0; } }

@media (prefers-reduced-motion: reduce) {
  .bell-loader { animation-duration: 1ms; }   /* keeps the 300ms wait, drops the fade */
  .bell-loader--bare { animation: none; }
  .bell-loader--bare .bl-lines { display: none; }   /* an existing Bell just stays as drawn; the page's own words say it's waiting */
}

/* ---------- The stride (640ms) ----------
   0-50%   spread: legs reach out, body lifts and tips nose-up, hooves leave the line
   50-100% gather: legs pass under her, hooves skim the line (the small lift at 75% keeps the
           angled back and front hooves from dipping below it)
   Every loop starts and ends on her drawn pose. Sine ease-out to each extreme, ease-in back:
   no overshoot, no bounce. */
@media (prefers-reduced-motion: no-preference) {
  .bell-loader.is-running .bl-bell   { animation: bl-bob    var(--bl-t) var(--bl-run-delay) infinite; }
  .bell-loader.is-running .bl-leg-b  { animation: bl-leg-b  var(--bl-t) var(--bl-run-delay) infinite; }
  .bell-loader.is-running .bl-leg-m  { animation: bl-leg-m  var(--bl-t) var(--bl-run-delay) infinite; }
  .bell-loader.is-running .bl-leg-f  { animation: bl-leg-f  var(--bl-t) var(--bl-run-delay) infinite; }
  .bell-loader.is-running .bl-ground { animation: bl-ground var(--bl-t) linear var(--bl-run-delay) infinite; }
  .bell-loader.is-running .bl-lines i { animation: bl-streak var(--bl-t) linear infinite; }
  .bell-loader.is-running .bl-lines i:nth-child(2) { animation-delay: calc(var(--bl-t) * -.667); }
  .bell-loader.is-running .bl-lines i:nth-child(3) { animation-delay: calc(var(--bl-t) * -.333); }
}

@keyframes bl-bob {
  0%, 50%, 100% { transform: none; animation-timing-function: cubic-bezier(.61, 1, .88, 1); }
  25% { transform: translateY(-3.66%) rotate(-1.5deg); animation-timing-function: cubic-bezier(.12, 0, .39, 0); }
  75% { transform: translateY(-.49%); animation-timing-function: cubic-bezier(.12, 0, .39, 0); }
}
@keyframes bl-leg-b {
  0%, 50%, 100% { transform: rotate(0deg); animation-timing-function: cubic-bezier(.61, 1, .88, 1); }
  25% { transform: rotate(13deg); animation-timing-function: cubic-bezier(.12, 0, .39, 0); }
  75% { transform: rotate(-13deg); animation-timing-function: cubic-bezier(.12, 0, .39, 0); }
}
/* The far hind leg trails the near one: same crossings, later peaks. */
@keyframes bl-leg-m {
  0%, 50%, 100% { transform: rotate(0deg); animation-timing-function: cubic-bezier(.61, 1, .88, 1); }
  30% { transform: rotate(11deg); animation-timing-function: cubic-bezier(.12, 0, .39, 0); }
  80% { transform: rotate(-11deg); animation-timing-function: cubic-bezier(.12, 0, .39, 0); }
}
@keyframes bl-leg-f {
  0%, 50%, 100% { transform: rotate(0deg); animation-timing-function: cubic-bezier(.61, 1, .88, 1); }
  25% { transform: rotate(-14deg); animation-timing-function: cubic-bezier(.12, 0, .39, 0); }
  75% { transform: rotate(14deg); animation-timing-function: cubic-bezier(.12, 0, .39, 0); }
}
/* Three dash periods per stride (18.191% of the ground's own width), so it loops without a seam. */
@keyframes bl-ground { to { transform: translateX(-18.191%); } }
@keyframes bl-streak {
  0% { transform: translateX(40%); opacity: 0; }
  30% { opacity: 1; }
  100% { transform: translateX(-65%); opacity: 0; }
}
