/* ADOR cinematic loader.
 *
 * Hand-written, and linked in <head> apart from the compiled Tailwind output,
 * so the loader does not depend on that build.
 *
 * Nothing in this file applies until `loader.js` — synchronous in <head>, so
 * before anything paints — has written this load's mode onto <html>:
 * `ador-loader-full`, `-short` (later pages in the same session), `-reduced`
 * or `-skip`, plus `ador-loading` while the sequence holds the page. A browser
 * that never runs the script gets the site untouched, not a stuck overlay.
 *
 * Built for the compositor: everything that moves is transform or opacity (one
 * short blur aside), static texture is painted once into the stage, and each
 * animated layer is kept as small as its job allows.
 */

/* ------------------------------------------------------------------ *
 * Page state
 * ------------------------------------------------------------------ */

/* The page holds still while the sequence plays. `loader.js` lifts this as the
   wipe starts rather than when it ends, so the reflow that follows (on a
   desktop with classic scrollbars the page gains its scrollbar) happens behind
   the panel. It also stops the browser restoring a scroll position on reload:
   the page comes back at the top, which is what an intro wants. */
html.ador-loading,
html.ador-loading body {
  overflow: hidden;
}

/* The site is underneath — laid out and loading, but not painted and not
   reachable: nothing to click, tab to or have read out before the reveal, and
   nothing but the loader for the renderer to draw while it plays. */
html.ador-loading body > :not(.ador-loader) {
  visibility: hidden;
}

/* ------------------------------------------------------------------ *
 * Overlay shell
 * ------------------------------------------------------------------ */

.ador-loader {
  display: none;
  position: fixed;
  inset: 0;
  /* Above everything the site uses (its highest is z-50), with headroom left
     under the 32-bit maximum for anything a browser extension injects. */
  z-index: 2147483000;
  overflow: hidden;
  /* Swallows clicks, taps and scroll gestures aimed at the page underneath. */
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
  transition: opacity 240ms linear;
}

html.ador-loader-full .ador-loader,
html.ador-loader-short .ador-loader,
html.ador-loader-reduced .ador-loader {
  display: block;
}

/* Once the sequence has ended the overlay never shows again — even if a safety
   net ended it before this element had been parsed. */
html.ador-loaded .ador-loader {
  display: none;
}

.ador-loader.is-fading {
  opacity: 0;
  pointer-events: none;
}

/* ------------------------------------------------------------------ *
 * Scene 1 — dark industrial ground
 * ------------------------------------------------------------------ */

/* The ground and every static texture on it, as one background: painted once,
   no extra layers. Front to back — vignette, faint horizontal rules, brushed
   grain, and the graphite itself. */
