/* Motion: a few purposeful moments, never decoration on content people read.
   Curves come from --ease-out / --ease-in-out in theme.css. Entrances use CSS
   animations so they stay smooth while the page is still loading; every rule
   has a gentler reduced-motion variant at the end of this file. */

@keyframes motion-rise { from { opacity: 0; transform: translateY(14px); } }
@keyframes motion-lift { from { opacity: 0; transform: translateY(32px); } }
@keyframes motion-settle { from { opacity: 0; transform: scale(.97); } }
@keyframes motion-fade { from { opacity: 0; } }
@keyframes motion-fill { from { transform: scaleX(0); } }
@keyframes motion-draw { from { transform: scaleX(0); } to { transform: scaleX(1); } }
@keyframes motion-live { 50% { opacity: .35; } }

/* Hero copy: eyebrow, headline, lede, actions arrive as one short cascade on
   page open (and on client-side navigation, where it doubles as the page
   transition). 60ms steps keep the whole sequence under half a second. */
:is(.hero-copy, .product-hero-copy, .chat-stories-hero-copy, .stories-hero-copy, .page-hero-grid > :first-child, .about-hero-grid > :first-child) > * {
  animation: motion-rise .6s var(--ease-out) both;
}
:is(.hero-copy, .product-hero-copy, .chat-stories-hero-copy, .stories-hero-copy, .page-hero-grid > :first-child, .about-hero-grid > :first-child) > :nth-child(2) { animation-delay: 60ms; }
:is(.hero-copy, .product-hero-copy, .chat-stories-hero-copy, .stories-hero-copy, .page-hero-grid > :first-child, .about-hero-grid > :first-child) > :nth-child(3) { animation-delay: 120ms; }
:is(.hero-copy, .product-hero-copy, .chat-stories-hero-copy, .stories-hero-copy, .page-hero-grid > :first-child, .about-hero-grid > :first-child) > :nth-child(4) { animation-delay: 180ms; }
:is(.hero-copy, .product-hero-copy, .chat-stories-hero-copy, .stories-hero-copy, .page-hero-grid > :first-child, .about-hero-grid > :first-child) > :nth-child(n + 5) { animation-delay: 240ms; }

/* Client stories hero: the index of clients settles in after the copy. */
.stories-roster { animation: motion-fade .6s ease .3s both; }

/* Home hero: the phone is the product, so it rises onto its shelf. */
.hero-art-stage { animation: motion-fade .5s ease both; }
.hero-phone { animation: motion-lift .8s var(--ease-out) .15s both; }
.hero-art-caption { animation: motion-rise .6s var(--ease-out) .35s both; }

/* Product heroes: the mockup plays out what the product does. The window
   settles, the conversation arrives turn by turn, and vote and slider bars
   fill to their values. Explanation, seen once per visit. */
.product-mockup-stage .mock-window { animation: motion-settle .5s var(--ease-out) .1s both; }
.product-mockup-stage .mock-thread > * { animation: motion-rise .45s var(--ease-out) both; }
.product-mockup-stage .mock-thread > :nth-child(1) { animation-delay: .45s; }
.product-mockup-stage .mock-thread > :nth-child(2) { animation-delay: .85s; }
.product-mockup-stage .mock-thread > :nth-child(3) { animation-delay: 1.25s; }
.product-mockup-stage .mock-thread > :nth-child(4) { animation-delay: 1.6s; }
.product-mockup-stage .mock-thread > :nth-child(n + 5) { animation-delay: 1.9s; }
.product-mockup-stage .mock-bar > span { transform-origin: left; animation: motion-fill 1s var(--ease-out) .7s both; }
.product-mockup-stage .mock-thread .mock-bar > span { animation-delay: 1.6s; }

/* A live indicator reads as live. */
.mock-pill-live::before { animation: motion-live 2s var(--ease-in-out) infinite; }

/* FAQ answers open and close instead of snapping (browsers without
   ::details-content keep the instant toggle). */
@supports (interpolate-size: allow-keywords) {
  .faq-list details { interpolate-size: allow-keywords; }
  .faq-list details::details-content {
    block-size: 0;
    overflow: clip;
    opacity: 0;
    transition: block-size .26s var(--ease-out), opacity .2s ease, content-visibility .26s allow-discrete;
  }
  .faq-list details[open]::details-content { block-size: auto; opacity: 1; }
}

/* Mobile menu grows from the button that opened it. */
.mobile-menu nav {
  transform-origin: top right;
  transition: opacity .2s var(--ease-out), transform .2s var(--ease-out);
  @starting-style { opacity: 0; transform: scale(.96) translateY(-4px); }
}

/* How it works: each step's rule draws in, left to right, as the list
   scrolls into view, so the sequence reads as a sequence. Tied to scroll
   position, no scroll-jacking; unsupported browsers keep the static rule. */
