/* ==========================================================================
   Cool Pro Services — Scroll-controlled zoom section
   --------------------------------------------------------------------------
   Pairs with cp-scroll-zoom.js and one Elementor HTML widget inside a
   full-width container carrying the classes `cp-zoom-section cp-static`.

   Self-contained on purpose: the static folder's .eyebrow / .btn-primary /
   .split-bar helpers do not exist on this install, so every visual is
   defined here from the site's own --cp-* tokens.

   Default state = a plain static image band. The JS upgrades it to the
   pinned, scroll-scrubbed video only on large screens without a reduced-motion
   preference, so no-JS, reduced-motion and phones all get the same fallback
   and never download the video.
   ========================================================================== */

/* The site-wide scroll-reveal hides every widget inside a parent container
   until its classifier reveals it. This section opts out via .cp-static on the
   container, so make that opt-out cover the widget inside it too — otherwise
   the section sits at opacity 0 until the classifier's failsafe fires, and a
   reveal transform on an ancestor would break position: sticky outright.

   .cp-lead-in is exempted from the guard: those widgets run their own
   entrance keyframes (cp-hero-zoom.css), and an !important declaration here
   would outrank the animation and stop it playing. The site-wide hide rule
   they fall back to is a plain `opacity: 0`, which an animation does outrank,
   and it only exists under prefers-reduced-motion: no-preference. */
html.cp-anim .cp-static .elementor-widget:not(.cp-lead-in) {
  opacity: 1 !important;
  transform: none !important;
  filter: none !important;
}

.cp-zoom {
  /* Extra scroll distance the pin consumes on top of one stage height.
     ~1.3 screens keeps the interaction short. */
  --cp-zoom-scroll: 130vh;
  /* Overwritten inline by the JS with the live sticky-header height. */
  --cp-zoom-top: 0px;
  /* Scroll progress, published by the JS in scrub mode. Defaults to 1 so that
     with no JS, on a phone, or under reduced motion the copy is simply
     visible: nothing in those states adds .is-scrub. */
  --cp-p: 1;

  position: relative;
  display: block;
  background: var(--cp-ink-900, #070C1C);
  font-family: var(--cp-body, "Inter", sans-serif);
}

/* --------------------------------------------------------------------------
   Fallback / no-JS state — a normal, non-pinned image band
   -------------------------------------------------------------------------- */
.cp-zoom-track { position: relative; }

.cp-zoom-stage {
  position: relative;
  overflow: hidden;
  aspect-ratio: 16 / 9;
  max-height: 78vh;
  display: flex;
  align-items: flex-end;
}

.cp-zoom-frame { position: absolute; inset: 0; }

.cp-zoom-still,
.cp-zoom-video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  margin: 0;
  max-width: none;
}

/* The video only exists once the JS has decided to use it. */
.cp-zoom-video {
  opacity: 0;
  transition: opacity .35s ease;
  pointer-events: none;
}

/* Weighted to the right: the subject sits left of centre at the wide end of
   the move, so the copy has to stay off it. */
.cp-zoom-scrim {
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to top, rgba(7, 12, 28, .82) 0%, rgba(7, 12, 28, .42) 34%, rgba(7, 12, 28, 0) 66%),
    linear-gradient(to left, rgba(7, 12, 28, .72) 0%, rgba(7, 12, 28, .28) 38%, rgba(7, 12, 28, 0) 68%);
  pointer-events: none;
}

.cp-zoom-copy {
  position: relative;
  z-index: 2;
  width: 100%;
  padding: clamp(32px, 5vw, 68px) 0 clamp(38px, 5vw, 72px);
}

.cp-zoom-shell {
  width: 100%;
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 24px;
}

.cp-zoom-inner {
  max-width: 480px;
  margin-left: auto;
}

/* --------------------------------------------------------------------------
   Type and controls — defined here, not inherited from the theme
   -------------------------------------------------------------------------- */
.cp-zoom .cp-zoom-eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-family: var(--cp-display, "Barlow Condensed", sans-serif);
  font-size: .95rem;
  font-weight: 700;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: #fff;
  margin: 0 0 16px;
}

