/* Raven loading splash — full-screen overlay shown on the first page load of a session.
   See raven-loader.js for the show/hide/replay logic.

   The raven is a flat CSS colour revealed through an alpha mask generated from the original
   artwork, so it recolours per theme with no filter tricks and no baked-in background. */

/* The raven is painted as a CSS colour through an alpha mask, so nothing about the source
   artwork's baked-in background survives into the page and every colour is a token here.

   light: the full silhouette in near-black, with the eye line-work punched back to the
          ground colour, which is how the original artwork reads.
   dark:  outline only, in the same grey the navbar text uses (DaisyUI's --nc), over black.
          The fill stays empty so the raven reads as line art rather than a negative. */
:root,
[data-theme="light"] {
  --loader-bg: #ffffff;
  --loader-raven-ink: #101012;
  --loader-eye-ink: var(--loader-bg);
  /* Six columns by three rows. These percentages select each 480px cell after the complete
     eye sequence was packed into one paint-ready sprite. */
  --loader-eye-sheet-size: 600% 300%;
  --loader-eye-col-0: 0%;
  --loader-eye-col-1: 20%;
  --loader-eye-col-2: 40%;
  --loader-eye-col-3: 60%;
  --loader-eye-col-4: 80%;
  --loader-eye-col-5: 100%;
  --loader-eye-row-0: 0%;
  --loader-eye-row-1: 50%;
  --loader-eye-row-2: 100%;
  /* The wordmark and the dismiss hint track the raven, so the splash reads as one colour. */
  --loader-ink: var(--loader-raven-ink);
}

[data-theme="dark"] {
  --loader-bg: #000000;
  /* Warm off-white at ~74% alpha. To lock this to the navbar text colour instead, use
     oklch(var(--nc, 74.6477% 0.0216 264.435964)) — DaisyUI's --nc, which is #a6adbb in this
     theme; the inline fallback matters because an undefined --nc is invalid at
     computed-value time and would drop the background to unset rather than to a previous
     declaration. Note the alpha here multiplies with the mask's own anti-aliasing, so the
     outline edges soften slightly; an opaque #aba9a9 renders the same colour over black
     with maximally crisp edges. */
  --loader-raven-ink: #e8e6e6bc;
  --loader-eye-ink: var(--loader-raven-ink);
  --loader-ink: var(--loader-raven-ink);
  /* raven-outline.png is a higher-resolution redraw that frames the raven larger than
     raven-silhouette.png does, so the eye frames — authored against the silhouette's
     framing — have to be rescaled onto it. Derived by matching the two artworks' content
     bounding boxes: scale 1.21200 x 1.15709, offset -0.10680 -0.11104, converted to the
     background-position percentage form p = offset / (1 - scale). The values below also
     account for the sprite grid. Recompute them if either artwork is ever recropped. */
  --loader-eye-sheet-size: 727.200% 347.127%;
  --loader-eye-col-0: 1.702794%;
  --loader-eye-col-1: 21.026774%;
  --loader-eye-col-2: 40.350753%;
  --loader-eye-col-3: 59.674733%;
  --loader-eye-col-4: 78.998712%;
  --loader-eye-col-5: 98.322692%;
  --loader-eye-row-0: 4.493707%;
  --loader-eye-row-1: 51.315382%;
  --loader-eye-row-2: 98.137056%;
}

:root {
  --loader-size: min(42vw, 360px);
  /* One cycle of the whole loop. The art fade, the wordmark draw and the eye-frame playback
     are all driven off this, so they cannot drift apart. */
  --loader-cycle: 3600ms;
}

.loading-screen {
  position: fixed;
  inset: 0;
  display: grid;
  place-items: center;
  background: var(--loader-bg);
  z-index: 9999;
  transition: opacity 180ms ease, visibility 180ms ease;
}

.loading-screen.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* Skip the fade when the overlay was never shown (repeat loads in the same session). */
.loading-screen.is-instant {
  transition: none;
}

.raven-loader {
  position: relative;
  width: var(--loader-size);
  display: grid;
  justify-items: center;
  align-content: start;
  row-gap: 10px;
  user-select: none;
}

.raven-loader__art {
  position: relative;
  width: 100%;
  aspect-ratio: 1;
  opacity: 1;
}

.raven-loader.is-playing .raven-loader__art {
  animation: raven-art-fade var(--loader-cycle) ease-in-out infinite;
}

