/* ---------------------------------------------------------------------------
   Motion: every transition the site makes, in one file that every page loads,
   so the home page, help and the legal pages move the same way.

   Three kinds, all on the app's own timing ladder and curves (motionV2):
   a page transition between any two pages, an entrance for what is on screen
   when a page opens, and a reveal for blocks that arrive as the reader
   scrolls. Hover and press feedback lives with each control. Everything here
   collapses to its end state under prefers-reduced-motion, and nothing here
   hides content from a reader without scripting.
   --------------------------------------------------------------------------- */

/* --- page transitions ------------------------------------------------------ */

/* Every page opts in, so moving between any two of them (the home page, help,
   the legal pages, English and Thai) is one consistent transition rather than
   a blank flash. The header is named, so it holds still while the page under
   it changes; the language segment is named, so its blue fill slides from one
   language to the other. The old page leaves on the app's exit curve and the
   new one arrives on its emphasis curve. Browsers without cross-document view
   transitions simply navigate. */
@view-transition {
  navigation: auto;
}

.site-header {
  view-transition-name: site-header;
}

/* Only the header's switch is named: a name must be unique on the page, and
   the footer carries a second switch. */
.site-header .lang-switch__link[aria-current="true"] {
  view-transition-name: lang-current;
}

::view-transition-old(root) {
  animation: page-out var(--dur-quick) var(--ease-exit) both;
}

::view-transition-new(root) {
  animation: page-in var(--dur-emphasis) var(--ease-emphasis) both;
}

::view-transition-group(site-header),
::view-transition-group(lang-current) {
  animation-duration: var(--dur-emphasis);
  animation-timing-function: var(--ease-emphasis);
}

@keyframes page-out {
  to {
    opacity: 0;
    transform: translateY(-8px);
  }
}

@keyframes page-in {
  from {
    opacity: 0;
    transform: translateY(16px);
  }
}

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}

/* --- scroll reveal ---------------------------------------------------------- */

/* The same arrival on every page. scroll.js marks only the blocks that start
   below the screen as `reveal--pending`, so nothing a reader can already see
   is ever hidden, and with scripting off, under prefers-reduced-motion, or in
   print, nothing is hidden at all.

   A pending block rises `--reveal-rise` and fades in on the app's `slow` step
   and emphasis curve when it comes into view. Blocks arriving together are
   staggered 90ms apart from the top of the screen down (the script sets
   `--reveal-delay`). Inside a card the picture trails the words by 90ms, a
   row of phones follows one after another 60ms apart, and privacy chips pop
   in on the overshoot curve that stands in for the app's spring. */
.reveal--pending {
  opacity: 0;
  transform: translateY(var(--reveal-rise));
}

/* The transition lives on the arriving state only. A transition runs on the
   style an element changes TO, so hiding a block (adding `reveal--pending`)
   is instant and only its arrival (adding `is-in`) animates. */
.reveal--pending.is-in {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur-slow) var(--ease-emphasis) var(--reveal-delay, 0ms),
    transform var(--dur-slow) var(--ease-emphasis) var(--reveal-delay, 0ms);
}

.reveal--pending .card__figure:not(.gallery),
.reveal--pending .gallery__item {
  opacity: 0;
  transform: translateY(var(--reveal-rise));
}

.reveal--pending .gallery__item:nth-child(2) {
  --trail: 150ms;
}

.reveal--pending .gallery__item:nth-child(3) {
  --trail: 210ms;
}

.reveal--pending .gallery__item:nth-child(4) {
  --trail: 270ms;
}

.reveal--pending .gallery__item:nth-child(5) {
  --trail: 330ms;
}

.reveal--pending .chips > .chip {
  opacity: 0;
  transform: scale(0.86);
}

.reveal--pending .chips > .chip:nth-child(2) {
  --trail: 270ms;
}

.reveal--pending .chips > .chip:nth-child(3) {
  --trail: 360ms;
}

.reveal--pending.is-in .card__figure:not(.gallery),
.reveal--pending.is-in .gallery__item {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur-slow) var(--ease-emphasis) calc(var(--reveal-delay, 0ms) + var(--trail, 90ms)),
    transform var(--dur-slow) var(--ease-emphasis) calc(var(--reveal-delay, 0ms) + var(--trail, 90ms));
}

.reveal--pending.is-in .chips > .chip {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur-standard) var(--ease-standard) calc(var(--reveal-delay, 0ms) + var(--trail, 180ms)),
    transform var(--dur-emphasis) var(--ease-pop) calc(var(--reveal-delay, 0ms) + var(--trail, 180ms));
}

/* The home pages. A product view's phone trails its words, as a card's
   picture does. The small features arrive one after another, 60ms apart. */
.reveal--pending .story-panel__visual,
.reveal--pending .feature-list > li {
  opacity: 0;
  transform: translateY(var(--reveal-rise));
}

.reveal--pending .story-panel__visual {
  --trail: 120ms;
}

.reveal--pending .feature-list > li:nth-child(2) {
  --trail: 150ms;
}

.reveal--pending .feature-list > li:nth-child(3) {
  --trail: 210ms;
}

.reveal--pending .feature-list > li:nth-child(4) {
  --trail: 270ms;
}

.reveal--pending .feature-list > li:nth-child(5) {
  --trail: 330ms;
}

.reveal--pending .feature-list > li:nth-child(6) {
  --trail: 390ms;
}

.reveal--pending .feature-list > li:nth-child(7) {
  --trail: 450ms;
}

.reveal--pending .feature-list > li:nth-child(8) {
  --trail: 510ms;
}

.reveal--pending .feature-list > li:nth-child(n + 9) {
  --trail: 570ms;
}

