/* The interactive gradient field, shared by the homepage and the client portal.
   It lives in its own file because the portal is served under a stricter CSP
   than the rest of the site — `style-src 'self'` with no 'unsafe-inline' — so
   it cannot carry this in a <style> block the way the homepage used to. Same
   reason gradient.js is external.

   Colours are literals rather than custom properties: the portal stylesheet
   defines its own palette and does not share the homepage's :root. */

.stage {
  position: relative;
  min-height: 100svh;
  overflow: hidden;
  isolation: isolate;
  background: var(--navy-950, #040a13);
}

.stage__gradient,
.stage__scrim,
.stage__grain {
  position: absolute;
  pointer-events: none;
}

/* Scrim: a subtle top-band and a gentle bottom fade so the gradient reads
   as bound to the page rather than just cropped. The text overlay scrim
   is no longer needed since the hero text now lives below the stage. */
.stage__scrim {
  inset: 0;
  z-index: -2;
  background:
    linear-gradient(
      180deg,
      rgba(2, 5, 11, 0.55) 0%,
      rgba(2, 5, 11, 0.2) 10%,
      rgba(2, 5, 11, 0) 30%,
      rgba(2, 5, 11, 0) 70%,
      rgba(2, 5, 11, 0.35) 90%,
      rgba(2, 5, 11, 0.7) 100%
    );
}

/* WebGL draws the interactive field. The gradient below is the no-script /
   no-WebGL fallback: same four colours, same black-cornered composition. */
.stage__gradient {
  inset: 0;
  z-index: -3;
  width: 100%;
  height: 100%;
  display: block;
}

.stage.gradient-fallback {
  background:
    radial-gradient(ellipse 62% 58% at 14% 74%, #e64f0f 0%, #aadfd9 34%, #23418a 58%, rgba(2, 5, 11, 0) 78%),
    radial-gradient(ellipse 54% 60% at 88% 30%, #23418a 0%, #16254b 46%, rgba(2, 5, 11, 0) 74%),
    #02050b;
}

.stage__grain {
  inset: -50%;
  z-index: -1;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.92' numOctaves='2' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23n)'/%3E%3C/svg%3E");
  opacity: 0.035;
  mix-blend-mode: overlay;
  animation: grain-shift 0.9s steps(3) infinite;
}

@keyframes grain-shift {
  0% { transform: translate(0, 0); }
  33% { transform: translate(-3%, 2%); }
  66% { transform: translate(2%, -3%); }
  100% { transform: translate(0, 0); }
}

/* The portal wants the field behind a whole scrolling page rather than as a
   band at the top, so it pins to the viewport and sits under everything. */
.stage--backdrop {
  position: fixed;
  inset: 0;
  min-height: 0;
  z-index: -1;
}

/* The portal has no motion toggle of its own, so the grain has to answer the
   OS setting and forced colours from here rather than from the homepage. */
@media (prefers-reduced-motion: reduce) {
  .stage__grain {
    animation: none;
  }
}

@media (forced-colors: active) {
  .stage__gradient,
  .stage__scrim,
  .stage__grain {
    display: none;
  }
}