.ador-loader__stage {
  position: absolute;
  inset: 0;
  background:
    radial-gradient(
      124% 96% at 50% 48%,
      rgba(0, 0, 0, 0) 38%,
      rgba(0, 0, 0, 0.45) 78%,
      rgba(0, 0, 0, 0.72) 100%
    ),
    repeating-linear-gradient(
      180deg,
      rgba(0, 0, 0, 0) 0 3px,
      rgba(255, 255, 255, 0.009) 3px 4px
    ),
    repeating-linear-gradient(
      101deg,
      rgba(255, 255, 255, 0.012) 0 1px,
      rgba(0, 0, 0, 0.02) 1px 2px,
      rgba(0, 0, 0, 0) 2px 5px
    ),
    radial-gradient(118% 82% at 50% 44%, #17191d 0%, #0d0f11 46%, #070809 100%);
}

/* Switched off the instant the panel has the screen, so the wipe uncovers the
   site rather than the stage. No transition: it happens behind the panel. */
.ador-loader.is-wiping .ador-loader__stage {
  opacity: 0;
}

/* The one light in the room: very large, very slow, barely there. Drawn at half
   size and scaled up — a soft gradient loses nothing to that, and the layer
   costs a quarter of the pixels. */
.ador-loader__ambient {
  position: absolute;
  left: 50%;
  top: 46%;
  width: 64vmax;
  height: 42vmax;
  background: radial-gradient(
    closest-side,
    rgba(196, 208, 222, 0.14),
    rgba(150, 164, 180, 0.055) 45%,
    rgba(0, 0, 0, 0) 72%
  );
  transform: translate3d(-50%, -46%, 0) scale(1.84);
  opacity: 0.35;
  will-change: transform, opacity;
  animation: ador-ambient 9s cubic-bezier(0.4, 0, 0.2, 1) both;
}

/* ------------------------------------------------------------------ *
 * Scene 5 — dust
 *
 * Two elements, not two dozen: each layer is a single dot whose box-shadow list
 * draws the rest, so the whole field costs two composited transforms.
 * ------------------------------------------------------------------ */

.ador-loader__dust {
  position: absolute;
  left: 50%;
  top: 50%;
  border-radius: 50%;
  background: rgba(0, 0, 0, 0);
  opacity: 0;
}

.ador-loader__dust--far {
  width: 1px;
  height: 1px;
  box-shadow:
    -42vmin -26vmin 0 0 rgba(255, 255, 255, 0.09),
    -33vmin 17vmin 0 0 rgba(255, 255, 255, 0.06),
    -27vmin -8vmin 0 0 rgba(255, 255, 255, 0.1),
    -19vmin 29vmin 0 0 rgba(255, 255, 255, 0.05),
    -12vmin -21vmin 0 0 rgba(255, 255, 255, 0.08),
    -4vmin 11vmin 0 0 rgba(255, 255, 255, 0.06),
    3vmin -30vmin 0 0 rgba(255, 255, 255, 0.07),
    9vmin 22vmin 0 0 rgba(255, 255, 255, 0.05),
    16vmin -6vmin 0 0 rgba(255, 255, 255, 0.09),
    23vmin 26vmin 0 0 rgba(255, 255, 255, 0.06),
    29vmin -18vmin 0 0 rgba(255, 255, 255, 0.08),
    36vmin 8vmin 0 0 rgba(255, 255, 255, 0.05),
    43vmin -27vmin 0 0 rgba(255, 255, 255, 0.07),
    -46vmin 4vmin 0 0 rgba(255, 255, 255, 0.05),
    47vmin 19vmin 0 0 rgba(255, 255, 255, 0.06);
  animation: ador-dust-far 11s linear both;
}

.ador-loader__dust--near {
  width: 2px;
  height: 2px;
  box-shadow:
    -37vmin 9vmin 0 0 rgba(255, 255, 255, 0.16),
    -24vmin -19vmin 0 0 rgba(255, 255, 255, 0.12),
    -11vmin 24vmin 0 0 rgba(255, 255, 255, 0.14),
    2vmin -12vmin 0 0 rgba(255, 255, 255, 0.11),
    14vmin 16vmin 0 0 rgba(255, 255, 255, 0.15),
    26vmin -24vmin 0 0 rgba(255, 255, 255, 0.12),
    38vmin 13vmin 0 0 rgba(255, 255, 255, 0.13),
    -44vmin -14vmin 0 0 rgba(255, 255, 255, 0.1),
    45vmin -3vmin 0 0 rgba(255, 255, 255, 0.11),
    -6vmin -31vmin 0 0 rgba(255, 255, 255, 0.13);
  animation: ador-dust-near 8s linear both;
}

/* ------------------------------------------------------------------ *
 * Composition — logo, rule, brand text
 * ------------------------------------------------------------------ */

.ador-loader__composition {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
}

/* The logo and its lines, shrink-wrapped. This is the layer that recedes and
   blurs in Scene 6, and a blur is paid for by the pixel. */
.ador-loader__lockup {
  display: flex;
  flex-direction: column;
  align-items: center;
  will-change: transform, filter, opacity;
}

/* Scenes 2 and 3 — the wordmark, and the light that crosses it.
 *
 * The mask is the project's own logo file, so the shape and its proportions are
 * the artwork's, untouched; what fills it is a machined-steel ramp, which is
 * what gives the sweep something to reflect off. The <img> underneath is the
 * same file, and stays the visible layer wherever masks are unsupported. */
.ador-loader__mark {
  position: relative;
  width: min(58vw, 340px);
  aspect-ratio: var(--ador-logo-ratio, 791 / 246);
  opacity: 0;
  transform: scale(0.92);
  will-change: transform, opacity;
}

.ador-loader__img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.ador-loader__metal,
.ador-loader__shine {
  display: none;
}

@supports ((-webkit-mask-image: url("a.png")) or (mask-image: url("a.png"))) {
  .ador-loader__mark {
    -webkit-mask-image: var(--ador-logo);
    mask-image: var(--ador-logo);
    -webkit-mask-size: 100% 100%;
    mask-size: 100% 100%;
    -webkit-mask-repeat: no-repeat;
    mask-repeat: no-repeat;
    -webkit-mask-position: center;
    mask-position: center;
  }

  .ador-loader__img {
    opacity: 0;
  }

  .ador-loader__metal,
  .ador-loader__shine {
    display: block;
  }
}

/* Polished steel: bright faces with two darker bands between them. The bands
   are what the light in Scene 3 has to lift — on a flat white fill a passing
   reflection has nothing to brighten. */
.ador-loader__metal {
  position: absolute;
  inset: 0;
  background: linear-gradient(
    178deg,
    #f1f3f5 0%,
    #ffffff 16%,
    #b3b9c0 38%,
    #eef0f3 54%,
    #9ca3aa 72%,
    #d8dce0 88%,
    #f6f7f8 100%
  );
}

/* A studio light crossing polished metal: soft, off-vertical, bright only at
   its core. `screen` is what makes it read as a reflection — it lifts the
   steel's darker bands instead of painting a white bar over them. */
.ador-loader__shine {
  position: absolute;
  top: -35%;
  bottom: -35%;
  left: 0;
  width: 24%;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.26) 38%,
    rgba(255, 255, 255, 0.92) 50%,
    rgba(255, 255, 255, 0.26) 62%,
    rgba(255, 255, 255, 0) 100%
  );
  filter: blur(3px);
  mix-blend-mode: screen;
  transform: translate3d(-200%, 0, 0) skewX(-12deg);
  will-change: transform;
}

