/* ==========================================================================
   MOTION — taken from the supplied concept (Spryt_Motion_Concept_v1).

   Three easings do all the work there:
     lock    a hard-in, hard-out curve for things that snap into position
     glide   the general-purpose move
     settle  overshoots slightly and comes back, so an arrival reads as a
             click rather than a fade

   Two rules govern everything below:

   1. Every initial "hidden" state is scoped to html.js. Without JavaScript
      the observer never runs, so an unscoped rule would leave the page
      permanently blank. The class is set by an inline script in <head>,
      before first paint, so there is no flash.

   2. prefers-reduced-motion switches all of it off. The concept does the
      same, and this is a healthcare site — vestibular triggers matter.
   ========================================================================== */

:root{
  --lock:cubic-bezier(.62,0,.18,1);
  --glide:cubic-bezier(.35,0,.15,1);
  --settle:cubic-bezier(.2,1.1,.3,1);
  --t-quick:220ms;
  --t-move:420ms;
  --t-story:760ms;
}

/* --- Reveal ---------------------------------------------------------------
   Sections rise a little as they enter. 26px and 760ms are the concept's
   numbers; anything faster reads as a jump, anything further as a slide. */
html.js .rv{
  opacity:0; transform:translateY(26px);
  transition:opacity var(--t-story) var(--glide),
             transform var(--t-story) var(--glide);
}
html.js .rv.in{ opacity:1; transform:none; }

/* --- Staggered run --------------------------------------------------------
   A run of colour blocks arrives left to right, one after the next, so the
   set reads as a sequence rather than a single slab appearing. */
html.js .rv-stagger > *{
  opacity:0; transform:translateY(20px);
  transition:opacity var(--t-move) var(--glide),
             transform var(--t-move) var(--glide);
}
html.js .rv-stagger.in > *{ opacity:1; transform:none; }
html.js .rv-stagger.in > *:nth-child(1){ transition-delay:0ms; }
html.js .rv-stagger.in > *:nth-child(2){ transition-delay:80ms; }
html.js .rv-stagger.in > *:nth-child(3){ transition-delay:160ms; }
html.js .rv-stagger.in > *:nth-child(4){ transition-delay:240ms; }

/* --- The lock -------------------------------------------------------------
   The concept's signature move, and the reason it belongs on this site: the
   connector's two blocks arrive apart and lock together. Because the easing
   overshoots, the join lands with a click instead of easing flat — the shape
   assembles itself, which is the whole idea behind the mark.

   The blocks travel on the diagonal they already sit on, so the movement is
   the connector's own geometry rather than a generic slide. */
html.js .connector--lock > .connector__b{
  opacity:0; transform:translate(18px,-30px);
}
html.js .connector--lock > .connector__a{
  opacity:0; transform:translate(-18px,30px);
}
/* The delay gives the lock its own beat. Without it the blocks travel while
   the section around them is still fading in, and the move is lost — the
   whole point is that you SEE the shape assemble. */
html.js .connector--lock > .connector__b,
html.js .connector--lock > .connector__a{
  transition:transform var(--t-story) var(--settle),
             opacity var(--t-move) var(--glide);
  transition-delay:260ms;
}
html.js .connector--lock.in > .connector__b,
html.js .connector--lock.in > .connector__a{
  opacity:1; transform:none;
}

/* --- Connector lock LOOP (the concept's signature idle animation) ---------
   Beyond the one-shot lock-on-scroll above, the concept runs the mark as a
   continuous story: the two blocks are locked (Sky), part and turn Asa
   Sunrise as the "dialogue" opens, then lock back together and the gap closes.
   A 9s cycle on the concept's own easings. Used as a hero device via
   .connector-loop; it only animates when on screen (.is-live, set by the
   observer) so it isn't burning frames off-screen. */
.connector-loop{ display:block; }
.connector-loop svg{ width:100%; height:auto; display:block; overflow:visible; }
html.js .connector-loop.is-live #loopTop{
  animation:loopTop 9s var(--glide) infinite;
}
html.js .connector-loop.is-live #loopBot{
  animation:loopBot 9s var(--glide) infinite;
}
/* Both in-shape labels belong to the LOCKED state: they name the two halves
   of the connector while it is one form, and clear together the moment the
   blocks part. They are not a cross-fade — that is the caption's job below. */