.reveal--pending.is-in .story-panel__visual,
.reveal--pending.is-in .feature-list > li {
  opacity: 1;
  transform: none;
  transition:
    opacity var(--dur-slow) var(--ease-emphasis) calc(var(--reveal-delay, 0ms) + var(--trail, 90ms)),
    transform var(--dur-slow) var(--ease-emphasis) calc(var(--reveal-delay, 0ms) + var(--trail, 90ms));
}

@media (prefers-reduced-motion: reduce) {
  .reveal--pending,
  .reveal--pending .card__figure:not(.gallery),
  .reveal--pending .gallery__item,
  .reveal--pending .chips > .chip,
  .reveal--pending .story-panel__visual,
  .reveal--pending .feature-list > li {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* Print never waits for a scroll. */
@media print {
  .reveal--pending,
  .reveal--pending .card__figure:not(.gallery),
  .reveal--pending .gallery__item,
  .reveal--pending .chips > .chip,
  .reveal--pending .story-panel__visual,
  .reveal--pending .feature-list > li {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* --- changing product view -------------------------------------------------- */

/* When the visitor picks another view, the new one arrives rather than
   appearing: its words, then its phone 60ms behind. story.js marks the list
   only after a choice, so the view shown on arrival never replays. */
@keyframes view-in {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
}

[data-story-panels][data-switched] .story-panel:not([hidden]) .story-panel__copy {
  animation: view-in var(--dur-emphasis) var(--ease-emphasis) both;
}

[data-story-panels][data-switched] .story-panel:not([hidden]) .story-panel__visual {
  animation: view-in var(--dur-emphasis) var(--ease-emphasis) 60ms both;
}

@media (prefers-reduced-motion: reduce) {
  [data-story-panels][data-switched] .story-panel:not([hidden]) .story-panel__copy,
  [data-story-panels][data-switched] .story-panel:not([hidden]) .story-panel__visual {
    animation: none;
  }
}

/* --- entrance ---------------------------------------------------------------- */

/* What is on screen when a page opens arrives without the script, the same
   way on every page: the home hero, and the head of the help and legal pages.
   Each line rises in 70ms after the one above it, the phone rises 120ms
   behind the headline, and a legal page's contents card follows its heading.
   The `from` frame is the only place opacity is ever 0, so a browser with no
   animation support, or a print, simply shows the page.

   The phone is the largest paint on the home page, so it only rises. Fading
   it would hold back the moment the browser counts the page as drawn. */
@keyframes rise-in {
  from {
    opacity: 0;
    transform: translateY(var(--reveal-rise));
  }
}

@keyframes rise {
  from {
    transform: translateY(var(--reveal-rise));
  }
}

.hero__copy > *,
.intro__copy > *,
.legal__header > * {
  animation: rise-in var(--dur-slow) var(--ease-emphasis) both;
}

.hero__copy > :nth-child(2),
.intro__copy > :nth-child(2),
.legal__header > :nth-child(2) {
  animation-delay: 70ms;
}

.hero__copy > :nth-child(3),
.intro__copy > :nth-child(3),
.legal__header > :nth-child(3) {
  animation-delay: 140ms;
}

.hero__copy > :nth-child(n + 4),
.intro__copy > :nth-child(n + 4),
.legal__header > :nth-child(n + 4) {
  animation-delay: 210ms;
}

.hero__stage,
.slip-story__input {
  animation: rise var(--dur-slow) var(--ease-emphasis) 120ms both;
}

.legal__toc {
  animation: rise-in var(--dur-slow) var(--ease-emphasis) 210ms both;
}

/* The home page's demonstration plays once, as the page opens: the slip rises
   into place and a band of the slips colour reads it from top to bottom; then
   the transaction it becomes arrives, and its category chip pops in on the
   overshoot curve. About two seconds from first paint to the last frame. The
   slip is the largest paint, so it only rises; the arrow never moves, because
   its own transform turns it to point down on narrow screens. */
.bank-slip::after {
  position: absolute;
  inset: 0 0 auto;
  height: 38%;
  content: "";
  pointer-events: none;
  background: linear-gradient(
    180deg,
    transparent 0%,
    color-mix(in srgb, var(--feature-slips-ink) 14%, transparent) 86%,
    color-mix(in srgb, var(--feature-slips-ink) 55%, transparent) 100%
  );
  opacity: 0;
  animation: slip-read 1200ms var(--ease-standard) 520ms both;
}

@keyframes slip-read {
  0% {
    opacity: 1;
    transform: translateY(-100%);
  }

  85% {
    opacity: 1;
  }

  100% {
    opacity: 0;
    transform: translateY(265%);
  }
}

@keyframes fade-in {
  from {
    opacity: 0;
  }
}

@keyframes chip-pop {
  from {
    opacity: 0;
    transform: scale(0.86);
  }
}

.slip-story .flow-arrow {
  animation: fade-in var(--dur-standard) var(--ease-standard) 1150ms both;
}

.slip-story__output > * {
  animation: rise-in var(--dur-slow) var(--ease-emphasis) 1250ms both;
}

.slip-story__output > :nth-child(2) {
  animation-delay: 1320ms;
}

.slip-story__output > :nth-child(n + 3) {
  animation-delay: 1700ms;
}

.slip-story .category-chip {
  animation: chip-pop var(--dur-emphasis) var(--ease-pop) 1600ms both;
}

@media (prefers-reduced-motion: reduce) {
  .hero__copy > *,
  .intro__copy > *,
  .legal__header > *,
  .hero__stage,
  .slip-story__input,
  .legal__toc,
  .slip-story .flow-arrow,
  .slip-story__output > *,
  .slip-story .category-chip {
    animation: none;
  }

  .bank-slip::after {
    display: none;
  }
}