/* Scene 5 — a machined rule that opens from the centre. */
.ador-loader__rule {
  width: min(52vw, 296px);
  height: 1px;
  margin-top: clamp(18px, 3.4vh, 30px);
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.22) 50%,
    rgba(255, 255, 255, 0) 100%
  );
  transform: scaleX(0);
}

/* Scene 4 — brand text. A system geometric stack on purpose: the site's webfont
   may still be swapping in at this point, and a typeface change mid-reveal is
   the one thing the sequence cannot absorb. */
.ador-loader__brand,
.ador-loader__tagline {
  margin: 0;
  font-family: ui-sans-serif, system-ui, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  font-weight: 500;
  text-transform: uppercase;
  white-space: nowrap;
  opacity: 0;
  transform: translate3d(0, 8px, 0);
}

.ador-loader__brand {
  margin-top: clamp(16px, 2.8vh, 26px);
  font-size: clamp(10px, 0.5vw + 8px, 13px);
  /* The trailing letter-space would push the word off centre without this. */
  letter-spacing: 0.44em;
  text-indent: 0.44em;
  color: rgba(255, 255, 255, 0.74);
}

.ador-loader__tagline {
  margin-top: 9px;
  font-size: clamp(8px, 0.34vw + 6.6px, 10px);
  letter-spacing: 0.32em;
  text-indent: 0.32em;
  color: rgba(255, 255, 255, 0.34);
}

/* ------------------------------------------------------------------ *
 * Scene 7 — the metal panel
 * ------------------------------------------------------------------ */

