/*
  Motion system: restrained, decelerated easing (--ease-luxe), generous
  duration, small travel distances. Nothing bounces or overshoots —
  that reads as "luxurious" rather than "playful".
*/

[data-reveal] {
  opacity: 0;
  transform: translateY(24px);
  transition: opacity var(--duration-reveal) var(--ease-luxe),
              transform var(--duration-reveal) var(--ease-luxe);
  /* kept as its own layer: dropping the hint makes the browser build and
     tear down a layer (and redraw the card) every time one reveals, and with
     reveals that replay on every pass that is a lot of redrawing */
  will-change: opacity, transform;
}
[data-reveal].is-visible {
  opacity: 1;
  transform: translateY(0);
}

[data-reveal-scale] {
  opacity: 0;
  transform: scale(0.96);
  transition: opacity var(--duration-slow) var(--ease-luxe),
              transform var(--duration-slow) var(--ease-luxe);
}
[data-reveal-scale].is-visible {
  opacity: 1;
  transform: scale(1);
}

/* stagger children of a [data-reveal-group] via inline --delay set by JS */
[data-reveal] { transition-delay: var(--delay, 0ms); }

.hover-lift {
  transition: transform var(--duration-fast) var(--ease-luxe),
              box-shadow var(--duration-fast) var(--ease-luxe);
}
.hover-lift:hover {
  transform: translateY(-6px);
  box-shadow: var(--shadow-xl);
}

.hover-underline {
  position: relative;
}
.hover-underline::after {
  content: "";
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform var(--duration-base) var(--ease-luxe);
}
.hover-underline:hover::after {
  transform: scaleX(1);
  transform-origin: left;
}

@keyframes fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

.fade-in {
  animation: fade-in var(--duration-slow) var(--ease-luxe) both;
}

/* ==========================================================================
   Text reveal utility — driven by js/text-reveal.js.
   Three modes translated from 21st.dev references, slowed/eased to match
   the site's restrained "luxurious" brief (no bounce, generous duration).
   ========================================================================== */

/* mode: cut — per-character vertical mask wipe */
.tr-cut-word {
  display: inline-block;
  white-space: nowrap;
}
.tr-cut-mask {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
}
.tr-cut-char {
  display: inline-block;
  transform: translateY(110%);
  transition-property: transform;
}
.tr-revealed .tr-cut-char {
  transform: translateY(0);
}

/* mode: blur — faded and offset to opaque and in place. (It used to be a
   real blur as well, but blurring text is one of the heaviest things a
   graphics chip is asked to do; opacity and movement read nearly the same
   and cost next to nothing.) */
.tr-blur {
  display: inline-block;
  opacity: 0;
  transform: translateY(20%);
  transition-property: opacity, transform;
}
.tr-blur.tr-revealed {
  opacity: 1;
  transform: translateY(0);
}

/* mode: slide — unclipped translate + fade, per word or character */
.tr-slide-unit {
  display: inline-block;
  opacity: 0;
  transform: translateY(var(--tr-dist, 20px));
  transition-property: opacity, transform;
}
.tr-revealed .tr-slide-unit {
  opacity: 1;
  transform: translateY(0);
}

/* ==========================================================================
   Scroll-driven reveals. Where the browser can tie an animation to the
   scroll position itself, the reveals are driven by that: they play as the
   element slides into view, run in both directions (so they replay on every
   pass), and are carried out by the graphics chip alone — they stay smooth
   even when the page's own scripts are busy, which time-based transitions
   started from scripts cannot promise. Elsewhere, the transitions above
   apply as before — which is also what iPhones and iPads get, since Apple's
   browsers do not yet carry this kind of animation out on the graphics chip
   as well as Chrome does. (The hero keeps its own timed intro.)
   ========================================================================== */
@supports (animation-timeline: view()) and (not (-webkit-touch-callout: none)) {
  main > section:not(.hero) [data-reveal],
  .site-footer [data-reveal] {
    opacity: 1;
    transform: none;
    transition: none;
    will-change: auto;
    animation: sr-rise linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 55%;
  }
  .services-grid > [data-reveal]:nth-child(2) { animation-range: entry 6% entry 61%; }
  .services-grid > [data-reveal]:nth-child(3) { animation-range: entry 12% entry 67%; }

  [data-reveal-scale] {
    opacity: 1;
    transform: none;
    transition: none;
    animation: sr-scale linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 60%;
  }

  .tr-blur {
    opacity: 1;
    transform: none;
    transition: none;
    animation: sr-rise-text linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 70%;
  }

  /* the portfolio cards settle into their fan (their own transform) */
  .portfolio-fan .portfolio-card {
    opacity: 1;
    transform: translateY(var(--settle-y)) rotate(var(--settle-rotate)) scale(1);
    transition: background-color var(--duration-base) var(--ease-luxe),
                border-color var(--duration-base) var(--ease-luxe);
    animation: sr-card linear both;
    animation-timeline: view();
    animation-range: entry 0% entry 75%;
  }
  .portfolio-fan .portfolio-card:nth-child(2) { animation-range: entry 8% entry 83%; }
  .portfolio-fan .portfolio-card:nth-child(3) { animation-range: entry 16% entry 91%; }

  @keyframes sr-rise {
    from { opacity: 0; transform: translateY(24px); }
    to { opacity: 1; transform: none; }
  }
  @keyframes sr-scale {
    from { opacity: 0; transform: scale(0.96); }
    to { opacity: 1; transform: none; }
  }
  @keyframes sr-rise-text {
    from { opacity: 0; transform: translateY(20%); }
    to { opacity: 1; transform: none; }
  }
  @keyframes sr-card {
    from { opacity: 0; transform: translateY(48px) rotate(calc(var(--settle-rotate) * 3)) scale(0.94); }
  }
}
