/* Milestone Renovate — motion system. Pairs with js/motion.js; durations and easing come from tokens.css.
   Scroll reveals only hide an element while motion.js has tagged it (.m-reveal / .m-mask) and it has not yet
   arrived; motion.js never tags anything under reduced motion, and if it fails nothing is hidden. */

/* Text entrances use a rising mask, never an opacity fade, so text is at full contrast whenever it is visible. */
@keyframes m-rise{from{transform:translateY(var(--m-rise));clip-path:polygon(-10% 100%,110% 100%,110% 100%,-10% 100%)}to{transform:none;clip-path:polygon(-10% -60%,110% -60%,110% 160%,-10% 160%)}}
@keyframes m-title{from{transform:translateY(112%) rotate(1.6deg)}to{transform:none}}
@keyframes m-settle{from{transform:scale(var(--opening-scale,1.14))}to{transform:none}}
@keyframes m-shutter-left{to{transform:translateX(-102%)}}
@keyframes m-shutter-right{to{transform:translateX(102%)}}
@keyframes m-frame{from{transform:translate(14px,30px);opacity:0}to{transform:translate(14px,14px);opacity:.55}}
/* Starts partly open so the hero image paints (and counts for LCP) on the first frame. */
@keyframes m-unveil{from{clip-path:inset(0 0 22% 0)}to{clip-path:inset(0 0 0 0)}}
@keyframes m-cue{0%{transform:scaleY(0);transform-origin:top}45%,55%{transform:scaleY(1);transform-origin:top}56%{transform-origin:bottom}100%{transform:scaleY(0);transform-origin:bottom}}

/* Headline lines sit in clipped boxes; each line rises into view in turn. The padding keeps descenders and italics. */
.h-line{display:block;overflow:hidden;padding:.02em .1em .16em 0;margin:-.02em 0 -.16em}
.h-line>span{display:inline-block}

/* 1. Page load: hero entrance sequence (CSS only, so it never waits on script) */
@media(prefers-reduced-motion:no-preference){
  .hero-copy>*{animation:m-rise var(--dur-enter) var(--ease-out) backwards}
  .hero-copy>h1{animation:none}
  .hero-copy>h1 .h-line>span{transform-origin:left bottom;animation:m-title 1.1s var(--ease-out) both}
  .hero-copy>h1 .h-line:nth-child(1)>span{animation-delay:.12s}
  .hero-copy>h1 .h-line:nth-child(2)>span{animation-delay:.32s}
  .hero-copy>:nth-child(3){animation-delay:.44s}
  .hero-copy>:nth-child(4){animation-delay:.54s}
  .hero-copy>:nth-child(5){animation-delay:.62s}
  .hero-copy>:nth-child(n+6){animation-delay:.7s}
  /* Gold-edged forest shutters frame the first painted image, then retract. At least 52% of the photograph
     stays visible throughout; these decorative layers never cover controls or depend on JavaScript. */
  .hero-media-inner::before,.hero-media-inner::after{content:"";position:absolute;top:0;bottom:0;width:24%;box-sizing:border-box;z-index:1;background:var(--color-forest-deep);pointer-events:none;animation:m-shutter-left 1.45s .06s var(--ease-in-out) both}
  .hero-media-inner::before{left:0;border-right:2px solid var(--color-gold)}
  .hero-media-inner::after{right:0;border-left:2px solid var(--color-gold);animation-name:m-shutter-right}
  .hero-media-inner img{animation:m-settle 1.9s var(--ease-out) both}
  .hero-media::before{animation:m-frame 1.2s .2s var(--ease-out) both}
  .hero-corner,.hero-chip{animation:m-rise var(--dur-enter) .85s var(--ease-out) backwards}
  .scroll-cue{animation:m-rise var(--dur-enter) 1.1s var(--ease-out) backwards}
  .scroll-cue i::after{animation:m-cue 2.4s 2s var(--ease-in-out) 3}

  .inner-hero>.wrap>*{animation:m-rise var(--dur-enter) var(--ease-out) backwards}
  .inner-hero>.wrap>:nth-child(2){animation-delay:.1s}
  .inner-hero>.wrap>:nth-child(3){animation-delay:.22s}
  .inner-hero>.wrap>:nth-child(n+4){animation-delay:.32s}
  .inner-hero-image{animation:m-unveil 1.2s var(--ease-out) both}
  /* Keyboard and pointer interaction immediately clears the copy sequence, keeping controls stationary. */
  .hero-copy:focus-within>*,.hero-copy:focus-within>h1 .h-line>span,
  .hero-copy.opening-interacted>*,.hero-copy.opening-interacted>h1 .h-line>span{animation:none}
}

