/* ==========================================================================
   RBT Bank — Animation keyframes + utility classes
   GSAP/AOS handle most motion from JS; this file holds pure-CSS keyframes
   (float, ripple, shimmer) and the reduced-motion baseline that every
   animation in this project must respect.
   ========================================================================== */

@keyframes float {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-14px); }
}

.animate-float {
  animation: float 6s ease-in-out infinite;
}

@keyframes ripple {
  to {
    transform: scale(4);
    opacity: 0;
  }
}

@keyframes shimmer {
  0% { background-position: -200% 0; }
  100% { background-position: 200% 0; }
}

.skeleton {
  background: linear-gradient(90deg, var(--color-light-gray) 25%, #eef1f8 50%, var(--color-light-gray) 75%);
  background-size: 200% 100%;
  animation: shimmer 1.4s ease-in-out infinite;
}

@keyframes spin-slow {
  to { transform: rotate(360deg); }
}

.animate-spin-slow {
  animation: spin-slow 20s linear infinite;
}

/* Elements entrance-animated by GSAP (assets/js/animations.js) start hidden
   only via inline style set at runtime — never via this stylesheet, so the
   page is fully visible if JavaScript fails to load. */

/* Respect reduced-motion as a baseline rule, not a per-component afterthought.
   View-transition pseudo-elements live outside the normal DOM tree, so the
   universal selector below doesn't reach them — they need their own rule. */
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }

  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }

  html {
    scroll-behavior: auto !important;
  }
}

/* ==========================================================================
   Interaction layer
   Loaded last on every page, so these safely refine what the page
   stylesheets already lay out. Everything here is transform / shadow /
   opacity only — never layout — so it can't fight responsive.css.
   All of it is opt-in from main.js and gated on reduced-motion.
   ========================================================================== */

/* ── Scroll progress ──────────────────────────────────────────────────────
   A hairline at the very top of the viewport showing how far through the
   page you are. Reads as precision instrumentation rather than decoration,
   which is the tone we want on a bank. */

.scroll-progress {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 200;
  height: 3px;
  width: 100%;
  transform: scaleX(var(--scroll-progress, 0));
  transform-origin: 0 50%;
  background: linear-gradient(90deg, var(--color-navy) 0%, var(--color-blue) 55%, var(--color-gold) 100%);
  pointer-events: none;
  will-change: transform;
}

/* ── Card spotlight ───────────────────────────────────────────────────────
   A soft highlight that follows the pointer across a card, plus a lift and
   a brightened edge. Chosen over a 3D tilt for the card grids: tilt on a
   whole grid of financial products reads as a gimmick, whereas a light
   source that tracks the cursor reads as depth. --mx/--my are written by
   initCardSpotlight in main.js as percentages. */

[data-spotlight] {
  position: relative;
  /* isolate creates a stacking context on the card, which is what lets the
     ::before below sit at z-index:-1 — above the card's own background but
     beneath its content — without escaping behind the card entirely.
     Do NOT reposition the children to achieve this: several cards
     (.service-photo-card) rely on absolutely-positioned scrims and bodies
     overlaying their photo, and forcing those to relative collapses the
     card into a vertical stack. */
  isolation: isolate;
  transition:
    transform var(--duration-base) var(--ease-out),
    box-shadow var(--duration-base) var(--ease-out);
}

[data-spotlight]::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  border-radius: inherit;
  pointer-events: none;
  opacity: 0;
  transition: opacity var(--duration-base) var(--ease-out);
  background: radial-gradient(
    22rem circle at var(--mx, 50%) var(--my, 50%),
    rgba(0, 180, 216, 0.16) 0%,
    rgba(0, 119, 182, 0.07) 38%,
    transparent 70%
  );
}

[data-spotlight]:hover::before {
  opacity: 1;
}

[data-spotlight]:hover {
  transform: translateY(-4px);
  box-shadow: var(--shadow-lg);
}

/* ── 3D tilt ──────────────────────────────────────────────────────────────
   Real perspective transforms, reserved for a handful of hero/feature
   surfaces rather than every card. --tilt-x/--tilt-y come from
   initTilt in main.js. */