/* Both layers are a flat colour revealed through a mask: the silhouette/outline for the
   raven, and a per-frame eye mask that raven-loader.js swaps. */
.raven-loader__base,
.raven-loader__eyes {
  position: absolute;
  inset: 0;
  pointer-events: none;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

/* The mask URL is a plain declaration rather than a custom property: a relative url() in a
   declaration always resolves against this stylesheet, whereas engines have disagreed about
   relative url() inside a custom property. Staying relative also means these keep working
   if Flask's static_url_path ever changes. */
.raven-loader__base {
  background-color: var(--loader-raven-ink);
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  -webkit-mask-image: url("raven-silhouette.png");
  mask-image: url("raven-silhouette.png");
}

[data-theme="dark"] .raven-loader__base {
  -webkit-mask-image: url("raven-outline.png");
  mask-image: url("raven-outline.png");
}

/* Every eye frame is in one sprite, so animation changes only the crop position of an image
   that is already decoded. Frame 00 is empty and is the stable pre-animation state. */
.raven-loader__eyes {
  background-color: var(--loader-eye-ink);
  -webkit-mask-image: url("raven-eyes-sprite.png");
  mask-image: url("raven-eyes-sprite.png");
  -webkit-mask-position: var(--loader-eye-col-0) var(--loader-eye-row-0);
  mask-position: var(--loader-eye-col-0) var(--loader-eye-row-0);
  -webkit-mask-size: var(--loader-eye-sheet-size);
  mask-size: var(--loader-eye-sheet-size);
}

.raven-loader.is-playing .raven-loader__eyes {
  animation: raven-eyes-open var(--loader-cycle) steps(1, end) infinite;
}

.raven-loader.eyes-unavailable .raven-loader__eyes {
  -webkit-mask-image: none;
  mask-image: none;
}

@keyframes raven-eyes-open {
  0% {
    -webkit-mask-position: var(--loader-eye-col-0) var(--loader-eye-row-0);
    mask-position: var(--loader-eye-col-0) var(--loader-eye-row-0);
  }
  2.583333% {
    -webkit-mask-position: var(--loader-eye-col-1) var(--loader-eye-row-0);
    mask-position: var(--loader-eye-col-1) var(--loader-eye-row-0);
  }
  5.166667% {
    -webkit-mask-position: var(--loader-eye-col-2) var(--loader-eye-row-0);
    mask-position: var(--loader-eye-col-2) var(--loader-eye-row-0);
  }
  7.75% {
    -webkit-mask-position: var(--loader-eye-col-3) var(--loader-eye-row-0);
    mask-position: var(--loader-eye-col-3) var(--loader-eye-row-0);
  }
  10.333333% {
    -webkit-mask-position: var(--loader-eye-col-4) var(--loader-eye-row-0);
    mask-position: var(--loader-eye-col-4) var(--loader-eye-row-0);
  }
  12.916667% {
    -webkit-mask-position: var(--loader-eye-col-5) var(--loader-eye-row-0);
    mask-position: var(--loader-eye-col-5) var(--loader-eye-row-0);
  }
  15.5% {
    -webkit-mask-position: var(--loader-eye-col-0) var(--loader-eye-row-1);
    mask-position: var(--loader-eye-col-0) var(--loader-eye-row-1);
  }
  18.083333% {
    -webkit-mask-position: var(--loader-eye-col-1) var(--loader-eye-row-1);
    mask-position: var(--loader-eye-col-1) var(--loader-eye-row-1);
  }
  20.666667% {
    -webkit-mask-position: var(--loader-eye-col-2) var(--loader-eye-row-1);
    mask-position: var(--loader-eye-col-2) var(--loader-eye-row-1);
  }
  23.25% {
    -webkit-mask-position: var(--loader-eye-col-3) var(--loader-eye-row-1);
    mask-position: var(--loader-eye-col-3) var(--loader-eye-row-1);
  }
  25.833333% {
    -webkit-mask-position: var(--loader-eye-col-4) var(--loader-eye-row-1);
    mask-position: var(--loader-eye-col-4) var(--loader-eye-row-1);
  }
  28.416667% {
    -webkit-mask-position: var(--loader-eye-col-5) var(--loader-eye-row-1);
    mask-position: var(--loader-eye-col-5) var(--loader-eye-row-1);
  }
  31% {
    -webkit-mask-position: var(--loader-eye-col-0) var(--loader-eye-row-2);
    mask-position: var(--loader-eye-col-0) var(--loader-eye-row-2);
  }
  33.583333% {
    -webkit-mask-position: var(--loader-eye-col-1) var(--loader-eye-row-2);
    mask-position: var(--loader-eye-col-1) var(--loader-eye-row-2);
  }
  36.166667% {
    -webkit-mask-position: var(--loader-eye-col-2) var(--loader-eye-row-2);
    mask-position: var(--loader-eye-col-2) var(--loader-eye-row-2);
  }
  38.75% {
    -webkit-mask-position: var(--loader-eye-col-3) var(--loader-eye-row-2);
    mask-position: var(--loader-eye-col-3) var(--loader-eye-row-2);
  }
  41.333333% {
    -webkit-mask-position: var(--loader-eye-col-4) var(--loader-eye-row-2);
    mask-position: var(--loader-eye-col-4) var(--loader-eye-row-2);
  }
  43.916667%, 100% {
    -webkit-mask-position: var(--loader-eye-col-5) var(--loader-eye-row-2);
    mask-position: var(--loader-eye-col-5) var(--loader-eye-row-2);
  }
}

@keyframes raven-art-fade {
  0%, 84% {
    opacity: 1;
  }
  100% {
    opacity: 0;
  }
}

.raven-loader__wordmark {
  width: min(74%, 260px);
  height: 42px;
  overflow: visible;
}

.raven-loader__wordmark text {
  font-family: "Avenir Next", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  font-weight: 700;
  font-size: 30px;
  letter-spacing: 0.26em;
  text-transform: uppercase;
  fill: var(--loader-ink);
  stroke: var(--loader-ink);
  stroke-width: 1.2;
  stroke-linecap: round;
  stroke-linejoin: round;
  paint-order: stroke fill;
  fill-opacity: 0;
  stroke-dasharray: 420;
  stroke-dashoffset: 420;
  opacity: 0;
}

.raven-loader.is-playing .raven-loader__wordmark text {
  animation: raven-wordmark var(--loader-cycle) ease-in-out infinite;
}

@keyframes raven-wordmark {
  0%, 46% {
    fill-opacity: 0;
    stroke-dashoffset: 420;
    opacity: 0;
  }
  50% {
    opacity: 1;
    fill-opacity: 0;
    stroke-dashoffset: 420;
  }
  68% {
    opacity: 1;
    fill-opacity: 0;
    stroke-dashoffset: 0;
  }
  68%, 84% {
    opacity: 1;
    fill-opacity: 1;
    stroke-dashoffset: 0;
  }
  100% {
    opacity: 0;
    fill-opacity: 1;
    stroke-dashoffset: 0;
  }
}

/* Pins the loop faded out with the animation off. Applied at the moment we hide: without
   it the infinite animation wraps back to opacity 1 behind the 180ms overlay fade, which is
   what makes the final raven frame flash back on screen. */
.raven-loader.is-frozen .raven-loader__art,
.raven-loader.is-frozen .raven-loader__wordmark text {
  animation: none;
  opacity: 0;
}

/* The wordmark doubles as a replay button. */
.raven-loader__replay {
  display: grid;
  place-items: center;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  font: inherit;
  cursor: pointer;
}

.raven-loader__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Only shown once the splash is being held open by an on-demand replay, so the user always
   has a way out. */
.raven-loader__hint {
  position: absolute;
  left: 50%;
  bottom: 8%;
  transform: translateX(-50%);
  margin: 0;
  font-family: "Avenir Next", "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  font-size: 12px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--loader-ink);
  opacity: 0;
  transition: opacity 200ms ease;
  pointer-events: none;
}

.loading-screen.is-sticky .raven-loader__hint {
  opacity: 0.5;
}

@media (prefers-reduced-motion: reduce) {
  .raven-loader.is-playing .raven-loader__art {
    animation: none;
    opacity: 1;
  }

  .raven-loader.is-playing .raven-loader__eyes {
    animation: none;
    -webkit-mask-position: var(--loader-eye-col-5) var(--loader-eye-row-2);
    mask-position: var(--loader-eye-col-5) var(--loader-eye-row-2);
  }

  .raven-loader.is-playing .raven-loader__wordmark text {
    animation: none;
    opacity: 1;
    fill-opacity: 1;
    stroke-dashoffset: 0;
  }
}
