/*
 * NGTECH suite loader: the full-screen wait in every NGTECH app.
 *
 * Identical across the suite. Copy it in unchanged and set the app's colours on :root in
 * index.html (there, so the boot splash has them before any script runs):
 *
 *   --ngtech-accent         the app's colour: the unlit mark, the top of the charge, the glow
 *   --ngtech-accent-2       optional second stop, where the current enters (defaults to accent)
 *   --ngtech-loader-bg      the page behind it    (--ngtech-loader-bg-dark for data-tone="dark")
 *   --ngtech-loader-ink     the caption           (--ngtech-loader-ink-dark)
 *
 * Add the class is-error when the wait has failed and the caption now explains it.
 *
 * The motion is the mark powering up. Current enters at the solder node at the foot of the
 * circuit trace (a pulse rings out from it), rises through the mark in the app's colour, glows
 * once at full charge and lets go. Reduced motion gets the mark fully lit, breathing instead.
 *
 * index.html renders this markup as the boot splash; SuiteLoader.tsx renders it afterwards and
 * sets --ngtech-loader-phase / --ngtech-loader-enter so the cycle carries on across the
 * hand-over instead of restarting. Keep the 2.6s cycle and 0.2s entrance in step with it.
 */

.ngtech-loader {
  --_accent: var(--ngtech-accent, #2563EB);
  --_accent-2: var(--ngtech-accent-2, var(--_accent));
  --_unlit: .16;
  --_glow: .2;
  position: fixed;
  inset: 0;
  z-index: var(--ngtech-loader-z, 1200);
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 22px;
  margin: 0;
  padding: 24px;
  background: var(--ngtech-loader-bg, #FAFAF7);
  color: var(--ngtech-loader-ink, #5C6885);
  font: 500 13px/1.5 Inter, ui-sans-serif, system-ui, -apple-system, "Segoe UI", sans-serif;
  text-align: center;
  /* Held back a beat so a warm boot that resolves in ~150ms never flashes it at all. */
  animation: ngtech-loader-in .3s ease-out var(--ngtech-loader-enter, .2s) both;
}

.ngtech-loader[data-tone="dark"] {
  --_unlit: .22;
  --_glow: .32;
  background: var(--ngtech-loader-bg-dark, #0B1220);
  color: var(--ngtech-loader-ink-dark, rgba(255, 255, 255, .6));
}

.ngtech-loader *,
.ngtech-loader *::before { box-sizing: border-box; }

/* Sized to the mark's own proportions (74.2 x 100), so the node below can be placed in %. */
.ngtech-loader__stage {
  position: relative;
  display: block;
  height: 80px;
  aspect-ratio: 742 / 1000;
}

.ngtech-loader__mark {
  position: absolute;
  inset: 0;
  display: block;
  overflow: hidden;
  /* The NGTECH mark, traced to a vector so it stays crisp at any size. */
  --_mark: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 74.2 100'%3E%3Cpath d='M25.1.6C16 2.2 7.5 8.2 3.5 16.1C.8 21.6.5 23.3.5 39.2C.4 59 0 57.9 13.4 71.6C18.6 77 23 81.4 23.2 81.4C23.4 81.5 24.8 80.2 26.3 78.6C27.8 77 29.4 75.3 29.9 74.8L30.8 73.9 24 67C15 57.9 13.5 56.1 12.1 53.2L10.9 50.7 10.9 39.2C10.9 28.1 10.9 27.5 11.8 25.1C13.6 19.9 17 16.1 21.9 13.6C26.4 11.3 28 11.2 51.8 11.2L73 11.2 73.7 6.2C74 3.5 74.2 1.1 74.1.8C73.9.1 28.9 0 25.1.6M22.8 39.4L22.8 55.9 27.2 60.8C29.8 63.7 31.8 65.5 32 65.3C32.2 65.1 32.3 60.5 32.2 54.7C32.1 49.1 32.2 44.6 32.4 44.8C32.5 44.9 35.4 48.5 38.8 52.6C42.2 56.8 45.2 60.1 45.4 60.1C45.7 60.1 47.2 58.6 48.9 56.7L51.9 53.3 51.9 43.4L51.9 33.5 57.4 33.5L63 33.5 62.8 43C62.7 56.1 64 53.8 47.6 70C40.5 77.1 33.9 83.5 32.9 84.3C29.8 86.8 28.9 89.2 29.8 92.6C31.7 100 43.2 98.6 43.2 90.9L43.2 89.1 56 76.4C69.8 62.8 70.4 62.1 71.9 58.2L72.9 55.6 73 39.3L73.2 22.9 57.6 22.9L42.1 22.9 42 31.9L41.9 41 34.8 31.9L27.7 22.9 25.2 22.9L22.8 22.9 22.8 39.4'/%3E%3C/svg%3E");
  -webkit-mask: var(--_mark) center / contain no-repeat;
          mask: var(--_mark) center / contain no-repeat;
}

/* The mark before the current reaches it. */
.ngtech-loader__mark::before {
  content: "";
  position: absolute;
  inset: 0;
  background: var(--_accent);
  opacity: var(--_unlit);
}

.ngtech-loader__charge {
  position: absolute;
  inset: 0;
  display: block;
  background: linear-gradient(to bottom, var(--_accent) 20%, var(--_accent-2));
  transform: translateY(100%);
  animation: ngtech-loader-charge 2.6s cubic-bezier(.5, 0, .3, 1) var(--ngtech-loader-phase, 0s) infinite;
}

/* The bright leading edge of the current as it climbs. */
.ngtech-loader__charge::before {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  right: 0;
  height: 3px;
  background: #fff;
  opacity: 0;
  animation: ngtech-loader-edge 2.6s linear var(--ngtech-loader-phase, 0s) infinite;
}

/* The solder node at the foot of the trace: where the current comes in. */
.ngtech-loader__node {
  position: absolute;
  left: 48.8%;
  top: 92.3%;
  width: 19.5%;
  aspect-ratio: 1;
  /* % margins resolve against the stage's width on both axes, which is what centres it. */
  margin: -9.75% 0 0 -9.75%;
  border: 1.5px solid var(--_accent-2);
  border-radius: 50%;
  opacity: 0;
  animation: ngtech-loader-node 2.6s ease-out var(--ngtech-loader-phase, 0s) infinite;
}

.ngtech-loader__glow {
  position: absolute;
  inset: -40%;
  display: block;
  border-radius: 50%;
  background: radial-gradient(closest-side, var(--_accent), transparent);
  opacity: 0;
  animation: ngtech-loader-glow 2.6s ease-in-out var(--ngtech-loader-phase, 0s) infinite;
}

.ngtech-loader__caption {
  margin: 0;
  max-width: 34ch;
  animation: ngtech-loader-in .25s ease-out both;
}

/* Only shown if the wait drags on: past this, silence starts to read as broken. Out of the
   flow, hanging below the caption, so the mark stays centred while it is still hidden. */
.ngtech-loader__hint {
  position: absolute;
  top: calc(50% + 75px);
  left: 50%;
  width: min(36ch, calc(100% - 48px));
  margin: 0;
  transform: translateX(-50%);
  font-size: 12px;
  font-weight: 400;
  opacity: .9;
  animation: ngtech-loader-in .4s ease-out 7s both;
}

.ngtech-loader__sr {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
}

/* A wait that failed (class is-error): the mark goes still and unlit while the caption says why. */
.ngtech-loader.is-error .ngtech-loader__mark,
.ngtech-loader.is-error .ngtech-loader__stage *,
.ngtech-loader.is-error .ngtech-loader__charge::before { animation: none; }

@keyframes ngtech-loader-in { from { opacity: 0; } }

@keyframes ngtech-loader-charge {
  0%, 8%    { transform: translateY(100%); opacity: 1; }
  58%, 74%  { transform: translateY(0);    opacity: 1; }
  94%, 100% { transform: translateY(0);    opacity: 0; }
}

@keyframes ngtech-loader-edge {
  0%, 10%   { opacity: 0; }
  16%, 50%  { opacity: .5; }
  60%, 100% { opacity: 0; }
}

@keyframes ngtech-loader-node {
  0%        { transform: scale(1);   opacity: 0; }
  3%        { transform: scale(1);   opacity: .75; }
  34%, 100% { transform: scale(3.2); opacity: 0; }
}

@keyframes ngtech-loader-glow {
  0%, 50%   { transform: scale(.85); opacity: 0; }
  66%       { transform: scale(1);   opacity: var(--_glow); }
  92%, 100% { transform: scale(1.05); opacity: 0; }
}

@keyframes ngtech-loader-breathe { 50% { opacity: .55; } }

@media (prefers-reduced-motion: reduce) {
  .ngtech-loader__charge { transform: none; animation: none; }
  .ngtech-loader__charge::before,
  .ngtech-loader__node,
  .ngtech-loader__glow { display: none; }
  .ngtech-loader__mark { animation: ngtech-loader-breathe 2.4s ease-in-out infinite; }
}
