/* ==========================================================================
   Motion system — one value per gesture
   --------------------------------------------------------------------------
   These values had accumulated across three separate passes, leaving 47
   distinct durations, seven easing tokens (two of which were the same curve
   under different names) and four different hover-lift distances for what is
   visually one gesture. Everything below now resolves to a single scale, so
   the same effect is the same effect wherever it appears.

   Pick by GESTURE, not by taste:

     colour / opacity only ....... --dur-quick   + ease
     standard hover state ........ --dur-base    + ease
     card & button lift .......... --dur-lift    + --ease-lift
     spreads (rules, arrows) ..... --dur-spread  + --ease-hover
     surfaces that expand ........ --dur-wash    + --ease-wash
     scroll-in entrance .......... --dur-rise    + --ease-rise
   ========================================================================== */
:root {
  --dur-quick: .3s;
  --dur-base: .48s;
  --dur-lift: .62s;
  --dur-spread: .72s;
  --dur-rise: 1.05s;
  --dur-wash: 1.15s;

  --ease: cubic-bezier(.22, 1, .36, 1);
  --ease-rise: cubic-bezier(.33, 1, .68, 1);
  --ease-lift: cubic-bezier(.34, 1.16, .64, 1);
  --ease-hover: cubic-bezier(.28, .08, .22, 1);
  --ease-wash: cubic-bezier(.4, .5, .2, 1);

  /* One lift distance everywhere. Was -3px, -4px, -5px and -7px depending on
     which component you hovered. */
  /* One stagger step for every sequenced reveal. Was 45ms, 110ms and 130ms
     in three different places. */
  --stagger: 90ms;
  /* Held before a colour change so the surface moves first and the text
     follows, rather than both switching at once. */
  --delay-hover: .34s;

  --lift-distance: -4px;
  --rise-distance: 34px;

  /* Legacy names kept as aliases so existing rules keep working. Each now
     points at the canonical curve rather than its own near-duplicate;
     --motion-ease in particular was character-for-character identical to
     --ease. */
  --motion-ease: var(--ease);
  --motion-soft: var(--ease-wash);
  --hover-ease: var(--ease-hover);
  --rise-ease: var(--ease-rise);
  --lift-ease: var(--ease-lift);
  --wash-ease: var(--ease-wash);
  --hv-quick: var(--dur-quick);
  --hv-base: var(--dur-base);
  --hv-spread: var(--dur-spread);
}

.motion-progress {
  position: fixed;
  z-index: 22000;
  inset: 0 0 auto;
  height: 2px;
  pointer-events: none;
}
.motion-progress i {
  width: 100%;
  height: 100%;
  display: block;
  background: #c49a56;
  transform: scaleX(var(--scroll-progress, 0));
  transform-origin: left;
  will-change: transform;
}

/* The curtain crosses the document boundary in two matched halves. The old
   page brings it up from below; the next page is already covered on its first
   paint, then releases the same surface upward. */
.motion-curtain {
  position: fixed;
  z-index: 21000;
  inset: 0;
  pointer-events: none;
  background: #171713;
  transform: translate3d(0, 101%, 0);
  transition: transform var(--dur-base) var(--ease-wash);
  will-change: transform;
}
body.motion-leaving .motion-curtain { transform: translate3d(0, 0, 0); }

html.motion-arriving::before {
  position: fixed;
  z-index: 21000;
  inset: 0;
  pointer-events: none;
  background: #171713;
  content: "";
  transform: translate3d(0, 0, 0);
  will-change: transform;
}
html.motion-arriving.motion-arrival-release::before {
  transform: translate3d(0, -101%, 0);
  transition: transform var(--dur-base) var(--ease-wash);
}

.site-header{ animation: none; }

/* Below 820px the mobile menu is a full-screen `position: fixed; inset: 0`
   panel that lives inside the header. motionHeaderIn animates transform and,
   with fill-mode both, leaves one behind — and any non-none transform makes
   that element the containing block for its fixed descendants. So the panel
   sized itself against the 70px header instead of the 812px viewport, ended
   up 136px tall with overflow-y:auto, and hid 10 of its 11 links. A header
   entrance is not worth an unusable menu on phones. */
