/*
 * GrahaGuru standard page loader.
 *
 * One overlay for the whole site: the Inlaid Ga squared mark flies in part by part and
 * assembles into its real brand colours, under a "loading" word in Deep Aubergine.
 *
 * Authority:
 *   Geometry, easing, overlay colour and the bottom progress line are the Cleo template's
 *   own `.page-loader` (landing-new/styles.css) - background #f5f3ff, ease
 *   cubic-bezier(.16,1,.3,1), 0.7s reveal, 3px gradient line, 1.8s orbit breathing.
 *   Logo colours are the brand masters (landing-new/assets/grahaguru/brand/README.md):
 *   Deep Aubergine #2D174C, Mineral Inlay #8FAFB0, Mauve Haze #C2B0C8, with Lake Mist
 *   #9DBFC4, Deep Lake #779198, Night Slate #151B22 and Deep Plum #25103F used only as
 *   in-flight stops. The word is never white.
 *   The word is set in the platform's own monospace face. The template's Simplon would be
 *   a 17 KB download on every first visit for a seven-character label nobody will identify,
 *   so it is deliberately not loaded here.
 *
 * The overlay only ever exists when js/gg-loader.js built it, so a crawler or a
 * scripting-off reader never sees it.
 */

#gg-loader {
  --gg-l-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --gg-l-field: #f5f3ff;
  --gg-l-aubergine: #2d174c;
  position: fixed;
  /*
   * Measured, not guessed. The published site paints above Cleo's own `.page-loader` (999)
   * in several places: the Vedika demo button at 9998 on every homepage, modals at
   * 9999-20000, the inline consent banner at 999999 on the six localized homepages that do
   * NOT load js/cookie-consent.js, and the auth overlay at 100000. Anything lower and one
   * of them punches through the loading screen. The outage notices at 2147483647 stay above
   * this deliberately - an outage must always be readable.
   */
  z-index: 2147483646;
  inset: 0;
  display: grid;
  place-items: center;
  align-content: center;
  gap: 26px;
  margin: 0;
  padding: 0;
  background: var(--gg-l-field);
  /*
   * The overlay has no interactive content, so it must never swallow a click. Without this
   * a full-viewport fixed element eats every tap for the whole HIDE_AFTER_MS window.
   */
  pointer-events: none;
  /*
   * Opaque from the frame it first exists, and it never animates on the way IN.
   *
   * Measured, not assumed. The `transition` used to sit here on the base rule, so it governed
   * the show direction as well as the hide, and a `gg-l-field-in 0.16s` fade sat on top of it.
   * `show()` stepped the overlay through `hidden` first, so the field then needed ~523 ms to
   * reach 0.99 - while `window load`, which hides it, lands at 91-716 ms on real pages.
   * Measured peak opacity on a real initial paint was 0.50 on `/` and 0.00 on
   * /zodiac/aries.html and /privacy-policy.html: a half-visible purple wash with the page
   * readable straight through it, or nothing at all.
   *
   * The Cleo template's own `.page-loader` (landing-new/styles.css) is the authority and does
   * the opposite: `opacity: 1; visibility: visible` from first paint, with the transition
   * spent only on the `is-done` fade-OUT. So the transition now lives on the hidden state
   * alone, where it can only ever run on the way out.
   */
  opacity: 1;
  visibility: visible;
}

#gg-loader[data-gg-state="hidden"] {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition:
    opacity 0.7s var(--gg-l-ease),
    visibility 0.7s;
}

/* ---- the mark ------------------------------------------------------------ */

#gg-loader .gg-l-mark {
  display: block;
  width: clamp(120px, 22vmin, 200px);
  height: clamp(120px, 22vmin, 200px);
  overflow: visible;
  animation: gg-l-orbit 1.8s ease-in-out 1.05s infinite;
}

@keyframes gg-l-orbit {
  50% {
    transform: scale(1.045);
  }
}

/*
 * Every part rests at its final brand fill with no transform, so `animation: none`
 * (reduced motion, or an unsupported engine) already shows the settled logo.
 */
#gg-loader .gg-l-p {
  animation-duration: 1.05s;
  animation-timing-function: var(--gg-l-ease);
  animation-fill-mode: forwards;
}

#gg-loader #ggLoaderInlay {
  animation-name: gg-l-inlay;
}

#gg-loader #ggLoaderShirorekha {
  animation-name: gg-l-shirorekha;
  animation-delay: 0.04s;
}

#gg-loader #ggLoaderDanda {
  animation-name: gg-l-danda;
  animation-delay: 0.1s;
}

#gg-loader #ggLoaderHook {
  animation-name: gg-l-hook;
  animation-delay: 0.16s;
}

#gg-loader #ggLoaderSuperscript {
  animation-name: gg-l-superscript;
  animation-delay: 0.22s;
}