@media(max-width:760px) and (prefers-reduced-motion:no-preference){
  .hero-media-inner{--opening-scale:1.09}
  .hero-media-inner::before,.hero-media-inner::after{width:18%;animation-duration:1.2s}
}

/* 2. Scroll reveals (tagged by motion.js)
   Waiting blocks are only transparent: they keep their full box, so lazy images inside them still start loading
   early and the observer can see them. On arrival they rise out of a mask (no opacity fade), so visible text is
   always at full contrast. */
.m-reveal:not(.is-in),.m-mask:not(.is-in){opacity:0}
.m-reveal.is-in{animation:m-rise var(--dur-reveal) var(--ease-out) var(--m-delay,0ms) backwards}
/* Stagger steps set by motion.js (data attributes, not inline styles, to stay within the CSP). */
[data-m-step="1"]{--m-delay:80ms}[data-m-step="2"]{--m-delay:160ms}[data-m-step="3"]{--m-delay:240ms}[data-m-step="4"]{--m-delay:320ms}[data-m-step="5"]{--m-delay:400ms}[data-m-step="6"]{--m-delay:480ms}

/* Masked headings: the text lifts out of a clip window larger than its box, so italics and descenders are never cut. */
@keyframes m-lift{from{transform:translateY(.3em);clip-path:polygon(-6% 150%,106% 150%,106% 150%,-6% 150%)}to{transform:none;clip-path:polygon(-6% -40%,106% -40%,106% 150%,-6% 150%)}}
.m-mask.is-in{animation:m-lift var(--dur-reveal) var(--ease-out) var(--m-delay,0ms) backwards}

/* Journey: the forest rule draws across, then each step lands in sequence. */
.journey-list::after{content:"";position:absolute;left:0;right:0;top:28px;height:1px;background:var(--color-accent);transform-origin:left;transition:transform 1.6s var(--ease-in-out) .15s}
.journey-list.m-reveal:not(.is-in){opacity:1}
.journey-list.m-reveal.is-in{animation:none}
.journey-list.m-reveal:not(.is-in)::after{transform:scaleX(0)}
.journey-list.m-reveal:not(.is-in) li{opacity:0}
.journey-list.m-reveal.is-in li{animation:m-rise var(--dur-reveal) var(--ease-out) backwards}
.journey-list.m-reveal.is-in li:nth-child(2){animation-delay:.25s}
.journey-list.m-reveal.is-in li:nth-child(3){animation-delay:.5s}
.journey-list.m-reveal.is-in li:nth-child(4){animation-delay:.75s}

@media(max-width:1020px){.journey-list::after{display:none}}
@media(max-width:600px){
  .journey-list::after{display:block;left:27px;right:auto;top:0;bottom:0;width:1px;height:auto;transform-origin:top}
  .journey-list.m-reveal:not(.is-in)::after{transform:scaleY(0)}
}

/* 3. Scroll-linked depth (values written by motion.js). `translate` composes with any transform already in use. */
[data-parallax]{translate:0 var(--py,0px)}
@media(min-width:761px){.inner-hero-image img{scale:1.08}}