html.js .connector-loop.is-live .loop-lblA,
html.js .connector-loop.is-live .loop-lblB{
  animation:loopLbl 9s var(--glide) infinite;
}
@keyframes loopTop{
  0%,24%{ transform:translate(0,0); fill:var(--spryt-sky); }
  40%,72%{ transform:translate(14px,-34px); fill:var(--asa-sunrise); }
  93%{ transform:translate(-3px,5px); fill:var(--spryt-sky); }
  100%{ transform:translate(0,0); fill:var(--spryt-sky); }
}
@keyframes loopBot{
  0%,24%{ transform:translate(0,0); fill:var(--spryt-sky); }
  40%,72%{ transform:translate(-14px,34px); fill:var(--asa-sunrise); }
  93%{ transform:translate(3px,-5px); fill:var(--spryt-sky); }
  100%{ transform:translate(0,0); fill:var(--spryt-sky); }
}
/* labels: present while locked, gone while the blocks are apart */
@keyframes loopLbl{ 0%,26%{opacity:1} 34%,86%{opacity:0} 100%{opacity:1} }

/* --- Notch reveal ---------------------------------------------------------
   Where a photograph carries a notch, the tab that cuts it wipes in after
   the image has settled, so you see the shape being cut. */
html.js .rv-notch .hero__tab,
html.js .rv-notch .meet-asa__tab{
  opacity:0; transform:translateY(-10px);
  transition:opacity var(--t-move) var(--glide),
             transform var(--t-move) var(--lock);
  transition-delay:220ms;
}
html.js .rv-notch.in .hero__tab,
html.js .rv-notch.in .meet-asa__tab{ opacity:1; transform:none; }

/* --- Watermark drift ------------------------------------------------------
   The p42 blocks ease up as the band arrives. Very small — it should read as
   depth, not as an animation. */
html.js .rv-wm .bw{
  opacity:0; transform:translateY(14px);
  transition:opacity var(--t-story) var(--glide),
             transform var(--t-story) var(--glide);
}
html.js .rv-wm.in .bw{ opacity:1; transform:none; }
html.js .rv-wm.in .bw--br{ transition-delay:120ms; }

/* --- Interaction ----------------------------------------------------------
   Buttons and cards use the lock curve so pointer feedback matches the
   page's arrival language. */
.btn{ transition:transform var(--t-quick) var(--lock),
                 background-color var(--t-quick) var(--lock),
                 color var(--t-quick) var(--lock); }
@media (hover:hover){
  .btn:hover{ transform:translateY(-2px); }
  .stat-cell{ transition:transform var(--t-move) var(--lock); }
}

@media (prefers-reduced-motion:reduce){
  html.js .rv,
  html.js .rv.in,
  html.js .rv-stagger > *,
  html.js .rv-stagger.in > *,
  html.js .connector--lock > .connector__b,
  html.js .connector--lock > .connector__a,
  html.js .rv-notch .hero__tab,
  html.js .rv-notch .meet-asa__tab,
  html.js .rv-wm .bw{
    opacity:1 !important; transform:none !important;
    transition:none !important; animation:none !important;
  }
  /* the looping lock device freezes on its locked (Sky, together) frame */
  html.js .connector-loop #loopTop,
  html.js .connector-loop #loopBot,
  html.js .connector-loop .loop-lblA,
  html.js .connector-loop .loop-lblB{
    animation:none !important; transform:none !important; fill:var(--spryt-sky);
  }
  html.js .connector-loop .loop-lblA,
  html.js .connector-loop .loop-lblB{ opacity:1; }
  .btn:hover{ transform:none; }
}

/* --- Scroll-linked rise (small screens) -----------------------------------
   Stacked, the stat sits below the photograph. As the section is scrolled
   through, the connector travels up until it tucks against the photograph's
   bottom edge — the two pieces closing on each other as you read, which is
   the same idea as the lock, drawn out over the scroll instead of a moment.

   Built with a scroll-driven animation rather than a scroll listener, so it
   runs off the main thread and costs nothing on a phone. Everything sits
   inside @supports: where it is unavailable the stat simply stays put, and
   the layout compensation is skipped with it — applying that alone would
   pull the next section over the stat. */