@keyframes gg-l-inlay {
  0% {
    opacity: 0;
    fill: #c2b0c8;
    transform: translate(-260px, -180px);
  }
  40% {
    opacity: 1;
    fill: #9dbfc4;
    transform: translate(60px, -40px);
  }
  72% {
    fill: #779198;
    transform: translate(-18px, 12px);
  }
  100% {
    opacity: 1;
    fill: #8fafb0;
    transform: translate(0, 0);
  }
}

@keyframes gg-l-shirorekha {
  0% {
    opacity: 0;
    fill: #9dbfc4;
    transform: translate(0, -420px);
  }
  42% {
    opacity: 1;
    fill: #779198;
    transform: translate(0, 64px);
  }
  74% {
    fill: #25103f;
    transform: translate(0, -16px);
  }
  100% {
    opacity: 1;
    fill: #2d174c;
    transform: translate(0, 0);
  }
}

@keyframes gg-l-danda {
  0% {
    opacity: 0;
    fill: #c2b0c8;
    transform: translate(380px, 210px);
  }
  44% {
    opacity: 1;
    fill: #8fafb0;
    transform: translate(-70px, -40px);
  }
  76% {
    fill: #25103f;
    transform: translate(14px, 10px);
  }
  100% {
    opacity: 1;
    fill: #2d174c;
    transform: translate(0, 0);
  }
}

@keyframes gg-l-hook {
  0% {
    opacity: 0;
    fill: #779198;
    transform: translate(-400px, 240px);
  }
  46% {
    opacity: 1;
    fill: #c2b0c8;
    transform: translate(74px, -48px);
  }
  78% {
    fill: #151b22;
    transform: translate(-12px, 14px);
  }
  100% {
    opacity: 1;
    fill: #2d174c;
    transform: translate(0, 0);
  }
}

@keyframes gg-l-superscript {
  0% {
    opacity: 0;
    fill: #2d174c;
    transform: translate(150px, -300px);
  }
  48% {
    opacity: 1;
    fill: #8fafb0;
    transform: translate(-30px, 52px);
  }
  80% {
    fill: #9dbfc4;
    transform: translate(8px, -10px);
  }
  100% {
    opacity: 1;
    fill: #c2b0c8;
    transform: translate(0, 0);
  }
}

/* ---- the word ------------------------------------------------------------ */

#gg-loader .gg-l-word {
  display: flex;
  gap: 0.06em;
  margin: 0;
  padding: 0 8vw;
  color: var(--gg-l-aubergine);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 13px;
  line-height: 1.45;
  text-align: center;
}

/*
 * Letter-spacing and lowercasing are only correct for the Latin label. Applied to an Indic
 * string they break the shaping of a cluster, so js/gg-loader.js renders those as one word
 * without this class.
 */
#gg-loader .gg-l-word--latin {
  letter-spacing: 0.34em;
  text-indent: 0.34em;
  text-transform: lowercase;
}

#gg-loader .gg-l-word span {
  animation: gg-l-letter 1.2s ease-in-out infinite alternate;
}

@keyframes gg-l-letter {
  0% {
    opacity: 0.5;
    filter: blur(1.1px);
  }
  100% {
    opacity: 1;
    filter: blur(0);
  }
}

/* ---- the Cleo progress line ---------------------------------------------- */

#gg-loader .gg-l-line {
  position: absolute;
  right: 0;
  bottom: 0;
  left: 0;
  height: 3px;
  transform-origin: left;
  background: linear-gradient(90deg, #b29ce0, #7c3aed, #d946ef);
  animation: gg-l-line 1.25s var(--gg-l-ease) forwards;
}

@keyframes gg-l-line {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

/* ---- quiet modes --------------------------------------------------------- */

/*
 * Every selector below repeats the exact shape of the rule that switched the animation on.
 * `#gg-loader .gg-l-p` would read as if it covered the five parts, but `#gg-loader
 * #ggLoaderInlay` carries two IDs and wins on specificity whatever the source order, so a
 * class-shaped reset here silently leaves the corpus pages animating. Measured, not assumed:
 * scripts/tests/gg-loader.test.mjs asserts the parity, and the evidence run reads the
 * computed `animation-name` back out of a real reduced-motion browser.
 */
@media (prefers-reduced-motion: reduce) {
  #gg-loader,
  #gg-loader .gg-l-mark,
  #gg-loader .gg-l-p,
  #gg-loader #ggLoaderInlay,
  #gg-loader #ggLoaderShirorekha,
  #gg-loader #ggLoaderDanda,
  #gg-loader #ggLoaderHook,
  #gg-loader #ggLoaderSuperscript,
  #gg-loader .gg-l-word span,
  #gg-loader .gg-l-line {
    animation: none;
  }

  /*
   * The transition now lives on the hidden state, which carries an attribute as well as the
   * ID, so a bare `#gg-loader` reset here would lose to it and the fade-out would survive
   * reduced motion. Repeat the exact shape of the rule being switched off.
   */
  #gg-loader,
  #gg-loader[data-gg-state="hidden"] {
    transition: none;
  }

  #gg-loader .gg-l-line {
    transform: scaleX(1);
  }
}

@media print {
  #gg-loader {
    display: none;
  }
}
