/* ==========================================================================
   MOTION LAYER
   Quiet, slow motion: things ease into place and then stay still. Only
   transform / opacity are animated (no CSS filters), and motion.js does not
   run at all under prefers-reduced-motion, so none of the hidden start
   states below apply there.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Headings: words rise out of a mask, one after another
   (motion.js wraps each word in .split-w > .split-w__i)
   -------------------------------------------------------------------------- */
.has-motion .display.is-split {
  opacity: 1;
  transform: none;
  animation: none;
}

.display .split-w,
.hero-home .display .split-w {
  display: inline-block;
  overflow: clip;
  /* room for descenders inside the mask */
  padding: 0.08em 0.04em 0.2em;
  margin: -0.08em -0.04em -0.2em;
}

.display .split-w__i,
.hero-home .display .split-w__i {
  display: inline-block;
  transform: translateY(112%);
  transition: transform 1.25s var(--ease-lux);
  transition-delay: calc(var(--w-i, 0) * 70ms + var(--split-delay, 0ms));
}

.display.is-split-visible .split-w__i {
  transform: none;
}

/* Gradient text does not paint through transformed children, so each word
   of an accent heading carries the gradient itself */
.display .accent .split-w__i {
  background: linear-gradient(120deg, var(--accent) 0%, #8fa8c9 100%);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
}

/* --------------------------------------------------------------------------
   Home hero: the copy drifts up and fades as you scroll away
   (--hero-p: 0 → 1, set by motion.js). The hero image itself never moves or
   resizes with scroll.

   There is deliberately no hand-over between the intro animation and the
   scroll effect — an earlier version switched over on a timer, and anyone
   already scrolling saw the hero jump. Instead both run from the first frame:
   the drift uses `translate` (which composes with the intro's `transform`),
   and opacity is the intro's fade-in multiplied by the scroll fade-out, via
   the registered --hero-intro number.
   -------------------------------------------------------------------------- */
@property --hero-intro {
  syntax: "<number>";
  inherits: false;
  initial-value: 1;
}

@keyframes hero-intro-rise {
  to {
    transform: translateY(0);
  }
}

@keyframes hero-intro-fade {
  to {
    --hero-intro: 1;
  }
}

.has-motion .hero-home > .eyebrow,
.has-motion .hero-home > .display,
.has-motion .hero-home > .lead,
.has-motion .hero-home > .hero-actions {
  translate: 0 calc(var(--hero-p, 0) * -56px);
}

/* Same timing as the page's own heroIn animation (its per-element delays
   still apply), with the fade carried by --hero-intro instead of opacity */
.has-motion .hero-home.hero-anim > .eyebrow,
.has-motion .hero-home.hero-anim > .lead,
.has-motion .hero-home.hero-anim > .hero-actions {
  --hero-intro: 0;
  opacity: calc(var(--hero-intro) * (1 - var(--hero-p, 0) * 1.4));
  animation-name: hero-intro-rise, hero-intro-fade;
}

/* The title is revealed word by word instead, so only the scroll fade applies */
.has-motion .hero-home > .display.is-split {
  opacity: calc(1 - var(--hero-p, 0) * 1.4);
}

/* --------------------------------------------------------------------------
   Service icons draw themselves in as their card arrives, and once more
   when the card is hovered (--stagger is the card's reveal delay)
   -------------------------------------------------------------------------- */
.has-motion .service-card.reveal .icon-draw {
  stroke-dasharray: 1;
  stroke-dashoffset: 1;
  transition: stroke-dashoffset 1.7s var(--ease-lux);
  transition-delay: calc(var(--stagger, 0ms) + 300ms);
}

.has-motion .service-card.reveal.is-visible .icon-draw {
  stroke-dashoffset: 0;
}

@keyframes icon-redraw {
  from {
    stroke-dashoffset: 1;
  }
  to {
    stroke-dashoffset: 0;
  }
}

/* --------------------------------------------------------------------------
   Images settle: photos start slightly zoomed and ease back as they appear
   -------------------------------------------------------------------------- */
.has-motion .portfolio-card.reveal > img,
.has-motion .gallery-item.reveal > img,
.has-motion .about-visual.reveal img {
  scale: 1.14;
  transition:
    scale 1.9s var(--ease-lux),
    transform 1.4s var(--ease-lux),
    filter 0.6s ease;
}

.has-motion .portfolio-card.reveal.is-visible > img,
.has-motion .gallery-item.reveal.is-visible > img,
.has-motion .about-visual.reveal.is-visible img {
  scale: 1;
}

.has-motion .about-visual {
  overflow: hidden;
}

/* --------------------------------------------------------------------------
   Stats: each figure eases up in turn inside the band
   -------------------------------------------------------------------------- */
.has-motion .home-stats-wrap.reveal .stat {
  opacity: 0;
  translate: 0 18px;
  transition: opacity 1s var(--ease-lux), translate 1.2s var(--ease-lux);
}

.has-motion .home-stats-wrap.reveal.is-visible .stat {
  opacity: 1;
  translate: 0 0;
}

.has-motion .home-stats-wrap.reveal .stat:nth-child(1) { transition-delay: 0.1s; }
.has-motion .home-stats-wrap.reveal .stat:nth-child(2) { transition-delay: 0.2s; }
.has-motion .home-stats-wrap.reveal .stat:nth-child(3) { transition-delay: 0.3s; }
.has-motion .home-stats-wrap.reveal .stat:nth-child(4) { transition-delay: 0.4s; }

/* --------------------------------------------------------------------------
   Desktop pointers only: cards ease up into place, and light follows the
   cursor across them. (Touch devices keep the lighter opacity-only reveal
   on glass cards.)
   -------------------------------------------------------------------------- */
@media (hover: hover) and (pointer: fine) {
  .has-motion .service-card.reveal,
  .has-motion .test-card.reveal,
  .has-motion .portfolio-card.reveal {
    transform: translateY(34px);
    transition:
      opacity 1.1s var(--ease-lux),
      transform 1.25s var(--ease-lux),
      box-shadow 0.6s var(--ease-lux);
  }

  .has-motion .service-card.reveal.is-visible,
  .has-motion .test-card.reveal.is-visible,
  .has-motion .portfolio-card.reveal.is-visible {
    transform: none;
  }

  .has-motion .service-card.reveal.is-visible:hover,
  .has-motion .test-card.reveal.is-visible:hover {
    transform: translateY(-4px);
  }

  .has-motion .portfolio-card.reveal.is-visible:hover,
  .has-motion .portfolio-card.reveal.is-visible:focus-visible {
    transform: translate3d(0, -5px, 0);
  }

  /* Hover should answer quickly once the entrance is over */
  .has-motion .service-card.reveal.is-settled,
  .has-motion .test-card.reveal.is-settled,
  .has-motion .portfolio-card.reveal.is-settled {
    transition: transform 0.6s var(--ease-lux), box-shadow 0.6s var(--ease-lux);
  }

  .has-motion .cta-card.reveal {
    transform: translateY(34px);
    transition: opacity 1.1s var(--ease-lux), transform 1.3s var(--ease-lux);
  }

  .has-motion .cta-card.reveal.is-visible {
    transform: none;
  }

  .has-motion .chip.reveal {
    transform: translateY(14px);
    transition:
      opacity 0.9s var(--ease-lux),
      transform 1.1s var(--ease-lux),
      box-shadow 0.45s var(--ease-out),
      background 0.3s,
      border-color 0.3s,
      color 0.3s;
  }

  .has-motion .chip.reveal.is-visible {
    transform: none;
  }

  .has-motion .chip.reveal.is-visible:hover {
    transform: translateY(-2px);
  }

  .has-motion .service-card.is-settled:hover .icon-draw {
    animation: icon-redraw 1.3s var(--ease-lux);
  }

  /* Cursor light: a soft glow inside the card and a brighter edge nearest
     the pointer (--sx / --sy set by motion.js) */
  .glass {
    position: relative;
  }

  .glass::after,
  .portfolio-card::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    background: radial-gradient(
      300px circle at var(--sx, 50%) var(--sy, 50%),
      rgba(255, 255, 255, 0.45),
      transparent 70%
    );
    opacity: 0;
    transition: opacity 0.5s ease;
    pointer-events: none;
  }

  .portfolio-card::after {
    background: radial-gradient(
      340px circle at var(--sx, 50%) var(--sy, 50%),
      rgba(255, 255, 255, 0.22),
      transparent 70%
    );
  }

  html[data-theme="dark"] .glass::after {
    background: radial-gradient(
      300px circle at var(--sx, 50%) var(--sy, 50%),
      rgba(130, 168, 220, 0.16),
      transparent 70%
    );
  }

  .glass::before {
    content: "";
    position: absolute;
    inset: -1px;
    padding: 1px;
    border-radius: inherit;
    background: radial-gradient(
      240px circle at var(--sx, 50%) var(--sy, 50%),
      rgba(61, 110, 168, 0.55),
      transparent 72%
    );
    -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0);
    -webkit-mask-composite: xor;
    mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
    opacity: 0;
    transition: opacity 0.5s ease;
    pointer-events: none;
  }

  html[data-theme="dark"] .glass::before {
    background: radial-gradient(
      240px circle at var(--sx, 50%) var(--sy, 50%),
      rgba(160, 195, 245, 0.7),
      transparent 72%
    );
  }

  .glass:hover::after,
  .glass:hover::before,
  .portfolio-card:hover::after {
    opacity: 1;
  }
}

/* Once an element has finished revealing, its stagger delay must not slow
   down hover feedback */
.reveal.is-settled {
  transition-delay: 0s !important;
}