.ador-loader__panel {
  position: absolute;
  inset: 0;
  transform: translate3d(-101%, 0, 0);
  background:
    /* the bevels along both long edges */
    linear-gradient(
      180deg,
      rgba(255, 255, 255, 0.07) 0 1px,
      rgba(0, 0, 0, 0) 1px calc(100% - 1px),
      rgba(0, 0, 0, 0.5) calc(100% - 1px) 100%
    ),
    /* brushed grain, running the length of the panel */
    repeating-linear-gradient(
      93deg,
      rgba(255, 255, 255, 0.035) 0 1px,
      rgba(0, 0, 0, 0.05) 1px 2px,
      rgba(0, 0, 0, 0) 2px 4px
    ),
    /* the surface itself */
    linear-gradient(176deg, #24272b 0%, #16181b 34%, #1d2125 56%, #101214 78%, #191c20 100%);
  /* The trailing edge's own bevel, painted with the panel. */
  box-shadow: inset 1px 0 0 rgba(255, 255, 255, 0.07);
  will-change: transform;
}

/* The shadow the panel drags behind it, so the site comes out from under the
   panel instead of appearing at a hard line. */
.ador-loader__panel::before {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  right: 100%;
  width: min(26vw, 300px);
  background: linear-gradient(90deg, rgba(0, 0, 0, 0) 0%, rgba(0, 0, 0, 0.46) 100%);
}

/* The machined leading edge. */
.ador-loader__panel::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: 100%;
  width: 1px;
  background: linear-gradient(
    180deg,
    rgba(255, 255, 255, 0.05) 0%,
    rgba(255, 255, 255, 0.55) 34%,
    rgba(255, 255, 255, 0.72) 54%,
    rgba(255, 255, 255, 0.06) 100%
  );
  box-shadow: 0 0 28px 2px rgba(188, 204, 222, 0.22);
}

/* A highlight travelling across the panel's own surface while the panel travels
   across the screen — the reflection moves, so the metal reads as metal. */
.ador-loader__gloss {
  position: absolute;
  top: -20%;
  bottom: -20%;
  left: 0;
  width: 42%;
  background: linear-gradient(
    90deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.045) 40%,
    rgba(255, 255, 255, 0.1) 52%,
    rgba(255, 255, 255, 0.03) 66%,
    rgba(255, 255, 255, 0) 100%
  );
  transform: translate3d(-70%, 0, 0) skewX(-14deg);
  will-change: transform;
}

/* ------------------------------------------------------------------ *
 * The cue. Nothing above moves until `loader.js` says the logo is decoded.
 * ------------------------------------------------------------------ */

.ador-loader.is-playing .ador-loader__mark {
  animation: ador-logo-in 700ms cubic-bezier(0.16, 0.84, 0.44, 1) 80ms both;
}

.ador-loader.is-playing .ador-loader__shine {
  animation: ador-shine 800ms cubic-bezier(0.4, 0, 0.25, 1) 400ms both;
}

.ador-loader.is-playing .ador-loader__rule {
  animation: ador-rule-in 620ms cubic-bezier(0.2, 0.8, 0.25, 1) 520ms both;
}

.ador-loader.is-playing .ador-loader__brand {
  animation: ador-text-in 420ms cubic-bezier(0.16, 0.84, 0.44, 1) 600ms both;
}

.ador-loader.is-playing .ador-loader__tagline {
  animation: ador-text-in 440ms cubic-bezier(0.16, 0.84, 0.44, 1) 690ms both;
}

.ador-loader.is-playing .ador-loader__dust {
  opacity: 1;
}

/* Scene 6 — the identity moving away from the viewer. */
.ador-loader.is-playing .ador-loader__lockup {
  animation: ador-recede 380ms cubic-bezier(0.65, 0, 0.35, 1) 1250ms both;
}

.ador-loader.is-playing .ador-loader__panel {
  animation: ador-panel-in 380ms cubic-bezier(0.72, 0, 0.24, 1) 1500ms both;
}

.ador-loader.is-playing .ador-loader__gloss {
  animation: ador-gloss 920ms cubic-bezier(0.5, 0, 0.3, 1) 1500ms both;
}

/* Scene 8 — the same panel carries on, and takes the overlay with it. */
.ador-loader.is-wiping .ador-loader__panel {
  animation: ador-panel-out 540ms cubic-bezier(0.6, 0, 0.18, 1) both;
}

/* ------------------------------------------------------------------ *
 * Keyframes
 * ------------------------------------------------------------------ */

@keyframes ador-ambient {
  from {
    opacity: 0.35;
    transform: translate3d(-50%, -46%, 0) scale(1.84);
  }
  to {
    opacity: 0.9;
    transform: translate3d(-50%, -56%, 0) scale(2.14);
  }
}

@keyframes ador-dust-far {
  from {
    opacity: 0;
    transform: translate3d(0, 0, 0);
  }
  18% {
    opacity: 1;
  }
  to {
    opacity: 1;
    transform: translate3d(1.6vmin, -4.2vmin, 0);
  }
}

