/* ══════════════════════════════════════════════════════════════════════
   EDGE WORLDWIDE — MOTION SYSTEM
   Loaded after style.css. Transform/opacity only (no layout thrash).
   Everything is inside prefers-reduced-motion: no-preference, so a
   reduced-motion visitor gets the full page, statically, with no loops.
   ══════════════════════════════════════════════════════════════════════ */

:root{
  --e-out:cubic-bezier(.22,.61,.19,1);
  --e-soft:cubic-bezier(.33,.9,.28,1);
  --m-dur:.85s;
  --m-stagger:70ms;
}

/* Static baseline — what reduced-motion and no-JS visitors see */
[data-m]{opacity:1;transform:none}
.hero-amb,.hero-grid,.hero-mark{display:none}
#mprogress{display:none}

@media (prefers-reduced-motion: no-preference){

/* ── 1. Reveal primitives ───────────────────────────────────────────── */
[data-m]{--i:0;--d:calc(var(--i) * var(--m-stagger))}
.js.motion-ready [data-m]:not(.m-in){opacity:0}
.js.motion-ready [data-m="up"]:not(.m-in){transform:translate3d(0,30px,0)}
.js.motion-ready [data-m="left"]:not(.m-in){transform:translate3d(-24px,0,0)}
.js.motion-ready [data-m="right"]:not(.m-in){transform:translate3d(24px,0,0)}
.js.motion-ready [data-m="rise"]:not(.m-in){transform:translate3d(0,44px,0) scale(.985)}
[data-m="up"],[data-m="left"],[data-m="right"],[data-m="rise"]{
  transition:opacity var(--m-dur) var(--e-out) var(--d),transform var(--m-dur) var(--e-out) var(--d)}
[data-m].m-in{opacity:1;transform:none}

/* Type that rises out of the page rather than fading in */
.rd [data-m="mask"],[data-m="mask"]{opacity:1;clip-path:inset(0 0 0 0);transition:clip-path 1.05s var(--e-out) var(--d),transform 1.05s var(--e-out) var(--d)}
.js.motion-ready [data-m="mask"]:not(.m-in){clip-path:inset(0 0 105% 0);transform:translate3d(0,14px,0)}
[data-m="mask"].m-in{clip-path:inset(-25% -5% -25% -5%);transform:none}

/* Hairlines and rules that draw themselves */
[data-m="line"]{opacity:1;transform-origin:left center;transition:transform .9s var(--e-out) var(--d)}
.js.motion-ready [data-m="line"]:not(.m-in){transform:scaleX(0)}
[data-m="line"].m-in{transform:scaleX(1)}
[data-m="linev"]{opacity:1;transform-origin:top center;transition:transform 1.1s var(--e-out) var(--d)}
.js.motion-ready [data-m="linev"]:not(.m-in){transform:scaleY(0)}
[data-m="linev"].m-in{transform:scaleY(1)}

/* Kicker rule grows in with its label */
.eye::before,.hero-eye::before{transition:width .8s var(--e-out) .1s}
.js.motion-ready [data-m].eye:not(.m-in)::before{width:0!important}

/* ── 2. Hero ──────────────────────────────────────────────────────────
   The decorative ambient/grid/wordmark layers were removed when the hero
   took a documentary photograph instead. Their radial-gradient glows and
   blurred colour blobs are deleted rather than left dormant: depth in the
   hero now comes from the photograph, the type column and the ground. */
.hero{isolation:isolate}
.js.motion-ready .hero-body>*{animation:mhero 1.05s var(--e-out) both}
.js.motion-ready .hero-body>.kicker{animation-delay:.12s}
.js.motion-ready .hero-body>h1{animation-delay:.24s;animation-name:mheroType;animation-duration:1.25s}
.js.motion-ready .hero-body>.hero-sub{animation-delay:.46s}
.js.motion-ready .hero-body>.hero-btns{animation-delay:.60s}
.js.motion-ready .hero-body>.hero-meta{animation-delay:.72s}

/* ── 3. Navigation responds to scroll ──────────────────────────────── */
.nav{transition:background .45s var(--e-out),box-shadow .45s var(--e-out),border-color .45s var(--e-out)}
.nav-in{transition:height .45s var(--e-out)}
.nav-logo-img{transition:height .45s var(--e-out),transform .45s var(--e-out)}

body.m-scrolled .nav{background:var(--deep);border-bottom-color:rgba(255,255,255,.22)}
/* Desktop only. Scoped so it cannot clamp the mobile nav height, which
   would clip links when .nav-ready is absent. */
/* Wide desktop only; 769–1120px uses the 72px compact height in style.css. */
@media (min-width:1121px){
  body.m-scrolled .nav-in{height:76px}
}
/* The compact logo changes real height, not transform. transform:scale()
   shrinks the image visually but leaves its layout box at full size, so the
   108px box sat inside a 76px bar and the mark overhung the bottom hairline.
   These heights leave 14px clearance above and below in both compact bands,
   matching the clearance at full height. */
@media (min-width:1121px){
  body.m-scrolled .nav-logo-img{height:48px;transform:none}
}
@media (min-width:769px) and (max-width:1120px){
  body.m-scrolled .nav-logo-img{height:44px;transform:none}
}
.nlink{position:relative}
.nlink::after{content:'';position:absolute;left:0;right:0;bottom:-2px;height:1px;background:currentColor;
  transform:scaleX(0);transform-origin:left center;transition:transform .42s var(--e-out);opacity:.85}
.nlink:hover::after,.nlink:focus-visible::after{transform:scaleX(1)}
.nav-cta::after{display:none}

/* Reading-progress bar, 3px */
#mprogress{display:block;position:fixed;top:0;left:0;height:3px;width:100%;z-index:1001;
  background:var(--terra,#92492b);transform:scaleX(0);transform-origin:left center;
  opacity:0;transition:opacity .4s var(--e-out);pointer-events:none}
.js.motion-ready body.m-scrolled #mprogress{opacity:1}

/* ── 4. Component life on hover ────────────────────────────────────── */
.rd .pillar,.rd .path,.pathway,.card-sand,.card-teal,.plogo,.principle,.team-card,.bio{
  transition:transform .5s var(--e-out),box-shadow .5s var(--e-out),border-color .5s var(--e-out),background .5s var(--e-out)}
.rd .path:hover,.pathway:hover,.principle:hover{transform:translate3d(0,-3px,0);border-color:var(--terra)}
.rd .pillar:hover{transform:translate3d(0,-3px,0)}
.plogo:hover{transform:translate3d(0,-4px,0);border-color:var(--terra,#92492b)}
.plogo{transition-delay:0s}
.pillar .pn,.pillar .pnum{transition:transform .5s var(--e-out),opacity .5s var(--e-out)}
.rd .pillar:hover .pn{transform:translate3d(0,-3px,0);opacity:1}
.values li{transition:transform .45s var(--e-out),color .45s var(--e-out)}
.values li:hover{transform:translate3d(0,-3px,0)}
.values li::after{transition:transform .45s var(--e-out),background .45s var(--e-out)}
.values li:hover::after{transform:translateY(-50%) scale(1.7)}
.btn{position:relative;overflow:hidden;transition:transform .4s var(--e-out),background .4s var(--e-out),color .4s var(--e-out),border-color .4s var(--e-out)}
.btn:hover{transform:translate3d(0,-2px,0)}
.btn:active{transform:translate3d(0,0,0)}
.tlink::after{transition:transform .4s var(--e-out)}
.tlink:hover::after{transform:translate3d(5px,0,0)}
.fcol a,.fc a,.footer a,.rfooter a{transition:color .35s var(--e-out),transform .35s var(--e-out)}
.fcol li:hover a,.fc li:hover a{transform:translate3d(3px,0,0)}
.bio-more>summary::after{transition:transform .4s var(--e-out)}
.bio-more[open]>summary::after{transform:rotate(180deg)}

/* ── 5. Legacy timeline draws itself as you read ───────────────────── */
.tl{position:relative;overflow:visible}
.tl>div:first-child{position:relative}

.tl-yr{transition:color .5s var(--e-out)}
.tl:hover .tl-yr{color:var(--terra,#92492b)}

/* ── 6. Section entrances keep their own rhythm ────────────────────── */
.stats-top .rule{transform-origin:left center}
.figure,.iw{overflow:hidden}
.figure img,.iw img{transition:transform 1.6s var(--e-out)}
.figure:hover img,.iw:hover img{transform:scale(1.05)}
.creds .fbadge{transition:transform .4s var(--e-out),border-color .4s var(--e-out),color .4s var(--e-out)}
.creds a:hover .fbadge,.fbadges a:hover .fbadge{transform:translate3d(0,-2px,0);border-color:var(--terra,#92492b)}

/* ── 7. Mobile menu opens with intent ──────────────────────────────── */
.mobile-menu .mm-link{opacity:0;transform:translate3d(0,16px,0);
  transition:opacity .5s var(--e-out),transform .5s var(--e-out)}
.mobile-menu.open .mm-link{opacity:1;transform:none}
.mobile-menu.open .mm-link:nth-child(1){transition-delay:.06s}
.mobile-menu.open .mm-link:nth-child(2){transition-delay:.10s}
.mobile-menu.open .mm-link:nth-child(3){transition-delay:.14s}
.mobile-menu.open .mm-link:nth-child(4){transition-delay:.18s}
.mobile-menu.open .mm-link:nth-child(5){transition-delay:.22s}
.mobile-menu.open .mm-link:nth-child(6){transition-delay:.26s}
.mobile-menu.open .mm-link:nth-child(7){transition-delay:.30s}
.mobile-menu.open .mm-link:nth-child(8){transition-delay:.34s}
.mobile-menu.open .mm-link:nth-child(9){transition-delay:.38s}

}/* end prefers-reduced-motion */

/* Never animate away the ability to see focus */
[data-m]:focus-within{opacity:1}

/* ═══════════════════════════════════════════════════════════════════════
   SCROLL FEEL — one shared layer
   Opt-in via [data-scroll-feel] on <main>, so legal and utility pages
   (privacy, terms, cookies, 404, thanks) are untouched.

   Scroll-linked, not entrance-triggered: progress is tied to each element's
   own passage through the viewport, so the page responds continuously as you
   move rather than popping once on arrival.

   Three safety properties, in order of importance:
   1. Everything sits inside @supports (animation-timeline: view()). Where
      that is unsupported (currently Safari and Firefox) NOTHING here
      applies, and the existing motion.js reveal system runs as before.
   2. Every keyframe set reaches its visible state before 50% and holds it,
      so no element can be left hidden mid-scroll — the usual failure mode
      of scroll animation.
   3. The whole layer is inside prefers-reduced-motion: no-preference,
      matching how the rest of this file is structured.
   ═══════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: no-preference){
  @supports (animation-timeline: view()){

    @keyframes sf-rise{
      0%{opacity:0;transform:translateY(24px)}
      42%,100%{opacity:1;transform:translateY(0)}
    }
    @keyframes sf-drift{
      from{transform:translateY(20px) scale(1.05)}
      to{transform:translateY(-20px) scale(1.05)}
    }

    /* Block-level settle. Excludes the hero (already composed on arrival)
       and the Legacy rail cards, which live in a horizontal scroller where
       a vertical view() range does not describe what the visitor sees. */
    /* .ab-bio is excluded deliberately: the About stakeholder cards use the
       brand's staggered "rise" reveal, and a view()-driven animation would
       override its opacity/transform and flatten the stagger to nothing. */
    [data-scroll-feel] :is(.reveal,.rise>*,.mark):not(.hero *):not(.rail-card):not(.rail-card *):not(.ab-bio):not(.plist):not(.plist>*){
      animation:sf-rise linear both;
      animation-timeline:view();
      animation-range:entry 6% cover 32%;
    }

    /* Photographs drift inside their frame. The 1.05 scale covers the
       travel so no edge is ever exposed. */
    [data-scroll-feel] .imgslot:not(.rail-card .imgslot){overflow:hidden}
    [data-scroll-feel] .imgslot:not(.rail-card .imgslot) img{
      animation:sf-drift linear both;
      animation-timeline:view();
      animation-range:cover 0% cover 100%;
      will-change:transform;
    }

    /* Hover writes the same property as the drift, so it is pinned to the
       drift's own transform rather than fighting it. */
    [data-scroll-feel] .imgslot:not(.rail-card .imgslot):hover img{transform:translateY(0) scale(1.05)}

    /* Never animate a focused element out from under the keyboard. */
    [data-scroll-feel] :focus-visible,
    [data-scroll-feel] :has(:focus-visible){animation:none!important;opacity:1!important;transform:none!important}
  }
}

/* ═══════════════════════════════════════════════════════════════════════
   EXPERIENCE LAYER · sticky statement + full-bleed photography
   Both opt-in per element, inside [data-scroll-feel], so legal, contact
   and utility pages are unaffected.
   ═══════════════════════════════════════════════════════════════════════ */

/* --- Scrollbar-safe full-bleed ------------------------------------------
   100vw includes the classic scrollbar, which is the usual cause of a
   1-15px horizontal overflow on desktop. --sbw is measured once in JS
   and defaults to 0, so the worst case without JS is a correct-width
   image rather than a broken page. */
:root{--sbw:0px}
/* !important is required, not lazy: page-scoped id rules such as
   #page-approach .ap-band set a `margin` shorthand, which zeroes
   margin-inline. Id specificity cannot be outranked by class selectors. */
[data-scroll-feel] .bleed{
  width:calc(100vw - var(--sbw))!important;
  margin-inline:calc(50% - ((100vw - var(--sbw)) / 2))!important;
  margin-block:clamp(48px,7vw,96px)!important;
  max-width:none!important;border-radius:0
}
/* The slot carries the height, not the image: .imgslot has an
   aspect-ratio and is a flex container, so setting a height on the img
   alone leaves the slot at its 3:2 ratio and centres a short photo
   inside it. aspect-ratio must be cleared for the height to take. */
[data-scroll-feel] .bleed .imgslot{width:100%!important;aspect-ratio:auto!important;height:clamp(280px,48vh,520px)!important;max-height:none!important;border:0;border-radius:0}
[data-scroll-feel] .bleed img{width:100%;height:100%;object-fit:cover;object-position:50% 38%}

/* --- Sticky statement ---------------------------------------------------
   The heading column holds while the body text passes it. Only above
   900px and only where the viewport is tall enough to make holding
   legible — below that it reverts to normal flow. */
@supports (position:sticky){
  @media (min-width:900px) and (min-height:620px){
    [data-scroll-feel] .stmt-hold{
      position:sticky;top:clamp(96px,14vh,148px);
      align-self:start
    }
  }
}

/* ── About · stakeholder cards ────────────────────────────────────────
   The cards use the brand's existing "rise" reveal (44px + a .985 scale)
   rather than the generic "up", so they arrive as objects with weight
   instead of sliding text blocks. Two additions on top of it:

   · the portrait settles out of a slight overscale as the card lands,
     which reads as the photograph coming to rest rather than appearing;
   · the role line and rule follow the name by a beat, so each card has
     an internal order — name, role, body — instead of one flat block.

   All timings reuse --m-dur / --e-out / --d, so the cards stay in step
   with every other reveal on the site and inherit the 90ms stagger. */
@media (prefers-reduced-motion: no-preference){
  /* #page-about .ab-bio sets a `transition` shorthand at id specificity
     (1,1,0), which outranks the [data-m="rise"] timing rule (0,1,0) and
     drops both the opacity transition and the --d stagger delay. Matching
     the id level here restores them without !important. The hover keys are
     carried through so the card still lifts on hover. */
  #page-about .ab-bio[data-m]{
    transition:opacity var(--m-dur) var(--e-out) var(--d),
               transform var(--m-dur) var(--e-out) var(--d),
               border-color .22s ease,box-shadow .22s ease}
  /* Once revealed, hover must feel immediate rather than inherit the
     reveal's long duration and stagger delay. */
  #page-about .ab-bio[data-m].m-in:hover,
  #page-about .ab-bio[data-m].m-in:focus-within{
    transition:transform .22s ease,border-color .22s ease,box-shadow .22s ease}
  #page-about .ab-bio .bio-por{overflow:hidden}
  #page-about .ab-bio .bio-por img{
    transition:transform 1.15s var(--e-out) var(--d);
    transform:scale(1.045)}
  #page-about .ab-bio.m-in .bio-por img{transform:scale(1)}

  #page-about .ab-bio .bio-role,
  #page-about .ab-bio .bio-sub{
    transition:opacity .7s var(--e-out) calc(var(--d) + 210ms),
               transform .7s var(--e-out) calc(var(--d) + 210ms)}
  .js.motion-ready #page-about .ab-bio:not(.m-in) .bio-role,
  .js.motion-ready #page-about .ab-bio:not(.m-in) .bio-sub{
    opacity:0;transform:translate3d(0,8px,0)}
  #page-about .ab-bio.m-in .bio-role,
  #page-about .ab-bio.m-in .bio-sub{opacity:1;transform:none}
}