.cp-zoom .cp-zoom-eyebrow::before {
  content: "";
  width: 34px;
  height: 3px;
  background: linear-gradient(90deg, #fff, var(--cp-red, #FE0000));
}

.cp-zoom .cp-zoom-copy h2 {
  font-family: var(--cp-display, "Barlow Condensed", sans-serif);
  font-size: clamp(2rem, 4.2vw, 3.4rem);
  font-weight: 700;
  line-height: 1.06;
  letter-spacing: -.01em;
  text-transform: uppercase;
  color: #fff;
  margin: 0 0 .38em;
}

.cp-zoom .cp-zoom-copy p {
  font-size: 1.05rem;
  line-height: 1.7;
  color: rgba(255, 255, 255, .82);
  margin: 0 0 28px;
  max-width: 52ch;
}

/* The button sits in its own row wrapper so the scroll-keyed reveal below has
   something to move that is not the anchor itself — the anchor's own
   transition and :hover lift both use transform, and a scroll-linked value on
   top of them would fight. Also the natural home for a second button. */
.cp-zoom .cp-zoom-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 14px;
}

/* Three classes deep on purpose: WordPress global styles colour links at
   :root :where(a:where(:not(.wp-element-button))), which outranks a single
   class and would repaint this button's text. */
.cp-zoom .cp-zoom-copy .cp-zoom-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  font-family: var(--cp-display, "Barlow Condensed", sans-serif);
  font-size: 1.08rem;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  line-height: 1.2;
  padding: 17px 32px;
  border: 2px solid transparent;
  border-radius: 4px;
  background: var(--cp-red, #FE0000);
  color: #fff;
  text-decoration: none;
  box-shadow: 0 8px 22px rgba(254, 0, 0, .28);
  transition: background-color .18s ease, transform .18s ease, box-shadow .18s ease;
}