@keyframes ador-dust-near {
  from {
    opacity: 0;
    transform: translate3d(0, 0, 0);
  }
  22% {
    opacity: 1;
  }
  to {
    opacity: 1;
    transform: translate3d(-2.4vmin, -6.4vmin, 0);
  }
}

@keyframes ador-logo-in {
  from {
    opacity: 0;
    transform: scale(0.92);
  }
  to {
    opacity: 1;
    transform: scale(1);
  }
}

@keyframes ador-shine {
  from {
    transform: translate3d(-200%, 0, 0) skewX(-12deg);
  }
  to {
    transform: translate3d(560%, 0, 0) skewX(-12deg);
  }
}

@keyframes ador-rule-in {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

@keyframes ador-text-in {
  from {
    opacity: 0;
    transform: translate3d(0, 8px, 0);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0);
  }
}

@keyframes ador-recede {
  from {
    transform: scale(1);
    filter: blur(0);
    opacity: 1;
  }
  to {
    transform: scale(0.75);
    filter: blur(1.1px);
    opacity: 0.82;
  }
}

@keyframes ador-panel-in {
  from {
    transform: translate3d(-101%, 0, 0);
  }
  to {
    transform: translate3d(0, 0, 0);
  }
}

@keyframes ador-panel-out {
  from {
    transform: translate3d(0, 0, 0);
  }
  to {
    transform: translate3d(101%, 0, 0);
  }
}

@keyframes ador-gloss {
  from {
    transform: translate3d(-70%, 0, 0) skewX(-14deg);
  }
  to {
    transform: translate3d(190%, 0, 0) skewX(-14deg);
  }
}

/* ------------------------------------------------------------------ *
 * Short mode — later pages in the same session
 *
 * The wordmark is already there; only the light and the panel move. About a
 * second, so moving through the site never feels gated.
 * ------------------------------------------------------------------ */

html.ador-loader-short .ador-loader__mark {
  opacity: 1;
  transform: scale(0.88);
}

html.ador-loader-short .ador-loader__rule,
html.ador-loader-short .ador-loader__brand,
html.ador-loader-short .ador-loader__tagline,
html.ador-loader-short .ador-loader__dust {
  display: none;
}

html.ador-loader-short .ador-loader.is-playing .ador-loader__mark,
html.ador-loader-short .ador-loader.is-playing .ador-loader__lockup {
  animation: none;
}

html.ador-loader-short .ador-loader.is-playing .ador-loader__shine {
  animation-duration: 520ms;
  animation-delay: 60ms;
}

html.ador-loader-short .ador-loader.is-playing .ador-loader__panel {
  animation-duration: 320ms;
  animation-delay: 220ms;
}

html.ador-loader-short .ador-loader.is-playing .ador-loader__gloss {
  animation-duration: 780ms;
  animation-delay: 220ms;
}

/* The playing rule above outranks the wiping shorthand, so the delay it set has
   to be cleared here, or the panel would hold for a beat before sliding out. */
html.ador-loader-short .ador-loader.is-wiping .ador-loader__panel {
  animation-duration: 460ms;
  animation-delay: 0ms;
}

/* ------------------------------------------------------------------ *
 * Reduced motion — the wordmark, held, then gone
 * ------------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .ador-loader__ambient,
  .ador-loader__dust,
  .ador-loader__shine,
  .ador-loader__panel {
    display: none;
  }

  .ador-loader__mark {
    opacity: 1;
    transform: none;
  }

  .ador-loader__rule {
    transform: scaleX(1);
  }

  .ador-loader__brand,
  .ador-loader__tagline {
    opacity: 1;
    transform: none;
  }

  .ador-loader.is-playing .ador-loader__mark,
  .ador-loader.is-playing .ador-loader__rule,
  .ador-loader.is-playing .ador-loader__brand,
  .ador-loader.is-playing .ador-loader__tagline,
  .ador-loader.is-playing .ador-loader__lockup,
  .ador-loader.is-playing .ador-loader__panel,
  .ador-loader.is-playing .ador-loader__gloss {
    animation: none;
  }

  .ador-loader__lockup,
  .ador-loader__mark {
    will-change: auto;
  }
}