/* 4. Card accent lines: a gold rule draws across the top edge on hover and keyboard focus. */
.tool-card,.platform-card,.value-grid article{position:relative}
.tool-card::after,.platform-card::after,.value-grid article::after{content:"";position:absolute;left:0;right:0;top:-3px;height:3px;background:var(--color-gold);transform:scaleX(0);transform-origin:left;transition:transform var(--dur-slow) var(--ease-out);pointer-events:none}
.tool-card:hover::after,.platform-card:hover::after,.value-grid article:hover::after,
.tool-card:focus-visible::after,.platform-card:focus-visible::after{transform:scaleX(1)}

/* 6. Forms and planning tools. Nothing here uses opacity on text that must stay legible, so contrast is constant. */
@keyframes m-spin{to{transform:rotate(360deg)}}
@keyframes m-wipe-fwd{from{clip-path:inset(-10px -10px -10px 100%);transform:translateX(18px)}to{clip-path:inset(-10px);transform:none}}
@keyframes m-wipe-back{from{clip-path:inset(-10px 100% -10px -10px);transform:translateX(-18px)}to{clip-path:inset(-10px);transform:none}}
@keyframes m-ring{from{stroke-dashoffset:151}to{stroke-dashoffset:0}}
@keyframes m-tick{from{stroke-dashoffset:40}to{stroke-dashoffset:0}}

/* Sending: a spinner joins the button label and the form softens and stops taking input until the reply arrives.
   Under reduced motion the spinner is a static dotted ring, so "busy" is still shown without movement. */
.btn.is-busy{justify-content:center}
.btn.is-busy::before{content:"";flex:0 0 auto;width:15px;height:15px;margin-right:12px;border:2px solid currentColor;border-right-color:transparent;border-radius:50%;animation:m-spin .8s linear infinite}
.is-sending .field-grid,.is-sending .choice-grid,.is-sending .slot-group,.is-sending .consent-field{opacity:.7;pointer-events:none;transition:opacity var(--dur)}

/* Success: a drawn tick above the panel's own heading (decorative, aria-hidden). */
.success-mark{display:block;width:52px;height:52px;margin-bottom:18px}
.success-ring,.success-tick{fill:none;stroke:var(--color-success);stroke-linecap:round;stroke-linejoin:round}
.success-ring{stroke-width:2;stroke-dasharray:151}
.success-tick{stroke-width:3;stroke-dasharray:40}

@media(prefers-reduced-motion:no-preference){
  /* Questionnaire: the incoming step wipes in from the side it is reached from. */
  .wizard-step.is-entering{animation:m-wipe-fwd .5s var(--ease-out) backwards}
  .wizard[data-step-dir="back"] .wizard-step.is-entering{animation-name:m-wipe-back}
  .form-success.is-entering{animation:m-rise var(--dur-reveal) var(--ease-out) backwards}
  .form-success.is-entering .success-ring{animation:m-ring .8s .1s var(--ease-out) backwards}
  .form-success.is-entering .success-tick{animation:m-tick .5s .55s var(--ease-out) backwards}
}

@media(prefers-reduced-motion:reduce){
  .m-reveal,.m-mask,.journey-list li{animation:none!important}
  .btn.is-busy::before{animation:none;border-style:dotted;border-right-color:currentColor}
  .journey-list::after{transition:none}
  .m-reveal,.m-mask,.journey-list li{opacity:1;transform:none;clip-path:none}
  .journey-list::after{transform:none}
  [data-parallax]{translate:none}
  .inner-hero-image img{scale:none}
}

/* Printing (and any other non-scrolling render) shows every block in its final state. */
@media print{.m-reveal,.m-mask,.journey-list li{opacity:1!important;transform:none!important;clip-path:none!important}.journey-list::after{transform:none!important}.hero-media-inner::before,.hero-media-inner::after{display:none}.hero-copy>*,.hero-copy>h1 .h-line>span,.hero-media-inner img,.hero-media::before{animation:none!important}}