[data-tilt] {
  transform: perspective(1000px) rotateX(var(--tilt-x, 0deg)) rotateY(var(--tilt-y, 0deg))
    translate3d(0, 0, 0);
  transform-style: preserve-3d;
  transition: transform var(--duration-slow) var(--ease-out);
  will-change: transform;
}

[data-tilt].is-tilting {
  /* Track the pointer closely while it's over the element, then ease back
     on exit using the longer transition above. */
  transition: transform 90ms linear;
}

/* ── Reveal-on-scroll refinements ────────────────────────────────────────
   AOS supplies the observer and the classes; these override its stock
   easing and travel distance. The default 'ease' + 100px rise feels
   generic — a shorter rise on an emphasized curve reads far more composed. */

[data-aos] {
  transition-timing-function: var(--ease-emphasized) !important;
}

[data-aos^="fade-up"]:not(.aos-animate) {
  transform: translate3d(0, 28px, 0);
}

[data-aos^="fade-down"]:not(.aos-animate) {
  transform: translate3d(0, -28px, 0);
}

[data-aos^="fade-right"]:not(.aos-animate) {
  transform: translate3d(-28px, 0, 0);
}

[data-aos^="fade-left"]:not(.aos-animate) {
  transform: translate3d(28px, 0, 0);
}

[data-aos^="zoom"]:not(.aos-animate) {
  transform: scale(0.96);
}

/* ── Heading underline sweep ─────────────────────────────────────────────
   The accent rule under a section heading draws itself in as the heading
   arrives, instead of appearing fully formed. */

.section-heading__rule,
.events-section-heading__accent {
  transform-origin: 0 50%;
}

[data-aos].aos-animate .section-heading__rule,
.section-heading[data-aos].aos-animate .section-heading__rule {
  animation: rule-draw var(--duration-slower) var(--ease-emphasized) both;
}

@keyframes rule-draw {
  from {
    transform: scaleX(0);
  }
  to {
    transform: scaleX(1);
  }
}

/* ── Link underline ──────────────────────────────────────────────────────
   Body links grow an underline from the left rather than toggling
   text-decoration, which snaps. */

/* position only — never `display`. Both card links are inline-flex with an
   animated gap, and forcing inline-block wraps their arrow onto a new line. */
.link-underline,
.event-card__link,
.service-photo-card__link {
  position: relative;
}

.link-underline {
  display: inline-block;
}

.link-underline::after,
.event-card__link::after,
.service-photo-card__link::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 1.5px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: 0 50%;
  transition: transform var(--duration-base) var(--ease-out);
}

.link-underline:hover::after,
.event-card__link:hover::after,
.service-photo-card__link:hover::after {
  transform: scaleX(1);
}

/* ── Image reveal ────────────────────────────────────────────────────────
   Photos inside cards settle from a slight zoom as they come into view,
   so imagery feels placed rather than pasted. */

.event-card__visual img,
.home-property-card__image img,
.property-card__image img,
.service-photo-card__media img,
.events-highlight-card__media img {
  transition: transform var(--duration-slower) var(--ease-out);
}

.event-card:hover .event-card__visual img,
.home-property-card:hover .home-property-card__image img,
.property-card:hover .property-card__image img,
.service-photo-card:hover .service-photo-card__media img,
.events-highlight-card:hover .events-highlight-card__media img {
  transform: scale(1.05);
}

/* ── Reduced motion ──────────────────────────────────────────────────────
   The global rule above already flattens durations, but transforms that
   are a *resting state* rather than an animation need switching off by
   hand — otherwise a tilted or lifted card just stays tilted. */

@media (prefers-reduced-motion: reduce) {
  .scroll-progress {
    display: none;
  }

  [data-tilt],
  [data-spotlight]:hover {
    transform: none;
  }

  [data-spotlight]::before {
    display: none;
  }

  .event-card:hover .event-card__visual img,
  .home-property-card:hover .home-property-card__image img,
  .property-card:hover .property-card__image img,
  .service-photo-card:hover .service-photo-card__media img,
  .events-highlight-card:hover .events-highlight-card__media img {
    transform: none;
  }

  .btn::after {
    display: none;
  }

  [data-aos].aos-animate .section-heading__rule {
    animation: none;
  }
}

