/* ===== hero: the astronaut render, given real depth =====================
   astronaut-scene.js replaces the template's banner image with one finished
   render driven by a depth map (see the note at the top of that file for why
   the old nine-layer stack could never have looked like anything).

   The host class is set by JS rather than matched with :has(), so the page
   does not depend on selector support to lay the hero out. */

.banner-bg-wrap.sw-hero3d-host {
  position: absolute;
  inset: 0;
  overflow: hidden;
  background: radial-gradient(122% 96% at 32% 46%, #edf3ff 0%, #dcecff 44%, #cfe1fa 100%);
}

.sw-hero3d-scene {
  position: absolute;
  inset: 0;
  z-index: 2;
  overflow: hidden;
  perspective: 1600px;
  /* follows the plate wherever a breakpoint puts it, so the vanishing point
     never has to be restated alongside --plate-x / --plate-y */
  perspective-origin: var(--plate-x) var(--plate-y);
}

.banner-bg-wrap.sw-hero3d-host > .banner-bg-shape {
  z-index: 1;
  pointer-events: none;
}

/* One shared geometry for the plate and the bloom behind it, so they cannot
   drift apart when the viewport changes. */
.sw-hero3d-scene {
  /* Three terms, so the plate answers to the window in both directions and
     re-solves continuously as it is dragged rather than only at breakpoints:
     a share of the width, a hard ceiling, and a height cap for short windows.
     150vh is deliberately loose - the plate is meant to overrun the viewport a
     little and let the vignette carry the edges, which is what it already does
     on a 1080p screen. It only bites when a window is short enough that the
     helmet would otherwise be cropped at both ends. */
  --plate-w: min(74vw, 1340px, 150vh);
  --plate-x: 33%;
  --plate-y: 50%;
}

.sw-hero3d-plate {
  position: absolute;
  left: var(--plate-x);
  top: var(--plate-y);
  width: var(--plate-w);
  aspect-ratio: 4 / 3;   /* the framed plate, margin included - see make-hero */
  transform: translate(-50%, -50%);
  transform-origin: 50% 50%;
  transform-style: preserve-3d;
  will-change: transform;
  backface-visibility: hidden;
  /* The render's backdrop is a dark violet rectangle. Dissolving all four
     edges is what stops it reading as a photo pasted onto a pale page and
     makes it a window cut into one - which is the metaphor the banner is
     already built on, since this whole scene lives inside the O portal.

     The stops have to reach zero by 100%, or the vignette is still partly
     opaque where the plate ends and the fade becomes a straight cut. That is
     what the render's bleed margin is for: everything out here is blurred
     backdrop, so the falloff never touches the helmet. */
  -webkit-mask-image: radial-gradient(ellipse 50% 50% at 50% 47%,
    #000 0 60%, rgba(0, 0, 0, .9) 74%, rgba(0, 0, 0, .45) 87%, transparent 100%);
  mask-image: radial-gradient(ellipse 50% 50% at 50% 47%,
    #000 0 60%, rgba(0, 0, 0, .9) 74%, rgba(0, 0, 0, .45) 87%, transparent 100%);
}

.sw-hero3d,
.sw-hero3d-flat {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
}

.sw-hero3d-flat {
  object-fit: cover;
  -webkit-user-select: none;
  user-select: none;
  will-change: transform;
}

/* Ambient bloom, sized off the plate so the dark art is seated in light
   rather than punched into it. Static: nothing inside this scene animates on
   a keyframe, because the hero is a full-bleed composited stack and animating
   anything in it repaints the whole group whether or not it is on screen. */
.sw-hero3d-halo {
  position: absolute;
  left: var(--plate-x);
  top: var(--plate-y);
  width: calc(var(--plate-w) * .95);
  aspect-ratio: 1;
  transform: translate(-50%, -50%);
  pointer-events: none;
  background: radial-gradient(circle,
    rgba(120, 112, 220, .28) 0%, rgba(146, 172, 255, .17) 38%,
    rgba(190, 210, 255, .07) 58%, transparent 74%);
}

/* Keeps the hero copy off the artwork on the widest screens, where the plate
   is at its largest. The scene ends well short of the text column, so this is
   a blend rather than the heavy scrim the layer stack needed. */
.sw-hero3d-scene::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 4;
  pointer-events: none;
  background: linear-gradient(90deg,
    transparent 0 56%, rgba(220, 234, 253, .42) 72%, rgba(223, 236, 253, .8) 86%, #dfecfd 96%);
}

@media (max-width: 991px) {
  .sw-hero3d-scene {
    /* On a phone the copy is centred OVER the art instead of sitting in a
       column beside it, so a plate in the middle of the frame puts the helmet
       directly behind the headline. Drop it into the lower half - which is
       also where the hero had nothing in it - and the two stop competing. */
    --plate-w: min(122vw, 760px, 150vh);
    --plate-x: 50%;
    --plate-y: 70%;
  }
  /* Inverted from the desktop scrim, which fades in from the right: here it
     is near-solid across the top where the words are and clears completely by
     the time it reaches the helmet. The old one was transparent for its first
     46%, i.e. over exactly the band the copy occupies, which is why the
     headline was sitting unprotected on the artwork. */
  .sw-hero3d-scene::after {
    background: linear-gradient(180deg,
      rgba(233, 241, 253, .94) 0 24%,
      rgba(233, 241, 253, .64) 42%,
      rgba(233, 241, 253, .16) 58%,
      rgba(233, 241, 253, 0) 70%);
  }
}

@media (max-width: 767px) {
  .sw-hero3d-scene { --plate-w: min(116vw, 620px, 150vh); --plate-y: 72%; }
}

/* Short viewports: a phone held sideways, or a desktop window dragged down
   small. Width on its own cannot size this - 143vw on a landscape phone is a
   plate half again as tall as the screen, so the helmet gets cropped at both
   ends and the vignette has nowhere to fade. Height leads here instead, and
   because these are the same viewport units the rest of the scene uses, it
   re-solves on every resize rather than only at the width breakpoints. */
@media (max-height: 620px) {
  .sw-hero3d-scene {
    --plate-w: min(104vh, 96vw);
    --plate-x: 50%;
    --plate-y: 47%;
  }
  /* Too little height here to give the copy and the helmet separate bands, so
     the art goes back to being a wash behind the words rather than a subject
     beside them. */
  .sw-hero3d-scene::after {
    background: linear-gradient(180deg,
      rgba(233, 241, 253, .82) 0 30%,
      rgba(233, 241, 253, .58) 56%,
      rgba(233, 241, 253, .26) 80%,
      rgba(233, 241, 253, 0) 100%);
  }
}

@media (prefers-reduced-motion: reduce) {
  /* astronaut-scene.js already skips the pointer parallax here and draws the
     plate once, undisplaced; this covers the tilt it would otherwise keep. */
  .sw-hero3d-plate { transform: translate(-50%, -50%) !important; }
  .sw-hero3d-flat { transform: none !important; }
}