@media (max-width: 820px) {
  .site-header{ animation: none; }
}

.motion-item {
  opacity: 0;
  filter: blur(5px);
  translate: 0 30px;
  transition:
    opacity var(--dur-rise) var(--ease) var(--motion-delay, 0ms),
    transform var(--dur-rise) var(--ease) var(--motion-delay, 0ms),
    translate var(--dur-rise) var(--ease) var(--motion-delay, 0ms),
    filter var(--dur-rise) var(--ease) var(--motion-delay, 0ms);
  will-change: opacity, translate, filter;
}
.motion-item.is-visible {
  opacity: 1;
  filter: blur(0);
  translate: 0 0;
}

.motion-headline { opacity: 1 !important; transform: none !important; }
.motion-line { display: block; overflow: hidden; padding-bottom: .06em; margin-bottom: -.06em; }
.motion-line > span {
  display: block;
  opacity: 0;
  transform: translate3d(0, 112%, 0) rotate(.8deg);
  transform-origin: left bottom;
  transition: opacity var(--dur-quick) ease, transform var(--dur-lift) var(--ease);
  transition-delay: calc(var(--motion-line, 0) * var(--stagger) + var(--stagger));
  will-change: transform;
}
.motion-headline.is-animated .motion-line > span { opacity: 1; transform: translate3d(0, 0, 0) rotate(0); }

.motion-media {
  clip-path: inset(0 100% 0 0);
  transition: clip-path var(--dur-wash) var(--ease-wash);
  will-change: clip-path;
}
.motion-media.is-visible { clip-path: inset(0 0 0 0); }
/* News cards already reveal as a whole. Never independently conceal their
   images (also covers stale cached scripts that still add motion-media). */
.insight-image.motion-media, .article-card-image.motion-media { clip-path: none !important; }
.motion-media img {
  transform: scale(1.075);
  transition: transform 1.45s var(--ease) !important;
  will-change: transform;
}
.motion-media.is-visible img { transform: scale(1); }
.insight-card:hover .motion-media.is-visible img,
.article-card:hover .motion-media.is-visible img{ transform: scale(1.04); }

.hero-media img,
.inner-hero-media img {
  transform: translate3d(0, var(--motion-parallax, 0px), 0) scale(1.065);
  will-change: transform;
}
.hero-media.motion-media.is-visible img,
.inner-hero-media.motion-media.is-visible img {
  transform: translate3d(0, var(--motion-parallax, 0px), 0) scale(1.065);
}

.motion-spotlight { position: relative; isolation: isolate; overflow: hidden; }
.motion-spotlight::after {
  content: "";
  position: absolute;
  z-index: 0;
  inset: 0;
  pointer-events: none;
  background: linear-gradient(135deg, rgba(196,154,86,.14), rgba(196,154,86,.045) 62%, rgba(255,255,255,.045));
  opacity: 0;
  /* The growing disc that used to live here is gone; the glow now simply
     fades, and its size and colour are set in the Softening pass below. */
}
.motion-spotlight:hover::after {
  opacity: 1;
}
.motion-spotlight > * { position: relative; z-index: 1; }

.button,
.header-cta,
.floating-consult{
  translate: var(--magnet-x, 0px) calc(var(--magnet-y, 0px) + var(--hover-lift, 0px));
  transition-property: color, background-color, border-color, box-shadow, translate;
  transition-duration: var(--dur-base), var(--dur-base), var(--dur-base), var(--dur-lift), var(--dur-lift);
  transition-timing-function: ease, ease, ease, ease, var(--ease);
}
.button.motion-item.is-visible {
  translate: var(--magnet-x, 0px) calc(var(--magnet-y, 0px) + var(--hover-lift, 0px));
}

.floating-consult::after{
  content: "";
  position: absolute;
  inset: -7px;
  border: 1px solid rgba(196,154,86,.44);
  border-radius: inherit;
  animation: motionConsultPulse 3.8s ease-out infinite;
  pointer-events: none;
}

