/* motion.css — H4: first-load choreography (loader + hero), Methodology letter reveal,
   captured hovers (pill text roll, email / Book a Call letter reveal, touch gating), custom cursor.
   Every number cites docs/reports/H0.md (measurements of the current live site).
   Rule of the file: nothing here animates under prefers-reduced-motion, and nothing that hides
   content applies unless JavaScript is running (html.hwg-js is set by base.njk's inline head script;
   the Methodology hidden state additionally waits for reveal.js — html.hwg-reveal). */

/* ---------- shared: one letter of a letter-by-letter reveal ----------
   H0 §6.3.1 / §6.1 / §6.5: per letter opacity .001 → 1 and blur 10 px → 0 together, ≈167 ms from the
   first change to ≥ .99 (180–185 ms end to end), 50 ms stagger, ease-out with a soft start
   (fits cubic-bezier(.22,−.08,.44,.9)); 185 ms here measures ≈170 ms first-change → ≥ .99, the
   same way. --i = the letter's index, spaces take no slot. */
@keyframes hwg-letter {
  from { opacity: 0.001; filter: blur(10px); }
  to { opacity: 1; filter: blur(0); }
}

/* ---------- first-load intro (home only) ----------
   H0 §6.1 — the inline appear manifest of live, played by CSS from the first frame:
   loader panels  Top +2000 → 0 (leaves upward), Bottom −2000 → 0 (leaves downward),
                  delay .6 s, 1.8 s, cubic-bezier(.44,0,.56,1)
   loader letters "Here We Go." ×2 (both halves of the seam), 50 ms stagger; on live the first letter
                  starts with the panels (H0: letters − panels = +11 / −67 / 0 ms at 1440 / 768 / 390,
                  first letter ≈ 0.71–0.78 s after navigation start), so here .58 s, just before the
                  panels' .6 s
   hero           translateY 60 → 0 and scale .95 → 1, delay .6 s, 1.8 s, same ease
   nav            H3 (nav.css): translateY −60 → 0, delay .8 s, 1.8 s, same ease
   Everything is at rest by ≈2.5 s (live: ≈2.6 s). The loader never shows with JS off, and under
   reduced motion it is skipped and the hero is at identity from the first frame. */
@media (prefers-reduced-motion: no-preference) {
  html.hwg-js [data-hwg-loader][hidden] {
    display: block;
    animation: hwg-loader-done 0s linear 2.45s forwards;
  }
  html.hwg-js [data-hwg-loader] .framer-1p9uetg {
    animation: hwg-loader-top 1.8s cubic-bezier(0.44, 0, 0.56, 1) 0.6s both;
  }
  html.hwg-js [data-hwg-loader] .framer-prhx8m {
    animation: hwg-loader-bottom 1.8s cubic-bezier(0.44, 0, 0.56, 1) 0.6s both;
  }
  html.hwg-js [data-hwg-loader] [data-letter] {
    animation: hwg-letter 185ms cubic-bezier(0.22, -0.08, 0.44, 0.9) both;
    animation-delay: calc(0.58s + var(--i, 0) * 50ms);
  }
  html.hwg-js [data-page-type="home"] [data-framer-appear-id="p5os0b"] {
    animation: hwg-hero-in 1.8s cubic-bezier(0.44, 0, 0.56, 1) 0.6s both;
  }
}
@keyframes hwg-loader-top {
  from { transform: translateY(2000px); }
  to { transform: none; }
}
@keyframes hwg-loader-bottom {
  from { transform: translateY(-2000px); }
  to { transform: none; }
}
@keyframes hwg-loader-done {
  to { visibility: hidden; }
}
@keyframes hwg-hero-in {
  from { transform: translateY(60px) scale(0.95); }
  to { transform: none; }
}

/* ---------- Methodology: step names reveal letter by letter, once ----------
   H0 §6.3.1. reveal.js arms the hidden state (html.hwg-reveal) and starts each step name when ≈10 px
   of it is uncovered by its rising card. The card rise itself is set inline by reveal.js (§6.3.2). */
@media (prefers-reduced-motion: no-preference) {
  html.hwg-reveal [data-section="methodology"] h3 [data-letter] {
    opacity: 0.001;
    filter: blur(10px);
  }
  html.hwg-reveal [data-section="methodology"] h3.hwg-letters-in [data-letter] {
    animation: hwg-letter 185ms cubic-bezier(0.22, -0.08, 0.44, 0.9) both;
    animation-delay: calc(var(--i, 0) * 50ms);
  }
}

/* ---------- hovers (fine pointers only; nothing under reduced motion) ---------- */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  /* Pill buttons ("Let's GO!", "Let's talk!", service CTAs, "See Our Full Process", 404 "Go back
     Home") — H0 §6.5 pill-button / pill-button-cta: the resting label fades 1 → 0 while its twin
     fades 0 → 1 (199 ms, fit (.18,−.2,.34,.98); back in 201 ms on leave) and the twin's letters
     reveal one by one (first at ≈43 ms, 50 ms apart, 166 ms each). The twin sits exactly over the
     label (framer.css: absolute, right 24 px, top 50 %, translateY(−50 %)). */
  .framer-EYLHz .framer-1j4f0is,
  .framer-EYLHz .framer-1oaobln {
    transition: opacity 200ms cubic-bezier(0.18, -0.2, 0.34, 0.98);
  }
  .framer-EYLHz:hover .framer-1j4f0is { opacity: 0 !important; }
  .framer-EYLHz:hover .framer-1oaobln { opacity: 1 !important; }
  .framer-EYLHz:hover .framer-1oaobln [data-letter] {
    animation: hwg-letter 185ms cubic-bezier(0.2, -0.1, 0.44, 0.88) both;
    animation-delay: calc(var(--i, 0) * 50ms);
  }

  /* Footer / overlay email + "Book a Call" (H3's .hwg-line-link; H3 draws the underline) —
     H0 §6.5 footer-email / footer-book-call: on hover the text is swapped for a letter-split copy
     whose letters reveal left to right (25 / 9 letters, first at ≈42 ms, 50 ms apart, 166 ms each;
     1.41 s / 0.61 s in total); on leave the plain text returns at once. reveal.js splits the text
     on pointerenter and restores it on pointerleave (live's hover copy is also inline-block letters,
     +3 px wide), so the resting text is never split. */
  .hwg-line-link.hwg-split-on [data-letter] {
    animation: hwg-letter 185ms cubic-bezier(0.22, -0.08, 0.44, 0.9) both;
    animation-delay: calc(var(--i, 0) * 50ms);
  }
}

