/* ==========================================================================
   Layout - containers, sections, surfaces, ambient light
   Mobile-first: every breakpoint below is min-width.
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--container);
  margin-inline: auto;
  padding-inline: var(--gutter);
  position: relative;
}

/* ---- Sections ---- */
.section {
  position: relative;
  isolation: isolate;
  /* Clips the ambient blooms at the section edge. clip, not hidden: hidden
     makes every section a scroll container, so a position: sticky element
     inside (.split__aside, .privacy__intro) pins to a section that never
     scrolls - it just sits pushed down by its top offset and never follows
     the page. clip draws the same edge without that. No layout depends on
     the formatting context hidden would add: there are no floats, and the
     padding below stops child margins collapsing through. hidden stays as
     the fallback for browsers without clip (Safari before 16), which lose
     the pinning but keep the clipping. */
  overflow: hidden;
  overflow: clip;
  padding-block: var(--sp-9);
}

@media (min-width: 768px) {
  .section {
    padding-block: var(--sp-10);
  }
}

/* Navbar and footer jump targets. The scroll stops with the section's own top padding
   tucked behind the header, so its label ("Configure", "Actions"...) sits just
   below the navbar instead of ~130px down. Negative margin = scroll further. */
#configure,
#response,
#actions,
#capture,
#analytics,
#review,
#security,
#testimonials {
  scroll-margin-top: -3.5rem;
}

@media (min-width: 768px) {
  #configure,
  #response,
  #actions,
  #capture,
  #analytics,
  #review,
  #security,
  #testimonials {
    scroll-margin-top: -6rem;
  }
}

/* First section on a page sits under the fixed header. */
.section--top {
  padding-top: 9rem;
}

@media (min-width: 640px) {
  .section--top {
    padding-top: 11rem;
  }
}

/* ---- Surfaces. The rhythm is ink-dominant with LIGHT bands as the contrast
   beat - inverted from the brief's light-dominant rhythm, because #32ABDE is
   unusable on white (2.62:1) but scores 7.15:1 on ink. ---- */
.surface-base { background-color: var(--base); color: var(--fg); }
.surface-ink { background-color: var(--ink); color: var(--fg); }

.surface-light {
  background-color: var(--ground);
  color: var(--body);
}

.surface-light h1,
.surface-light h2,
.surface-light h3 {
  color: var(--ink);
}

/* ---- Section heading block ---- */
.section-head {
  max-width: 40rem;
}

.section-head__title {
  margin-top: var(--sp-5);
}

.section-head__lede {
  margin-top: 1.25rem;
  color: var(--fg-muted);
}

.surface-light .section-head__lede {
  color: var(--body);
}

/* ---- Header beside a visual ----
   .section-head is capped at 40rem, which leaves ~450px of empty row beside
   it at desktop widths. .split puts a mockup in that space: the main column
   on the left, .split__aside on the right. Used by #capture, #actions and
   #review.

   The aside pins because the main column always runs longer - a long header,
   and in #actions the list under it too. Pinned, the visual stays beside
   whatever is being read instead of scrolling off and leaving the space
   empty again. 6.5rem clears the floating nav, same as .privacy__intro.

   No margin-top: the split is the first thing in its container, so the
   section's own padding sets the top edge, the same as every other section
   that opens on a bare .section-head. */
.split {
  display: grid;
  gap: 2.5rem;
  align-items: start;
}

/* A grid item's min-width defaults to its content width, which can push a
   minmax(0, ...) track wider than its share. */
.split__main {
  min-width: 0;
}

.split__aside {
  width: 100%;
  margin-inline: auto;
}

@media (min-width: 1024px) {
  .split {
    /* Same ratio as .hero__grid: a roomy text column, a narrower visual. */
    grid-template-columns: minmax(0, 1.18fr) minmax(0, 1fr);
    gap: var(--sp-7);
  }

  .split__aside {
    position: sticky;
    top: 6.5rem;
  }
}

/* ==========================================================================
   Ambient light. The reference image's signature: a soft diagonal streak plus
   a broad radial bloom. Pure CSS - no image weight, and it scales to any
   viewport without artefacts.
   ========================================================================== */

.ambient {
  position: absolute;
  inset: 0;
  z-index: -10;
  pointer-events: none;
  overflow: hidden;
}

/* A top bloom is cut off flat at its section's top edge. Where the section
   above is a different surface, that cut lands on the colour change and
   can't be seen; where it is the SAME surface, it shows as a hard line
   across one continuous background. So the glow fades in over its first
   7rem only when the previous section shares the surface - and has no
   border of its own, which already marks the join. Keyed to the neighbour,
   not to section ids, so it keeps working as sections are reordered. */