/* ==========================================================================
   Scroll-driven animations (native CSS)
   `animation-timeline` ties an animation's progress to scroll position
   instead of to time. The browser runs these on the compositor thread, so
   they hold 60fps without touching main-thread JS — the current
   best-practice replacement for scroll-listener animation.

   Everything is inside @supports: browsers without it keep the JS-driven
   progress bar and static images, which is a complete, correct page.
   ========================================================================== */

@supports (animation-timeline: scroll()) {
  /* Progress bar, straight off the compositor — the rAF scroll listener in
     main.js becomes redundant here (it keeps running harmlessly for
     browsers that need it, but this keyframe wins on the transform). */
  .scroll-progress {
    transform: scaleX(0);
    animation: sda-progress linear both;
    animation-timeline: scroll(root block);
  }

  @keyframes sda-progress {
    to {
      transform: scaleX(1);
    }
  }

  /* Card imagery drifts a little slower than the page as it passes through
     the viewport. Real parallax depth, and because the media boxes already
     clip with overflow:hidden, nothing shifts in layout — the image just
     moves inside its frame. The scale keeps the edges covered at both
     extremes of the drift. */
  .event-card__visual img,
  .home-property-card__image img,
  .property-card__image img,
  .events-highlight-card__media img {
    animation: sda-parallax linear both;
    animation-timeline: view();
    animation-range: cover 0% cover 100%;
  }

  @keyframes sda-parallax {
    from {
      transform: scale(1.12) translateY(-2.5%);
    }
    to {
      transform: scale(1.12) translateY(2.5%);
    }
  }

  /* Hover zoom and scroll parallax both want the transform. Let hover win
     by pausing the scroll animation while the pointer is over the card. */
  .event-card:hover .event-card__visual img,
  .home-property-card:hover .home-property-card__image img,
  .property-card:hover .property-card__image img,
  .events-highlight-card:hover .events-highlight-card__media img {
    animation-play-state: paused;
  }

  /* Section headings settle as they enter, on the compositor rather than
     via an observer. Only applied where AOS isn't already handling the
     element, so the two can't fight over the same transform. */
  .section-heading:not([data-aos]) {
    animation: sda-rise linear both;
    animation-timeline: view();
    animation-range: entry 10% entry 70%;
  }

  @keyframes sda-rise {
    from {
      opacity: 0;
      transform: translateY(24px);
    }
    to {
      opacity: 1;
      transform: none;
    }
  }
}

/* ==========================================================================
   Aurora — slow drifting brand-colour wash for dark sections.
   Two large, soft radial gradients on a navy base, offset and animated on
   long durations so the surface is never quite static. Cheap (two
   gradients, one transform) and it stops full-bleed navy blocks reading as
   flat colour fills.
   ========================================================================== */

.section--dark,
.home-apply-online {
  position: relative;
  isolation: isolate;
}

/* inset:0 with an over-sized background, drifting via background-position.
   A transform-and-overflow approach would need overflow:hidden on the
   section to stop the glow bleeding into the next one — and these sections
   contain absolutely-positioned children that clipping would cut off. */
.section--dark::before,
.home-apply-online::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  opacity: 0.55;
  background:
    radial-gradient(38rem 30rem at 18% 22%, rgba(0, 180, 216, 0.3) 0%, transparent 62%),
    radial-gradient(34rem 28rem at 82% 78%, rgba(0, 119, 182, 0.34) 0%, transparent 62%);
  background-size: 140% 140%, 140% 140%;
  background-position: 0% 0%, 100% 100%;
  background-repeat: no-repeat;
  animation: aurora-drift 24s var(--ease-in-out) infinite alternate;
}

@keyframes aurora-drift {
  from {
    background-position: 0% 0%, 100% 100%;
  }
  to {
    background-position: 22% 18%, 78% 82%;
  }
}

@media (prefers-reduced-motion: reduce) {
  .section--dark::before,
  .home-apply-online::before {
    animation: none;
  }

  .event-card__visual img,
  .home-property-card__image img,
  .property-card__image img,
  .events-highlight-card__media img,
  .section-heading:not([data-aos]) {
    animation: none !important;
  }
}