/* Menu button, closed: the hover swap of the line lengths (contract Addendum 3). Live runs it as a
   spring (60 samples/s at 1440 and 768): first change ≈29 ms after the pointer arrives, then t50 65 ms,
   t90 132 ms, t99 165 ms, a 1.5 % overshoot, last sub-pixel change 348–400 ms after the first.
   H3's nav.css width transition (200 ms) ends ≈185 ms after its first change — 47 % short — so it
   is corrected here: 340 ms cubic-bezier(.35,.5,.1,1.12) measures t50 67 / t90 133 / t99 183 ms,
   peak 1.55 %, 333 ms first → last change. reveal.js sets .hwg-morph for 450 ms whenever the menu
   opens or closes, so the open ⇄ closed morph keeps H3's 200 ms width transition (live: 203 ms)
   and only hover-driven changes use this one. The open ×'s hover (36-px lines at ∓20° with the
   pointer away, ∓35° hovered = 34.5 / 30.6-px wide boxes, the Addendum's "35 / 31 px") is H3's and
   matches live exactly, so it is not overridden. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .hwg-menu-btn[aria-expanded="false"]:not(.hwg-morph) .framer-1q2wq7a,
  .hwg-menu-btn[aria-expanded="false"]:not(.hwg-morph) .framer-fja6el {
    transition:
      background-color 350ms cubic-bezier(0.22, 0.61, 0.36, 1),
      width 340ms cubic-bezier(0.35, 0.5, 0.1, 1.12),
      transform 331ms cubic-bezier(0.3, 0.26, 0.1, 1.2);
  }
}

/* Client and partner logo cards: colour on hover is a hover-only effect (H0 §6.5 client-card). On touch the
   tap leaves :hover stuck, so keep the grayscale there (home.css owns the fine-pointer rule; the
   doubled class out-ranks it). */
@media not ((hover: hover) and (pointer: fine)) {
  [data-section="clients"] .framer-yrmZF.framer-yrmZF:hover .framer-eus7an,
  [data-section="partners"] .framer-yrmZF.framer-yrmZF:hover .framer-eus7an {
    filter: grayscale(1);
    -webkit-filter: grayscale(1);
  }
}

/* ---------- links never take the UA link colours ----------
   Live bug (H0 §7): links flash rgb(0,0,238) on hover because Framer's accent tokens resolve to
   currentColor, which inherits the <a>'s UA colour. :where() keeps this at zero specificity, so
   every author colour (Framer presets, H3's shell rules, rich-text link tokens) still wins; it only
   replaces the UA's -webkit-link / visited / active colours. */
:where(a:link, a:visited, a:hover, a:active, a:focus) {
  color: inherit;
}

/* ---------- custom cursor (cursor.js, D32) ----------
   H0 §6.6: a 12 × 12 filled square with four 6 × 6 corner brackets (1-px lines) 4 px outside it
   (20 × 20 overall), fixed, z 13, pointer-events none; the square sits 18 px right / 24 px below the
   pointer and follows it with an exponential lag (t50 ≈ 217 ms). Live renders it black everywhere,
   which vanishes on the dark hero; here it is white and blended with `difference`, so it is black
   over white sections and white over black ones (and over the dark Methodology cards / CTA photo).
   The native cursor stays (live does not hide it). Only created for fine pointers without
   reduced motion. */
.hwg-cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 13;
  width: 12px;
  height: 12px;
  pointer-events: none;
  mix-blend-mode: difference;
  opacity: 0;
  transition: opacity 200ms ease;
  will-change: transform;
  contain: layout style size;
}
.hwg-cursor.is-visible { opacity: 1; }
.hwg-cursor__dot,
.hwg-cursor__corner {
  position: absolute;
  display: block;
}
.hwg-cursor__dot {
  inset: 0;
  background: #fff;
}
.hwg-cursor__corner {
  width: 6px;
  height: 6px;
  border: 0 solid #fff;
}
.hwg-cursor__corner--tl { top: -4px; left: -4px; border-top-width: 1px; border-left-width: 1px; }
.hwg-cursor__corner--tr { top: -4px; right: -4px; border-top-width: 1px; border-right-width: 1px; }
.hwg-cursor__corner--bl { bottom: -4px; left: -4px; border-bottom-width: 1px; border-left-width: 1px; }
.hwg-cursor__corner--br { bottom: -4px; right: -4px; border-bottom-width: 1px; border-right-width: 1px; }
@media (prefers-reduced-motion: reduce), not ((hover: hover) and (pointer: fine)) {
  .hwg-cursor { display: none !important; }
}
@media (forced-colors: active) {
  .hwg-cursor { display: none !important; }
}