@supports (animation-timeline: view()) {
  .process-list li { position: relative; border-top-color: transparent; }
  .process-list li::before {
    content: '';
    position: absolute;
    inset: -1px 0 auto;
    height: 1px;
    background: var(--navy);
    transform-origin: left;
    animation: motion-draw linear both;
    animation-timeline: view();
    animation-range: entry 40% cover 35%;
  }
  .product-page-chat .process-list li::before { background: var(--purple-deep); }
  .process-list li:nth-child(2)::before { animation-range: entry 55% cover 38%; }
  .process-list li:nth-child(3)::before { animation-range: entry 70% cover 41%; }
  .process-list li:nth-child(4)::before { animation-range: entry 85% cover 44%; }
}

/* Client stories: each testimonial's closing rule draws in, left to right,
   as the row scrolls into view. Static rule where unsupported. */
@supports (animation-timeline: view()) {
  .story-row::after {
    content: '';
    position: absolute;
    inset: auto 0 -1px;
    height: 1px;
    background: var(--navy);
    transform-origin: left;
    animation: motion-draw linear both;
    animation-timeline: view();
    animation-range: entry 70% cover 40%;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* Keep the fades that help things read as arriving; drop the movement. */
  :is(.hero-copy, .product-hero-copy, .chat-stories-hero-copy, .stories-hero-copy, .page-hero-grid > :first-child, .about-hero-grid > :first-child) > *,
  .hero-phone, .hero-art-caption,
  .product-mockup-stage .mock-window,
  .product-mockup-stage .mock-thread > * { animation-name: motion-fade; animation-duration: .3s; }
  .product-mockup-stage .mock-bar > span, .mock-pill-live::before, .process-list li::before, .story-row::after { animation: none; }
  .process-list li::before, .story-row::after { transform: none; }
  .faq-list details::details-content { transition: opacity .2s ease, content-visibility .2s allow-discrete; }
  .faq-list summary::after { transition: none; }
  .mobile-menu nav { transition: opacity .2s ease; @starting-style { transform: none; } }
}

/* CU Voice: the waveforms speak, softly and out of step, like a live call. */
@keyframes voice-speak { 50% { transform: scaleY(1); } }
@keyframes voice-ring { from { transform: scale(.9); opacity: .9; } to { transform: scale(1.35); opacity: 0; } }
.voice-wave > span { animation: voice-speak 1.1s var(--ease-in-out) infinite; }
.voice-wave > span:nth-child(2n) { animation-duration: .9s; animation-delay: -.3s; }
.voice-wave > span:nth-child(3n) { animation-duration: 1.3s; animation-delay: -.6s; }
.voice-wave > span:nth-child(5n) { animation-delay: -.15s; }
.mock-voice-ring { animation: voice-ring 2.4s var(--ease-out) infinite; }
@media (prefers-reduced-motion: reduce) {
  .voice-wave > span, .mock-voice-ring { animation: none; }
}

/* Member app phone: sign in, accounts, loan, repeat. Each screen holds long
   enough to read; the swap is a short slide like a real page push. */
@keyframes app-login { 0%, 27% { opacity: 1; visibility: visible; transform: none; } 31%, 97% { opacity: 0; visibility: hidden; transform: translateX(-12px); } 100% { opacity: 1; visibility: visible; transform: none; } }
@keyframes app-home { 0%, 27% { opacity: 0; visibility: hidden; transform: translateX(16px); } 31%, 61% { opacity: 1; visibility: visible; transform: none; } 65%, 100% { opacity: 0; visibility: hidden; transform: translateX(-12px); } }
@keyframes app-loan { 0%, 61% { opacity: 0; visibility: hidden; transform: translateX(16px); } 65%, 94% { opacity: 1; visibility: visible; transform: none; } 98%, 100% { opacity: 0; visibility: hidden; } }
@keyframes app-tabs-in { 0%, 27% { opacity: 0; } 31%, 94% { opacity: 1; } 98%, 100% { opacity: 0; } }
@keyframes app-spin { to { transform: rotate(360deg); } }
@keyframes app-tab-home { 0%, 61% { color: var(--blue); } 65%, 100% { color: var(--ink-faint); } }
@keyframes app-tab-loans { 0%, 61% { color: var(--ink-faint); } 65%, 94% { color: var(--blue); } 98%, 100% { color: var(--ink-faint); } }
@media (prefers-reduced-motion: no-preference) {
  .mock-app-login { animation: app-login 12s var(--ease-in-out) infinite; }
  .mock-app-home { animation: app-home 12s var(--ease-in-out) infinite; }
  .mock-app-loan { animation: app-loan 12s var(--ease-in-out) infinite; }
  .mock-phone .mock-app-tabs { animation: app-tabs-in 12s var(--ease-in-out) infinite; }
  .mock-app-spinner { animation: app-spin .8s linear infinite; }
  .mock-app-tabs > span:first-child { animation: app-tab-home 12s var(--ease-in-out) infinite; }
  .mock-app-tabs > span:nth-child(3) { animation: app-tab-loans 12s var(--ease-in-out) infinite; }
  .mock-phone-screen .mock-bar > span { transform-origin: left; animation: motion-fill 1s var(--ease-out) both; }
}

/* CU Chat on a website: as the window scrolls into view, the launcher is
   there first, then the widget opens and the conversation arrives. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .mock-site-widget { transform-origin: bottom right; animation: motion-settle linear both; animation-timeline: view(); animation-range: entry 60% cover 40%; }
    .mock-site-widget .mock-thread > * { animation: motion-rise linear both; animation-timeline: view(); }
    .mock-site-widget .mock-thread > :nth-child(1) { animation-range: entry 70% cover 44%; }
    .mock-site-widget .mock-thread > :nth-child(2) { animation-range: entry 90% cover 50%; }
  }
}

/* Pointer feedback. Primary targets answer by moving toward where they go:
   a link's drawn arrow steps in its own direction (forward, down or out),
   and nav links draw their underline in from the left. The current page
   keeps its underline. Utility links (footer, legal) stay colour-only. */
.link-arrow { transition: translate .32s var(--ease-out); }
.desktop-nav > a, .nav-products > button { position: relative; }
.desktop-nav > a::after, .nav-products > button::before {
  content: '';
  position: absolute;
  inset: auto 0 -7px;
  height: 1.5px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .32s var(--ease-out);
}
.nav-products > button::before { right: 14px; }
.desktop-nav > a[aria-current="page"]::after, .nav-products.is-current > button::before { transform: none; }
@media (hover: hover) and (pointer: fine) {
  :is(.text-link, .button, .nav-products-overview):hover .link-arrow-forward { translate: 4px 0; }
  :is(.text-link, .button):hover .link-arrow-down { translate: 0 3px; }
  :is(.text-link, .button, .demo-banner-link):hover .link-arrow-out { translate: 2px -2px; }
  .desktop-nav > a:hover::after, .nav-products:is(:hover, .is-open) > button::before { transform: none; transform-origin: left; }
}
:is(.text-link, .button):active .link-arrow { transition-duration: .12s; }
@media (prefers-reduced-motion: reduce) {
  .link-arrow { transition: none; }
  :is(.text-link, .button, .nav-products-overview, .demo-banner-link):hover .link-arrow { translate: none; }
  .desktop-nav > a::after, .nav-products > button::before { transition: none; }
}

/* Home suite showcase: the outgoing product fades while the incoming one
   rises into place, then its mockup plays out what the product does, the
   same cascade as the product heroes. Each arrival replays it. */
@media (prefers-reduced-motion: no-preference) {
  .showcase-layer { translate: 0 16px; transition: opacity .25s ease, translate .25s ease, visibility 0s .25s; }
  :is(.showcase-stage[data-active="0"] .showcase-layer:nth-child(1), .showcase-stage[data-active="1"] .showcase-layer:nth-child(2), .showcase-stage[data-active="2"] .showcase-layer:nth-child(3), .showcase-stage[data-active="3"] .showcase-layer:nth-child(4), .showcase-stage[data-active="4"] .showcase-layer:nth-child(5), .showcase-stage[data-active="5"] .showcase-layer:nth-child(6)) { translate: none; transition: opacity .4s ease .1s, translate .7s var(--ease-out) .1s, visibility 0s; }
  :is(.showcase-stage[data-active="0"] .showcase-layer:nth-child(1), .showcase-stage[data-active="1"] .showcase-layer:nth-child(2), .showcase-stage[data-active="2"] .showcase-layer:nth-child(3), .showcase-stage[data-active="3"] .showcase-layer:nth-child(4), .showcase-stage[data-active="4"] .showcase-layer:nth-child(5), .showcase-stage[data-active="5"] .showcase-layer:nth-child(6)) .mock-window { animation: motion-settle .5s var(--ease-out) .15s both; }
  :is(.showcase-stage[data-active="0"] .showcase-layer:nth-child(1), .showcase-stage[data-active="1"] .showcase-layer:nth-child(2), .showcase-stage[data-active="2"] .showcase-layer:nth-child(3), .showcase-stage[data-active="3"] .showcase-layer:nth-child(4), .showcase-stage[data-active="4"] .showcase-layer:nth-child(5), .showcase-stage[data-active="5"] .showcase-layer:nth-child(6)) .mock-thread > * { animation: motion-rise .45s var(--ease-out) both; }
  :is(.showcase-stage[data-active="0"] .showcase-layer:nth-child(1), .showcase-stage[data-active="1"] .showcase-layer:nth-child(2), .showcase-stage[data-active="2"] .showcase-layer:nth-child(3), .showcase-stage[data-active="3"] .showcase-layer:nth-child(4), .showcase-stage[data-active="4"] .showcase-layer:nth-child(5), .showcase-stage[data-active="5"] .showcase-layer:nth-child(6)) .mock-thread > :nth-child(1) { animation-delay: .45s; }
  :is(.showcase-stage[data-active="0"] .showcase-layer:nth-child(1), .showcase-stage[data-active="1"] .showcase-layer:nth-child(2), .showcase-stage[data-active="2"] .showcase-layer:nth-child(3), .showcase-stage[data-active="3"] .showcase-layer:nth-child(4), .showcase-stage[data-active="4"] .showcase-layer:nth-child(5), .showcase-stage[data-active="5"] .showcase-layer:nth-child(6)) .mock-thread > :nth-child(2) { animation-delay: .8s; }
  :is(.showcase-stage[data-active="0"] .showcase-layer:nth-child(1), .showcase-stage[data-active="1"] .showcase-layer:nth-child(2), .showcase-stage[data-active="2"] .showcase-layer:nth-child(3), .showcase-stage[data-active="3"] .showcase-layer:nth-child(4), .showcase-stage[data-active="4"] .showcase-layer:nth-child(5), .showcase-stage[data-active="5"] .showcase-layer:nth-child(6)) .mock-thread > :nth-child(3) { animation-delay: 1.15s; }
  :is(.showcase-stage[data-active="0"] .showcase-layer:nth-child(1), .showcase-stage[data-active="1"] .showcase-layer:nth-child(2), .showcase-stage[data-active="2"] .showcase-layer:nth-child(3), .showcase-stage[data-active="3"] .showcase-layer:nth-child(4), .showcase-stage[data-active="4"] .showcase-layer:nth-child(5), .showcase-stage[data-active="5"] .showcase-layer:nth-child(6)) .mock-thread > :nth-child(n + 4) { animation-delay: 1.45s; }
  :is(.showcase-stage[data-active="0"] .showcase-layer:nth-child(1), .showcase-stage[data-active="1"] .showcase-layer:nth-child(2), .showcase-stage[data-active="2"] .showcase-layer:nth-child(3), .showcase-stage[data-active="3"] .showcase-layer:nth-child(4), .showcase-stage[data-active="4"] .showcase-layer:nth-child(5), .showcase-stage[data-active="5"] .showcase-layer:nth-child(6)) .mock-bar > span { transform-origin: left; animation: motion-fill 1s var(--ease-out) .6s both; }
}
@media (prefers-reduced-motion: reduce) {
  .showcase-layer { transition: opacity .2s ease, visibility 0s .2s; }
  :is(.showcase-stage[data-active="0"] .showcase-layer:nth-child(1), .showcase-stage[data-active="1"] .showcase-layer:nth-child(2), .showcase-stage[data-active="2"] .showcase-layer:nth-child(3), .showcase-stage[data-active="3"] .showcase-layer:nth-child(4), .showcase-stage[data-active="4"] .showcase-layer:nth-child(5), .showcase-stage[data-active="5"] .showcase-layer:nth-child(6)) { transition: opacity .2s ease, visibility 0s; }
}

/* Page transitions. Every link is a full page load, so the browser's
   cross-document view transitions carry one page into the next: the banner
   and header hold still, the page underneath crosses over, and the product
   stage travels from the home showcase (while it is on screen) into the
   product page's hero, or from one product hero to the next. Browsers
   without support keep the plain navigation and the hero cascade. */
@view-transition { navigation: auto; }
.demo-banner { view-transition-name: demo-banner; }
.site-header { view-transition-name: site-header; }
.showcase-stage[data-in-view], .product-mockup-stage { view-transition-name: product-stage; }
@keyframes page-out { to { opacity: 0; } }
@keyframes page-in { from { opacity: 0; } }
::view-transition-old(root) { animation: page-out .18s ease both; }
::view-transition-new(root) { animation: page-in .3s var(--ease-out) .06s both; }
::view-transition-group(product-stage) { animation-duration: .6s; animation-timing-function: var(--ease-out); }
::view-transition-old(product-stage), ::view-transition-new(product-stage) { height: 100%; object-fit: cover; }
::view-transition-old(product-stage) { animation: page-out .25s ease both; }
::view-transition-new(product-stage) { animation: page-in .35s ease .15s both; }
@media (prefers-reduced-motion: reduce) {
  /* A short crossfade only; nothing travels. */
  .showcase-stage[data-in-view], .product-mockup-stage { view-transition-name: none; }
  ::view-transition-new(root) { animation-delay: 0s; }
}