.section:not(.section--bordered).surface-base + .section.surface-base > .ambient,
.section:not(.section--bordered).surface-ink + .section.surface-ink > .ambient {
  -webkit-mask-image: linear-gradient(to bottom, transparent, #000 7rem);
  mask-image: linear-gradient(to bottom, transparent, #000 7rem);
}

.bloom {
  position: absolute;
  border-radius: var(--radius-pill);
  filter: blur(72px);
  pointer-events: none;
}

.bloom--brand {
  background: radial-gradient(circle at center, rgb(31 97 204 / 0.35), transparent 70%);
}

.bloom--accent {
  background: radial-gradient(circle at center, rgb(50 171 222 / 0.28), transparent 70%);
}

.streak {
  position: absolute;
  pointer-events: none;
  background: linear-gradient(
    115deg,
    transparent 22%,
    rgb(50 171 222 / 0.1) 38%,
    rgb(31 97 204 / 0.2) 48%,
    rgb(50 171 222 / 0.08) 56%,
    transparent 68%
  );
  filter: blur(48px);
}

/* Perspective grid, masked so it fades out rather than reading as noise. */
.grid-plane {
  position: absolute;
  inset: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, rgb(50 171 222 / 0.08) 1px, transparent 1px),
    linear-gradient(to bottom, rgb(50 171 222 / 0.08) 1px, transparent 1px);
  background-size: 64px 64px;
  -webkit-mask-image: radial-gradient(ellipse 75% 55% at 72% 8%, #000 0%, transparent 72%);
  mask-image: radial-gradient(ellipse 75% 55% at 72% 8%, #000 0%, transparent 72%);
}

/* ---- Bloom placements ---- */
.bloom--hero-a { top: -10rem; left: 8%; width: 32rem; height: 32rem; }
.bloom--hero-b { top: -6rem; right: -6%; width: 38rem; height: 38rem; }
.bloom--top-left { top: -10rem; left: 25%; width: 32rem; height: 32rem; }
.bloom--top-right { top: -8rem; right: 10%; width: 32rem; height: 32rem; }
.bloom--cta-a { bottom: -30%; left: 50%; translate: -50% 0; width: 44rem; height: 44rem; }
.bloom--cta-b { top: -8rem; right: 10%; width: 26rem; height: 26rem; }

.streak--hero {
  left: -25%;
  top: -33%;
  width: 140%;
  height: 56rem;
  rotate: -8deg;
}

/* ==========================================================================
   Aurora motion.

   The catalogue's Aurora UI profile calls for "large flowing gradients, subtle
   8-12s animations, depth via colour layering" on exactly this kind of premium
   hero treatment.

   Animated via the INDEPENDENT `translate` and `scale` properties rather than
   `transform`, because .bloom--cta-a and .bloom--proof carry a centring
   `translate` and animating `transform` would fight it.

   Amplitude is deliberately small - this should read as "alive", not "moving".
   Durations are desynced (11s / 12s / 9s) so the layers never visibly pulse in
   lockstep, and all sit inside the profile's 8-12s window.
   ========================================================================== */

@keyframes aurora-a {
  0%, 100% { scale: 1; }
  50% { scale: 1.09; }
}

@keyframes aurora-b {
  0%, 100% { scale: 1; }
  50% { scale: 1.06; }
}

@keyframes aurora-breathe {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.72; }
}

.aurora-a { animation: aurora-a 11s ease-in-out infinite; }
.aurora-b { animation: aurora-b 12s ease-in-out infinite; }
.aurora-breathe { animation: aurora-breathe 9s ease-in-out infinite; }

/* ---- Compositor hints ----
   will-change only on things that move for as long as they are on screen:
   the looping blooms (each carries a 72px blur, expensive to repaint) and
   the desktop hero parallax. One-shot scroll reveals get no hint - GSAP
   promotes them for the length of each tween and releases them after,
   which is what will-change would do, without holding a layer forever. */
.aurora-a,
.aurora-b {
  will-change: scale;
}

.aurora-breathe {
  will-change: opacity;
}

@media (min-width: 1024px) and (prefers-reduced-motion: no-preference) {
  [data-parallax] {
    will-change: transform;
  }
}

/* ==========================================================================
   Reduced motion. Everything animated is decorative; nothing here may hide
   content. The final readable state is always presented.

   GSAP reveals are handled separately in animations.js via gsap.matchMedia,
   which skips the tween entirely rather than fading it fast.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  /* Nothing loops here, so there is nothing to keep a layer ready for. */
  .aurora-a,
  .aurora-b,
  .aurora-breathe {
    will-change: auto;
  }
}

/* Centred variant, used by sections that lead with a middle-aligned heading
   over evenly weighted columns rather than a left-aligned block. */
.section-head--center {
  margin-inline: auto;
  text-align: center;
}