@media (max-width:900px){
  /* Stacked, the RISE is the connector's move — the two blocks travelling up
     together to meet the photograph. The lock is switched off here so the
     blocks are never seen apart: running both meant the pair was still
     converging while the rise had already begun, which reads as a gap
     between them rather than as an animation. */
  html.js .connector--lock > .connector__b,
  html.js .connector--lock > .connector__a{
    opacity:1; transform:none; transition:none; transition-delay:0s;
  }

  @supports (animation-timeline: view()){
    /* The travel is derived, not guessed: the stat rises by exactly its own
       top margin plus the height of the figure block, which lands the
       CAPTION block's top edge on the photograph's bottom edge. The figure
       block ends up sitting inside the photograph — the same interlock the
       desktop layout has, arrived at by scrolling. */
    html.js .sg-stat{
      --sg-gap:clamp(34px,9vw,60px);
      --sg-fig:clamp(74px,15vw,104px);
      /* one pixel short of the exact landing, so the figure block finishes a
         pixel BELOW the photograph's bottom edge rather than flush with it.
         Flush, the two antialiased edges share a fractional row and the
         photograph's last line shows beneath the block. */
      --sg-rise:calc(var(--sg-gap) + var(--sg-fig) - 1px);
      margin-top:var(--sg-gap);
      animation:sg-rise linear both;
      animation-timeline:view();
      /* finishes as the section finishes entering the viewport, rather than
         dragging on until it is 62% scrolled past */
      animation-range:entry 10% entry 100%;
      will-change:transform;
    }
    /* the media gives back exactly what the stat travels, so the rise does
       not leave a dead band above the next section */
    html.js .sg-media{
      margin-bottom:calc(-1 * (clamp(34px,9vw,60px) + clamp(74px,15vw,104px) - 1px));
    }
    /* Once risen, the connector lands ON the photograph's bottom corners:
       the figure block covers the bottom-left, and the caption block's top
       edge lands along the bottom edge, ending flush with the photograph's
       bottom-right. A rounded corner there leaves a crescent of the section
       colour between the photograph and the block, which is what breaks the
       blend. The photograph's bottom corners therefore go SQUARE and the
       connector carries the composite's rounded bottom instead — the same
       division of labour the copy card and photograph already use at their
       join. Inside @supports because without the rise the connector never
       reaches the photograph and the rounded corners are still correct. */
    html.js .sg-media img{
      border-bottom-left-radius:0; border-bottom-right-radius:0;
    }
    /* Right side: the caption block's right edge is collinear with the
       photograph's, so their shared right edge must run straight through the
       join — the caption's top-right is an interior corner here, not an
       outer one, and rounding it left a notch against the photograph's now
       square corner. */
    html.js .sg-stat > .connector__a{ border-top-right-radius:0; }
    /* Left side: the figure block covers the photograph's bottom-left, but
       it lands only 1px below the photograph's bottom edge, so a square
       photograph corner showed as a speck of picture inside the figure
       block's own corner curve. Matching the photograph's corner to the
       figure block's radius nests one curve just inside the other and the
       leak closes. */
    html.js .sg-media img{ border-bottom-left-radius:clamp(4px,0.7vw,6px); }
    @keyframes sg-rise{
      from{ transform:translateY(0); }
      to  { transform:translateY(calc(-1 * var(--sg-rise))); }
    }
  }
  html.js .sg-stat > .connector__b{ min-height:clamp(74px,15vw,104px); }
}

@media (prefers-reduced-motion:reduce){
  html.js .sg-media{ margin-bottom:0 !important; }
  html.js .sg-stat{ animation:none !important; transform:none !important; }
}
/* No rise means the connector never reaches the photograph, so the
   photograph keeps its own rounded bottom corners. */
@media (prefers-reduced-motion:reduce) and (max-width:900px){
  html.js .sg-media img{
    border-bottom-left-radius:var(--r-block);
    border-bottom-right-radius:var(--r-block);
  }
}

/* --- Icon assemble --------------------------------------------------------
   The brand icons are built from the same rounded blocks as the connector,
   so they get the same treatment: the blocks arrive offset on alternating
   diagonals and settle into the mark. Same easing, same idea — the shape
   puts itself together.

   transform-box:fill-box makes each path transform about its own bounding
   box; without it the offsets are computed against the whole SVG viewport
   and the blocks fly in from far outside the icon. */
html.js .rv-icon svg path{
  opacity:0;
  transform-box:fill-box; transform-origin:50% 50%;
  transform:translate(7px,-11px);
  transition:opacity var(--t-move) var(--glide),
             transform var(--t-story) var(--settle);
}
html.js .rv-icon svg path:nth-child(even){ transform:translate(-7px,11px); }
html.js .rv-icon.in svg path{ opacity:1; transform:none; }

/* a short stagger so the blocks land in sequence rather than together */
html.js .rv-icon.in svg path:nth-child(1){ transition-delay:0ms; }
html.js .rv-icon.in svg path:nth-child(2){ transition-delay:45ms; }
html.js .rv-icon.in svg path:nth-child(3){ transition-delay:90ms; }
html.js .rv-icon.in svg path:nth-child(4){ transition-delay:135ms; }
html.js .rv-icon.in svg path:nth-child(n+5){ transition-delay:180ms; }

@media (prefers-reduced-motion:reduce){
  html.js .rv-icon svg path,
  html.js .rv-icon.in svg path{
    opacity:1 !important; transform:none !important; transition:none !important;
  }
}