.cp-zoom .cp-zoom-copy .cp-zoom-btn:hover,
.cp-zoom .cp-zoom-copy .cp-zoom-btn:focus-visible {
  background: var(--cp-red-700, #B70000);
  color: #fff;
  transform: translateY(-2px);
  box-shadow: 0 12px 30px rgba(254, 0, 0, .36);
}

/* The site's blue/red signature rule, as used under the hero and CTA band. */
.cp-zoom-bar {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 6px;
  z-index: 3;
  background: linear-gradient(90deg,
    var(--cp-blue, #0026FF) 0%, var(--cp-blue, #0026FF) 50%,
    var(--cp-red, #FE0000) 50%, var(--cp-red, #FE0000) 100%);
}

/* --------------------------------------------------------------------------
   Scrubbing state — JS adds .is-scrub once the video is in play
   -------------------------------------------------------------------------- */
.cp-zoom.is-scrub .cp-zoom-track {
  height: calc(100vh + var(--cp-zoom-scroll));
  height: calc(100svh + var(--cp-zoom-scroll));
}

.cp-zoom.is-scrub .cp-zoom-stage {
  position: sticky;
  top: var(--cp-zoom-top);
  aspect-ratio: auto;
  max-height: none;
  height: calc(100vh - var(--cp-zoom-top));
  height: calc(100svh - var(--cp-zoom-top));
}

/* Swap still to video only once the video can actually be seeked, so the
   section never shows a blank frame mid-load. Both classes are required, so
   dropping out of scrub mode hands the still back immediately. */
.cp-zoom.is-scrub.is-live .cp-zoom-video { opacity: 1; }
.cp-zoom.is-scrub.is-live .cp-zoom-still { opacity: 0; transition: opacity .35s ease; }

/* --------------------------------------------------------------------------
   Scroll-keyed copy reveal (2026-08-14)
   --------------------------------------------------------------------------
   The same treatment the hero gets in cp-hero-zoom.css. The JS publishes its
   eased scroll progress as `--cp-p` (0 -> 1) on every .cp-zoom root, and each
   block of copy declares the slice of that journey it fades up over, so the
   type arrives *with* the camera instead of on a timer of its own.

   Per-element knobs:
     --cp-in   progress at which the block starts moving
     --cp-mul  1 / (out - in), i.e. how fast it completes

   Multiplication rather than division on purpose: dividing by a var() is a
   newer bit of calc() than every browser we support parses reliably.

   Unlike the hero, nothing here plays on page load: this section is well below
   the fold, so every block rides the scroll, eyebrow and headline included.
   The last one lands at 0.60, which the p^0.85 curve puts at about two thirds
   of the clip — the copy is complete while the dolly is still closing in.

   The hero keys its copy through .cp-reveal band classes because its copy is
   real Elementor widgets; here the whole section is one HTML widget, so the
   children of .cp-zoom-inner are addressed directly and the hero is excluded.
   No !important needed either: the .cp-static guard in this file targets
   .elementor-widget, which is an ancestor of these elements, not one of them.
   -------------------------------------------------------------------------- */
.cp-zoom:not(.cp-zoom-hero).is-scrub .cp-zoom-inner > * {
  /* Anything added to this section later reveals with the tail group. */
  --cp-in: .38;
  --cp-mul: 4.55;

  --lp: clamp(0, calc((var(--cp-p) - var(--cp-in)) * var(--cp-mul)), 1);
  /* Ease-out on the way in: fast off the mark, soft on arrival. */
  --e: calc(var(--lp) * (2 - var(--lp)));

  opacity: var(--e);
  transform: translate3d(0, calc((1 - var(--e)) * 30px), 0);
  filter: blur(calc((1 - var(--e)) * 5px));
  /* The JS has already eased this value; a transition layered on top of a
     scroll-linked one reads as the copy lagging behind the camera. */
  transition: none;
}

.cp-zoom:not(.cp-zoom-hero).is-scrub .cp-zoom-inner > .cp-zoom-eyebrow { --cp-in: .06; --cp-mul: 5.00; }
.cp-zoom:not(.cp-zoom-hero).is-scrub .cp-zoom-inner > h2               { --cp-in: .12; --cp-mul: 4.17; }
.cp-zoom:not(.cp-zoom-hero).is-scrub .cp-zoom-inner > p                { --cp-in: .24; --cp-mul: 4.17; }
.cp-zoom:not(.cp-zoom-hero).is-scrub .cp-zoom-inner > .cp-zoom-actions { --cp-in: .38; --cp-mul: 4.55; }

/* Only the blocks still moving late in the scroll are worth a layer. */
.cp-zoom:not(.cp-zoom-hero).is-scrub .cp-zoom-inner > p,
.cp-zoom:not(.cp-zoom-hero).is-scrub .cp-zoom-inner > .cp-zoom-actions {
  will-change: opacity, transform;
}

/* The button holds its layout space the whole time, so it would still take a
   click while invisible. The JS adds .is-copy-live at progress 0.36, which is
   exactly where the button starts to arrive. */
.cp-zoom:not(.cp-zoom-hero).is-scrub:not(.is-copy-live) .cp-zoom-inner > .cp-zoom-actions {
  pointer-events: none;
}

/* --------------------------------------------------------------------------
   Responsive
   -------------------------------------------------------------------------- */
/* Below the scrub threshold there is no pin and no video, so the copy stops
   competing with the picture and stacks underneath it. */
@media (max-width: 960px) {
  .cp-zoom-stage {
    display: block;
    aspect-ratio: auto;
    height: auto;
    max-height: none;
  }

  .cp-zoom-frame {
    position: relative;
    inset: auto;
    aspect-ratio: 4 / 3;
  }

  .cp-zoom-copy { padding: clamp(30px, 6vw, 48px) 0 clamp(36px, 7vw, 56px); }

  .cp-zoom-inner { max-width: none; margin-left: 0; }

  .cp-zoom-scrim {
    background: linear-gradient(to top, rgba(7, 12, 28, .78) 0%, rgba(7, 12, 28, .18) 38%, rgba(7, 12, 28, 0) 70%);
  }
}

@media (max-width: 640px) {
  .cp-zoom-frame { aspect-ratio: 3 / 4; }
  .cp-zoom-shell { padding: 0 20px; }
}

/* Short viewports: shorten the pin so the interaction never drags. */
@media (max-height: 720px) {
  .cp-zoom { --cp-zoom-scroll: 105vh; }
}

@media (prefers-reduced-motion: reduce) {
  .cp-zoom-video,
  .cp-zoom-still { transition: none; }
}