.proof-item strong.counting { color: #d0aa67; }

/* Per-component hover states. The shared rules live in the Hover system
   section at the end of this file. */
  .primary-nav a::after{
    transform-origin: center;
    transition-duration: var(--dur-spread);
    transition-timing-function: var(--ease-hover);
  }

  .button,
  .header-cta,
  .floating-consult{
    position: relative;
    isolation: isolate;
    overflow: hidden;
    transition: color var(--dur-base) ease, background-color var(--dur-spread) ease, border-color var(--dur-spread) ease, box-shadow var(--dur-lift) var(--ease-hover), translate var(--dur-lift) var(--ease-hover);
  }
  .button::before,
  .header-cta::before,
  .floating-consult::before{
    content: "";
    position: absolute;
    z-index: -1;
    inset: 0;
    pointer-events: none;
    background: var(--hover-wash, linear-gradient(115deg, rgba(255,255,255,.04), rgba(255,255,255,.16)));
    opacity: 0;
    clip-path: circle(0% at var(--spot-x, 50%) var(--spot-y, 50%));
    transition: opacity var(--dur-base) ease, clip-path var(--dur-wash) var(--ease-hover);
  }
  .button:hover::before,
  .header-cta:hover::before,
  .floating-consult:hover::before{
    opacity: 1;
    clip-path: circle(150% at var(--spot-x, 50%) var(--spot-y, 50%));
  }
  .button:hover,
  .header-cta:hover {
    --hover-lift: var(--lift-distance);
    transform: none;
    box-shadow: 0 22px 46px rgba(17,17,14,.12);
  }
  
  .button-gold { --hover-wash: linear-gradient(115deg, rgba(255,255,255,.02), rgba(255,245,211,.42)); }
  .button-gold:hover { background: var(--gold); }
  
  
  .button-dark { --hover-wash: linear-gradient(115deg, rgba(196,154,86,.07), rgba(255,255,255,.11)); }
  .button-dark:hover { background: var(--ink); }
  .header-cta{ --hover-wash: linear-gradient(115deg, #b98a3d, #d9b875); }
  .floating-consult{ --hover-wash: radial-gradient(circle, rgba(255,250,231,.48), rgba(255,250,231,.08) 68%); }
  .header-cta:hover { background: var(--gold); }

  .text-link {
    background-image: linear-gradient(90deg, currentColor, currentColor);
    background-repeat: no-repeat;
    background-position: center bottom;
    background-size: 0 1px;
    transition: gap var(--dur-spread) var(--ease-hover), color var(--dur-base) ease .26s, background-size var(--dur-spread) var(--ease-hover), border-color var(--dur-base) ease;
  }
  .text-link:hover { gap: 20px; color: #8d672b; background-size: 100% 1px; border-bottom-color: transparent; }
  .text-link-light:hover { color: var(--gold-light); }

  
  
  
  
  
  
  

  .solution-row { transition: color var(--dur-base) ease, padding var(--dur-spread) var(--ease-hover), border-color var(--dur-spread) ease; }
  .solution-row:hover { transition-delay: .3s, 0s, 0s; }
  .solution-row:hover { padding-left: 12px; border-color: rgba(217,184,117,.5); }

  .process-step {
    transition: box-shadow var(--dur-lift) var(--ease-hover), transform var(--dur-lift) var(--ease-hover);
  }
  .process-step:hover { background: var(--ivory); box-shadow: 0 22px 48px rgba(25,24,19,.06), inset 0 0 0 1px rgba(185,138,61,.12); transform: translateY(var(--lift-distance)); }

  .format-card {
    transition: transform var(--dur-lift) var(--ease-hover), box-shadow var(--dur-lift) var(--ease-hover);
  }

  .insight-card,
  .article-card {
    transition: transform var(--dur-lift) var(--ease-hover), box-shadow var(--dur-lift) var(--ease-hover), border-color var(--dur-spread) ease;
  }
  .insight-card:hover,
  .article-card:hover { transform: translateY(var(--lift-distance)); box-shadow: 0 24px 54px rgba(25,24,19,.075); border-color: rgba(185,138,61,.32); }
  .insight-card:hover .motion-media.is-visible img,
  .article-card:hover .motion-media.is-visible img{ transform: scale(1.025); }

  .news-filter button {
    background-image: radial-gradient(circle, rgba(185,138,61,.11), rgba(185,138,61,.035) 54%, transparent 72%);
    background-position: center;
    background-repeat: no-repeat;
    background-size: 0 0;
    transition: color var(--dur-base) ease .28s, background-size var(--dur-spread) var(--ease-hover), border-color var(--dur-spread) ease;
  }
  .news-filter button:not(.is-active):hover { color: #8d672b; background-color: transparent; background-size: 260% 260%; border-color: rgba(141,103,43,.45); }

  .footer-nav a{ transition: color var(--dur-base) ease; }
  .floating-consult:hover{
    --hover-lift: var(--lift-distance);
    transform: none;
    background: var(--gold);
    box-shadow: 0 28px 60px rgba(0,0,0,.15);
  }

  
  
  
  
  
  

@keyframes motionHeaderIn {
  from { opacity: 0; transform: translate3d(0, -22px, 0); }
  to { opacity: 1; transform: translate3d(0, 0, 0); }
}
@keyframes motionConsultPulse {
  0%, 46% { opacity: 0; transform: scale(.9); }
  62% { opacity: .9; }
  88%, 100% { opacity: 0; transform: scale(1.18); }
}

@media (max-width: 820px) {
  .hero-media img,
  .inner-hero-media img,
  .hero-media.motion-media.is-visible img,
  .inner-hero-media.motion-media.is-visible img { transform: scale(1.035); }
  .motion-item { filter: none; translate: 0 20px; }
  .motion-sequence > * { transition-delay: calc(var(--motion-index, 0) * var(--stagger)); }
}

@media (prefers-reduced-motion: reduce) {
  .motion-progress,
  .motion-curtain { display: none !important; }
  .site-header,
  .floating-consult::after{ animation: none !important; }
  .motion-item,
  .motion-line > span,
  .motion-media,
  .motion-media img,
  .motion-sequence > *,
  .process-grid,
  .research-pillars {
    opacity: 1 !important;
    filter: none !important;
    translate: none !important;
    transform: none !important;
    clip-path: none !important;
  }
  html.motion-arriving::before { display: none !important; }
}

/* ==========================================================================
   Hover system
   --------------------------------------------------------------------------
   Two problems this section solves.

   1. The nav underline never actually grew from the centre. The base rule in
      premium-site.css pinned transform-origin to `right` and the :hover rule
      flipped it to `left`, so the line wiped in from the left edge and then
      retracted to the right - two different motions for the same gesture.
      Both states now sit on `center`, and that centre-out line is reused as
      the shared idiom for every text link on the site.

   2. Roughly half the interactive surfaces had no hover state at all - the
      stat cells, the about/service/research cards, the timeline and quality
      rows, the logo, the scroll cue, the footer links, form fields, and every
      link inside the 43 legacy WordPress pages. They now all transition.

   Durations are deliberately three values, not eighteen: colour changes are
   quick, transforms sit in the middle, and anything that travels across a
   surface (an underline, a wash) takes the long one.
   ========================================================================== */

/* --hv-quick / --hv-base / --hv-spread are now defined once, with the rest of
   the motion scale, at the top of this file. */

/* --------------------------------------------------------------------------
   1. Centre-out underline
   -------------------------------------------------------------------------- */
.primary-nav a::after{
  transform-origin: center !important;
}
.primary-nav a:hover::after,
.primary-nav a:focus-visible::after{
  transform: scaleX(1);
}
.primary-nav a{ transition: color var(--dur-quick) ease; }
.primary-nav a:hover,
.primary-nav a:focus-visible { color: #d9b875; }

/* The same line, applied to links that previously only changed colour. The
   pseudo-element is added here rather than in the base sheets so the two
   footers (rebuilt and legacy) stay in step. */
.footer-nav a,
.contact-detail strong a,
.story-copy a {
  position: relative;
  display: inline-block;
  transition: color var(--dur-quick) ease;
}
.footer-nav a::after,
.contact-detail strong a::after,
.story-copy a::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: -2px;
  left: 0;
  height: 1px;
  background: currentColor;
  opacity: .6;
  transform: scaleX(0);
  transform-origin: center;
  transition: transform var(--dur-spread) var(--ease-hover);
}
.footer-nav a:hover::after,
.footer-nav a:focus-visible::after,
.contact-detail strong a:hover::after,
.story-copy a:hover::after { transform: scaleX(1); }

.contact-detail strong a:hover { color: #8d672b; }

/* --------------------------------------------------------------------------
   2. Header furniture - logo, menu button, scroll cue
   -------------------------------------------------------------------------- */
.brand{ transition: opacity var(--dur-base) ease; }
.brand:hover,
.brand:focus-visible{ opacity: .78; }

.menu-toggle{
  cursor: pointer;
  transition: color var(--dur-quick) ease;
}
.menu-toggle:hover{ color: #d9b875; }
.menu-toggle-lines i{ transition: transform var(--dur-quick) var(--ease), background-color var(--dur-quick) ease; }

.hero-scroll { transition: color var(--dur-base) ease, gap var(--dur-spread) var(--ease-hover); }
.hero-scroll:hover,
.hero-scroll:focus-visible { color: #fff; gap: 20px; }
.hero-scroll i { transition: width var(--dur-spread) var(--ease-hover); }
.hero-scroll:hover i { width: 64px; }

/* --------------------------------------------------------------------------
   3. Stat cells
   Not links, but they are the first grid on the page and sat completely
   inert. A wash plus the same centre-out rule ties them to the nav.
   -------------------------------------------------------------------------- */
.proof-item {
  position: relative;
  transition: background-color var(--dur-spread) ease;
}
.proof-item::after {
  content: "";
  position: absolute;
  right: 0;
  bottom: -1px;
  left: 0;
  height: 2px;
  background: #b98a3d;
  transform: scaleX(0);
  transform-origin: center;
  transition: transform var(--dur-spread) var(--ease-hover);
}
.proof-item:hover { background-color: rgba(255,255,255,.32); }
.proof-item:hover::after { transform: scaleX(1); }
.proof-item span { transition: color var(--dur-base) ease; }
.proof-item:hover span { color: #4a463f; }

/* --------------------------------------------------------------------------
   4. Cards that only had the spotlight wash
   premium-motion.js tags these with .motion-spotlight, which fades a gradient
   over them, but none of them moved. A short lift and a heading colour change
   give the wash something to sit on.
   -------------------------------------------------------------------------- */
.certification-card,
.value-card,
.service-overview-card {
  transition: transform var(--dur-spread) var(--ease-hover),
              box-shadow var(--dur-spread) ease,
              border-color var(--dur-base) ease;
}
/* Every ruled cell lifts the same way and picks up a gold rule; the heading
   takes the kicker colour, which is gold-dark on light ground and
   gold-light on dark, so the hover reads on either. */
.certification-card:hover,
.format-card:hover,
.research-pillar:hover,
.value-card:hover,
.service-overview-card:hover {
  transform: translateY(var(--lift-distance));
  box-shadow: 0 24px 52px rgba(25,24,19,.07);
  border-color: rgba(185,138,61,.34);
}
.value-card h3,
.service-overview-card h3,
.research-pillar h3,
.certification-card strong { transition: color var(--dur-base) ease; }
.value-card:hover h3,
.service-overview-card:hover h3,
.format-card:hover h3,
.research-pillar:hover h3,
.certification-card:hover strong { color: var(--cell-kicker); }

.research-pillar {
  transition: background-color var(--dur-spread) ease,
              transform var(--dur-spread) var(--ease-hover);
}

/* --------------------------------------------------------------------------
   5. List rows - timeline and quality list
   Same gesture as .solution-row on the homepage, which already had it.
   -------------------------------------------------------------------------- */
.timeline li,
.quality-row {
  transition: padding-left var(--dur-spread) var(--ease-hover),
              border-color var(--dur-base) ease,
              color var(--dur-base) ease;
}
.timeline li:hover { padding-left: 14px; border-bottom-color: rgba(217,184,117,.5); }
.quality-row:hover { padding-left: 14px; border-bottom-color: rgba(185,138,61,.45); }
.timeline strong,
.quality-row strong { transition: color var(--dur-base) ease; }
.timeline li:hover strong { color: #d9b875; }
.quality-row:hover strong { color: #8d672b; }

/* --------------------------------------------------------------------------
   6. Imagery
   .split-feature-media had overflow visible and no hover at all; the two card
   images had a transition declared but nothing to trigger it without JS.
   -------------------------------------------------------------------------- */
.split-feature-media { overflow: hidden; }
.split-feature:hover .split-feature-media img,
.split-feature:hover .motion-media.is-visible img { transform: scale(1.035); }
.insight-card:hover .insight-image img,
.article-card:hover .article-card-image img { transform: scale(1.025); }

/* --------------------------------------------------------------------------
   7. Arrows
   -------------------------------------------------------------------------- */
.solution-row i { transition: transform var(--dur-spread) var(--ease-hover), opacity var(--dur-base) ease; }
.solution-row:hover i { opacity: 1; transform: translate(3px,-3px); }
.button span[aria-hidden],
.header-cta span[aria-hidden] { transition: transform var(--dur-spread) var(--ease-hover); }
.button:hover span[aria-hidden],
.header-cta:hover span[aria-hidden] { transform: translate(2px,-2px); }

/* --------------------------------------------------------------------------
   8. News filter and forms
   -------------------------------------------------------------------------- */
.news-filter button { cursor: pointer; }
.news-filter button.is-active {
  transition: background-color var(--dur-base) ease, border-color var(--dur-base) ease;
}
.news-filter button.is-active:hover { background-color: #35342c; border-color: #35342c; }

.form-field label { transition: color var(--dur-base) ease; }
.form-field:focus-within label { color: #8d672b; }
.form-field input,
.form-field select,
.form-field textarea {
  transition: border-color var(--dur-base) ease,
              background-color var(--dur-spread) ease;
}
.form-field input:hover,
.form-field select:hover,
.form-field textarea:hover { border-bottom-color: #6f685c; }

/* --------------------------------------------------------------------------
   9. Legacy WordPress pages
   43 of the 49 pages are the original theme markup inside the premium shell.
   Body links there had no hover treatment of any kind.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   10. Keyboard parity
   Nothing on the site drew a focus ring, so a keyboard user got none of the
   above. :focus-visible only fires for keyboard interaction, so this stays
   invisible to mouse users.
   -------------------------------------------------------------------------- */
a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible {
  outline: 2px solid #c49a56;
  outline-offset: 3px;
}

/* --------------------------------------------------------------------------
   11. Card tilt
   premium-motion.js drives the rotation on .insight-card and .article-card;
   these two classes decide what is transitioned at each stage. The split
   matters: while the card tracks the pointer, transform must NOT be
   transitioned, or it trails the cursor by the transition's own duration. The
   slow return on leave is where the site's unhurried feel actually reads.

   The tilt is only wired up when the visitor has not asked for reduced motion,
   so neither class ever appears in that case - the plain translateY(-4px)
   hover further up stays in force for reduced-motion, touch and no-JS.
   -------------------------------------------------------------------------- */
.tilt-card { transform-style: preserve-3d; }

.tilt-card.is-tilting {
  will-change: transform;
  transition: box-shadow var(--dur-lift) var(--ease-hover), border-color var(--dur-spread) ease;
}

.tilt-card.is-returning {
  transition: transform var(--dur-spread) var(--ease),
              box-shadow var(--dur-lift) var(--ease-hover),
              border-color var(--dur-spread) ease;
}

/* --------------------------------------------------------------------------
   12. Softening pass
   Everything above this point moved correctly but landed hard. Three separate
   causes, all of which read to the eye as "stiff":

   a) --motion-ease is cubic-bezier(.22, 1, .36, 1). Its first control point
      reaches y=1 at x=.22, so roughly 80% of the travel happens in the first
      quarter of the duration and the element then crawls the last few pixels.
      That lurch-then-hang is the stiffness in every rise on the site.

   b) The button lift and the cursor magnet were both written onto `translate`,
      so they shared one transition. The magnet is a pointermove tracker, but
      it was inheriting the lift's ~1.1s easing and dragging a full second
      behind the cursor.

   c) The card washes grow with clip-path. clip-path is applied after filter in
      the rendering order, so the circle's edge is always a hard cut and no
      amount of blur softens it. A mask does feather, and mask-size animates.
   -------------------------------------------------------------------------- */

/* The three curves this pass introduced are now --ease-rise, --ease-lift and
   --ease-wash, defined once at the top of this file:

     --ease-rise  easeOutCubic. Same family as the original, but the tail is
                  four times longer, so movement stays visible instead of
                  finishing in the first few frames.
     --ease-lift  Overshoots by ~4% and settles back. On a 4px lift that is
                  under a fifth of a pixel past the mark — not seen, but felt
                  as give rather than as a stop.
     --ease-wash  Slow in, slow out, for surfaces that spread rather than move.
*/

/* --- a) Rises ----------------------------------------------------------- */
.motion-item {
  translate: 0 34px;
  filter: blur(4px);
  transition:
    opacity var(--dur-rise) var(--ease-rise) var(--motion-delay, 0ms),
    transform var(--dur-lift) var(--ease-rise) var(--motion-delay, 0ms),
    translate var(--dur-lift) var(--ease-rise) var(--motion-delay, 0ms),
    filter var(--dur-rise) var(--ease-rise) var(--motion-delay, 0ms);
}

/* The headline lines faded in over .2s while still sliding for another
   second, so the text was fully opaque before most of the movement had
   happened and the slide read as a separate, mechanical event. */
.motion-line > span {
  transform: translate3d(0, 108%, 0) rotate(.6deg);
  transition: opacity var(--dur-spread) var(--ease-rise), transform var(--dur-wash) var(--ease-rise);
  transition-delay: calc(var(--motion-line, 0) * var(--stagger) + var(--stagger));
}

/* --- b) Button lift and magnet, split apart ----------------------------- */
.button,
.header-cta,
.floating-consult{
  translate: 0 var(--hover-lift, 0px);
  transform: translate3d(var(--magnet-x, 0px), var(--magnet-y, 0px), 0);
  transition: color var(--dur-base) ease,
              background-color var(--dur-spread) ease,
              border-color var(--dur-spread) ease,
              box-shadow var(--dur-spread) var(--ease-wash),
              translate var(--dur-lift) var(--ease-lift),
              transform .22s ease-out;
}

/* The earlier rules set `transform: none` on hover to stop the lift and the
   magnet fighting; now that they live on separate properties the magnet has
   to survive the hover. */
.button:hover,
.header-cta:hover,
.floating-consult:hover{
  transform: translate3d(var(--magnet-x, 0px), var(--magnet-y, 0px), 0);
}

/* `translate` is also what the entrance rise uses, and the rule above would
   otherwise cancel it for buttons that are themselves reveal targets. */
.button.motion-item:not(.is-visible),
.header-cta.motion-item:not(.is-visible) { translate: 0 34px; }
.button.motion-item.is-visible { translate: 0 var(--hover-lift, 0px); }

/* --- c) Washes ---------------------------------------------------------- */
/* The dark surface rises from the bottom edge, which is what premium-site.css
   originally did with transform-origin:bottom before the motion layer replaced
   it with an expanding clip-path circle. The circle was the wrong shape for
   this: a disc opening in the middle of a rectangular card reads as an object
   sitting on the card rather than as the card changing state, and softening
   its edge only made it rounder still.

   A linear-gradient mask keeps the soft edge but gives it a direction. --wash
   is the height the solid fill has reached; the transparent stop sits 34%
   above it, so the boundary is always a gradient rather than a line. It starts
   at -34% so that at rest the transparent stop is exactly at the bottom edge
   and nothing shows.

   Growing a gradient stop needs --wash registered, or it is a plain token and
   cannot be interpolated. Where @property is unsupported the var() fallbacks
   keep the mask valid and the hover snaps instead of sweeping. */
@property --wash {
  syntax: "<percentage>";
  inherits: false;
  initial-value: -34%;
}

/* The gold spotlight on the other cards stops resizing and simply fades. It is
   a light source, not a shape, so a large soft ellipse centred on the pointer
   at a fixed size reads as the card catching light; growing it turned the same
   gradient into a second travelling disc. */
.motion-spotlight::after {
  clip-path: none;
  -webkit-mask-image: none;
  mask-image: none;
  background: radial-gradient(58% 78% at var(--spot-x, 50%) var(--spot-y, 50%),
              rgba(196,154,86,.17),
              rgba(196,154,86,.06) 44%,
              rgba(196,154,86,0) 74%);
  opacity: 0;
  transition: opacity .95s var(--ease-wash);
}
.motion-spotlight:hover::after { opacity: 1; }

/* --- d) Grids whose rules are the container's own background ------------- */
/* .process-grid (the 8 project steps, and the 4 on the contact page) and
   .research-pillars draw their hairlines as the container's background showing
   through 1px gaps, instead of as borders on the cells the way every other
   grid here does. That is invisible at rest, but the cells are reveal targets:
   at opacity 0 they stop covering the container, so the whole grid reads as a
   dark slab that then fades out from under them. Every other grid on the site
   has a transparent container, which is exactly why only these two looked
   wrong. They rise as one block instead of cell by cell. */
.process-grid,
.research-pillars {
  opacity: 0;
  translate: 0 34px;
  filter: blur(4px);
  transition: opacity var(--dur-rise) var(--ease-rise),
              translate var(--dur-lift) var(--ease-rise),
              filter var(--dur-rise) var(--ease-rise);
}
.process-grid.is-visible,
.research-pillars.is-visible {
  opacity: 1;
  translate: none;
  filter: blur(0);
}
.process-grid > .motion-item,
.research-pillars > .motion-item {
  opacity: 1 !important;
  translate: none !important;
  filter: none !important;
  transition-delay: 0s !important;
}

/* The small button washes keep clip-path - at that size the edge is a few
   pixels of travel - but they get the same unhurried curve. */
.button::before,
.header-cta::before,
.floating-consult::before{
  transition: opacity var(--dur-base) var(--ease-wash), clip-path var(--dur-wash) var(--ease-wash);
}

/* Card lifts pick up the same give as the buttons. */
.process-step,
.format-card,
.certification-card,
.value-card,
.service-overview-card,
.research-pillar {
  transition: color var(--dur-base) ease,
              background-color var(--dur-spread) var(--ease-wash),
              border-color var(--dur-base) ease,
              box-shadow var(--dur-spread) var(--ease-wash),
              transform var(--dur-lift) var(--ease-lift);
}

.insight-card,
.article-card {
  transition: transform var(--dur-lift) var(--ease-lift),
              box-shadow var(--dur-spread) var(--ease-wash),
              border-color var(--dur-base) ease;
}
.tilt-card.is-returning {
  transition: transform var(--dur-lift) var(--ease-lift),
              box-shadow var(--dur-spread) var(--ease-wash),
              border-color var(--dur-base) ease;
}

/* A short fade on arrival. Without it the new page snaps in the instant the
   curtain clears, which reads as a second flash right after the first.
   Deliberately opacity-only and CSS-driven: no transform, so it cannot shift
   layout, and it still runs if the motion script fails to load. */
body.premium-site{
  animation: motionPageIn var(--dur-base) var(--ease-rise) both;
}

@keyframes motionPageIn {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* Leaving pages must not fade out as well — the curtain is doing that job,
   and two overlapping fades read as a stutter. */
body.motion-leaving { animation: none; }

@media (prefers-reduced-motion: reduce) {
  body.premium-site{ animation: none !important; }
}