/* --- Conversation ---------------------------------------------------------
   The thread plays as a conversation rather than appearing all at once: a
   typing indicator, then the message, then the next. The concept's numbers —
   bubbles arrive over 460ms on the settle curve, from 12px down and 96.5%
   scale, and the dots pulse 140ms apart.

   Sequenced with animation-delay off a single .in class, so there is no
   per-message JavaScript and nothing to keep in sync with the markup. */
@keyframes bub-in{
  from{ opacity:0; transform:translateY(12px) scale(.965); }
  to  { opacity:1; transform:none; }
}
@keyframes typing-in{ from{ opacity:0; transform:translateY(8px); } to{ opacity:1; transform:none; } }
@keyframes typing-out{ to{ opacity:0; transform:translateY(-4px); } }
@keyframes typing-dot{
  0%,60%,100%{ opacity:.35; transform:translateY(0); }
  30%        { opacity:1;   transform:translateY(-3px); }
}

html.js .rv-chat .msg,
html.js .rv-chat .typing{ opacity:0; }

html.js .rv-chat.in .msg{
  animation:bub-in var(--t-move) var(--settle) both;
}
html.js .rv-chat.in .typing{
  animation:typing-in 240ms var(--settle) both,
            typing-out 200ms var(--glide) both;
}
html.js .rv-chat.in .typing i{
  animation:typing-dot 1s var(--glide) infinite;
}
html.js .rv-chat.in .typing i:nth-child(2){ animation-delay:.14s; }
html.js .rv-chat.in .typing i:nth-child(3){ animation-delay:.28s; }

/* The thread runs: type -> Asa -> patient -> type -> Asa.
   Each typing indicator fades out exactly as its message arrives. */
html.js .rv-chat.in > *:nth-child(1){ animation-delay:0ms, 900ms; }        /* typing  */
html.js .rv-chat.in > *:nth-child(2){ animation-delay:900ms; }             /* Asa     */
html.js .rv-chat.in > *:nth-child(3){ animation-delay:1700ms; }            /* patient */
html.js .rv-chat.in > *:nth-child(4){ animation-delay:2200ms, 3100ms; }    /* typing  */
html.js .rv-chat.in > *:nth-child(5){ animation-delay:3100ms; }            /* Asa     */

/* the indicator collapses once its message has landed, so it leaves no gap */
html.js .rv-chat.in > .typing{
  animation-fill-mode:both, forwards;
}

/* --- MediDrive thread: type -> Asa -> Asa -> patient -> type -> Asa -------
   Seven children: typing,msg, typing,msg, msg(patient), typing,msg. Each
   typing indicator collapses to zero height as its message lands, so the
   panel doesn't jump. Delays are per-child rather than shared, so this
   survives independently of the Asa page's own .rv-chat timing. */
html.js .thread-md.rv-chat .typing{ overflow:hidden; }
html.js .thread-md.rv-chat.in > *:nth-child(2){ animation-delay:820ms; }           /* Asa 1  */
html.js .thread-md.rv-chat.in > *:nth-child(4){ animation-delay:2240ms; }          /* Asa 2  */
html.js .thread-md.rv-chat.in > *:nth-child(5){ animation-delay:2980ms; }          /* patient*/
html.js .thread-md.rv-chat.in > *:nth-child(7){ animation-delay:4300ms; }          /* Asa 3  */

/* collapse the indicator's box as it fades, so the next bubble slides up */
@keyframes md-typing-collapse{
  to{ height:0; margin-top:calc(-1 * clamp(12px,1.5vw,18px)); padding-top:0; padding-bottom:0; }
}
html.js .thread-md.rv-chat.in > .typing{
  animation:typing-in 240ms var(--settle) both,
            typing-out 200ms var(--glide) both,
            md-typing-collapse 1ms linear both;
}
html.js .thread-md.rv-chat.in > *:nth-child(1){ animation-delay:0ms, 780ms, 980ms; }      /* typing */
html.js .thread-md.rv-chat.in > *:nth-child(3){ animation-delay:1500ms, 2200ms, 2400ms; } /* typing */
html.js .thread-md.rv-chat.in > *:nth-child(6){ animation-delay:3560ms, 4260ms, 4460ms; } /* typing */

@media (prefers-reduced-motion:reduce){
  html.js .rv-chat .msg,
  html.js .rv-chat .typing,
  html.js .rv-chat.in .msg,
  html.js .rv-chat.in .typing{
    opacity:1 !important; transform:none !important; animation:none !important;
  }
  html.js .rv-chat .typing{ display:none; }
}
