/* Scroll motion for every page, driven by assets/motion.js (no external library).
   Everything starts visible: the hidden start states below only apply once motion.js has put
   the class "motion" on <html>, so content stays readable without JavaScript, in print and when
   the visitor asks the system for reduced motion (motion.js then never adds the class).
   Only opacity, transform and (on large screens) a short blur are animated. */

:root {
  --m-ease: cubic-bezier(.16, 1, .3, 1);
  --m-ease-soft: cubic-bezier(.22, .61, .36, 1);
  --m-yellow: #ffb405;
  --m-green-light: #98bdac;
}

/* ---- reading progress bar ------------------------------------------------------------- */
#m-progress {
  position: fixed; top: 0; left: 0; right: 0; height: 3px; z-index: 2147482990;
  background: linear-gradient(90deg, var(--m-yellow), var(--m-green-light));
  transform: scaleX(0); transform-origin: 0 50%; pointer-events: none;
  box-shadow: 0 0 12px rgba(255, 180, 5, .45);
}

/* ---- hero entrance -------------------------------------------------------------------- */
.m-words { display: inline; }
.m-w { display: inline-block; overflow: hidden; vertical-align: top; padding-bottom: .08em; margin-bottom: -.08em; }
.m-wi { display: inline-block; }
.motion .m-wi { transform: translateY(105%) rotate(4deg); opacity: 0; transition: transform 1s var(--m-ease), opacity .7s var(--m-ease-soft); transition-delay: calc(var(--i, 0) * 45ms + 120ms); }
.motion .m-hero-in .m-wi { transform: none; opacity: 1; }

.motion .m-hero-part { opacity: 0; transform: translateY(18px); transition: opacity .9s var(--m-ease-soft), transform 1s var(--m-ease); transition-delay: var(--d, 0ms); }
.motion .m-hero-in .m-hero-part { opacity: 1; transform: none; }

/* ---- panels and their content ---------------------------------------------------------- */
.motion .m-reveal { opacity: 0; transform: translateY(56px) scale(.985); transition: opacity 1s var(--m-ease-soft), transform 1.1s var(--m-ease); }
.motion .m-reveal.m-in { opacity: 1; transform: none; }
@media (min-width: 1024px) {
  .motion .m-reveal { filter: blur(6px); transition: opacity 1s var(--m-ease-soft), transform 1.1s var(--m-ease), filter .9s var(--m-ease-soft); }
  .motion .m-reveal.m-in { filter: none; }
}

.motion .m-child { opacity: 0; transform: translateY(26px); transition: opacity .8s var(--m-ease-soft), transform .9s var(--m-ease); transition-delay: calc(var(--i, 0) * 70ms + 180ms); }
.motion .m-in .m-child { opacity: 1; transform: none; }

/* heading accent bar that draws in */
.lp .lp-panel > h2::after {
  content: ""; display: block; width: 56px; height: 4px; margin-top: 14px; border-radius: 4px;
  background: var(--m-yellow);
}
.motion .lp .lp-panel > h2::after { transform: scaleX(0); transform-origin: 0 50%; transition: transform .9s var(--m-ease) .25s; }
.motion .lp .lp-panel.m-in > h2::after { transform: scaleX(1); }

/* screenshots wipe in */
.motion .m-shot { clip-path: inset(22% 0 0 0 round 18px); transform: scale(1.06); opacity: .35; transition: clip-path 1.3s var(--m-ease), transform 1.6s var(--m-ease), opacity 1s var(--m-ease-soft); }
.motion .m-in .m-shot, .motion .m-shot.m-in { clip-path: inset(0 0 0 0 round 18px); transform: none; opacity: 1; }

/* steps: a line that fills as the list scrolls past */
.lp ol.lp-steps { position: relative; }
.lp ol.lp-steps::before, .lp ol.lp-steps::after {
  content: ""; position: absolute; left: 37px; top: 24px; bottom: 24px; width: 2px; border-radius: 2px;
}
.lp ol.lp-steps::before { background: rgba(240, 249, 245, .18); }
.lp ol.lp-steps::after { background: var(--m-yellow); transform: scaleY(var(--m-p, 1)); transform-origin: 50% 0; }
.lp ol.lp-steps > li { z-index: 1; }
.lp ol.lp-steps > li::before { z-index: 2; transition: transform .5s var(--m-ease), box-shadow .5s var(--m-ease-soft); }
.motion .lp ol.lp-steps > li.m-step-on::before { transform: scale(1.12); box-shadow: 0 0 0 6px rgba(255, 180, 5, .22); }

/* cards lift under the pointer */
@media (hover: hover) {
  .lp .lp-card { transition: transform .45s var(--m-ease), box-shadow .45s var(--m-ease-soft); }
  .lp .lp-card:hover { transform: translateY(-6px); box-shadow: 0 14px 28px rgba(0, 0, 0, .22); }
}

/* ---- hero decorations (landing pages) ----------------------------------------------- */
.lp .lp-hero { position: relative; }
/* the glass shapes may hang past the window edge; that must never make the page scroll sideways */
.lp main { overflow-x: clip; }
.lp .lp-deco { position: absolute; inset: 0 0 auto auto; width: 46%; height: 100%; pointer-events: none; z-index: -1; }
.lp .lp-deco img { position: absolute; height: auto; will-change: transform; filter: drop-shadow(0 18px 30px rgba(0, 0, 0, .18)); }
.lp .lp-deco .d1 { width: 360px; right: -60px; top: -40px; }
.lp .lp-deco .d2 { width: 150px; right: 30%; top: 58%; }
.lp .lp-deco .d3 { width: 95px; right: 6%; top: 82%; }
.motion .lp .lp-deco img { opacity: 0; transition: opacity 1.4s var(--m-ease-soft) .5s; }
.motion .lp .m-hero-in .lp-deco img { opacity: .9; }
@media (max-width: 899px) { .lp .lp-deco { display: none; } }
/* a state page shows the state's outline instead: it fades in and its border draws itself */
.lp .lp-deco-state .lp-deco-shape { position: absolute; display: block; width: 340px; right: 2%; top: -6px; will-change: transform; }
.lp .lp-deco-state .ic { width: 100%; height: auto; overflow: visible; filter: drop-shadow(0 18px 30px rgba(0, 0, 0, .2)); }
.lp .lp-deco-state .a-draw { stroke-width: .9; animation-duration: 2.4s; }
.motion .lp .lp-deco-state .lp-deco-shape { opacity: 0; transition: opacity 1.2s var(--m-ease-soft) .2s; }
.motion .lp .m-hero-in .lp-deco-state .lp-deco-shape { opacity: 1; }
.motion .lp .m-hero-in .lp-deco-state .ic * { animation-play-state: running; animation-iteration-count: 1; }

/* ---- depth (landing pages): a backdrop that moves slower than the page --------------------- */
/* motion.js adds #m-bg (the brand texture, drifting at 30 % of the scroll and wrapping every 300 px)
   and #m-glow (two soft glows on a slow wave), both fixed behind the content, and tints #m-bg with
   the tone of the section crossing the middle of the screen. Without motion.js the body keeps its
   own static texture. */
#m-bg, #m-glow { position: fixed; left: 0; right: 0; top: 0; z-index: -1; pointer-events: none; will-change: transform; }
#m-bg {
  height: calc(100vh + 300px); height: calc(100lvh + 300px);
  background: url(/assets/Noise.png) repeat 0 0 / 300px 300px, #268164; background-blend-mode: screen;
  transition: background-color 1.1s var(--m-ease-soft);
}
#m-bg[data-tone="forest"] { background-color: #15473a; }
#m-bg[data-tone="night"] { background-color: #0c1a16; }
#m-bg[data-tone="black"] { background-color: #050807; }
#m-glow {
  top: -15vh; left: -15vw; right: -15vw; height: 130vh;
  background:
    radial-gradient(38vmax 38vmax at 20% 26%, rgba(152, 189, 172, .17), transparent 70%),
    radial-gradient(30vmax 30vmax at 80% 74%, rgba(255, 180, 5, .08), transparent 70%);
}

/* ---- page-to-page transitions (browsers that support cross-document view transitions) ---- */
@view-transition { navigation: auto; }
::view-transition-old(root) { animation: m-vt-out .32s var(--m-ease-soft) both; }
::view-transition-new(root) { animation: m-vt-in .5s var(--m-ease) both; }
.lp-brand > img, .lp-lockup > .lp-logo, header a[aria-label="Aster homepage"] > img { view-transition-name: aster-logo; }
@keyframes m-vt-out { to { opacity: 0; transform: translateY(-14px); } }
@keyframes m-vt-in { from { opacity: 0; transform: translateY(22px); } }

/* ---- the visitor asked for less motion: everything still, nothing hidden ---------------- */
@media (prefers-reduced-motion: reduce) {
  @view-transition { navigation: none; }
  #m-progress { display: none; }
  .lp ol.lp-steps::after { transform: none; }
  /* the homepage's own scroll script (GSAP) writes inline styles; these win over them */
  .fade-in-section { opacity: 1 !important; transform: none !important; }
  .parallax__layer { transform: none !important; }
}

@media print {
  #m-progress, .lp-deco, #m-bg, #m-glow { display: none !important; }
  .motion .m-reveal, .motion .m-child, .motion .m-wi, .motion .m-hero-part, .motion .m-shot { opacity: 1 !important; transform: none !important; filter: none !important; clip-path: none !important; }
}

/* Swiss pages (body.lp-ch): the backdrop stays light, its tones move between ivory and sand */
/* the brand grain, darkened onto ivory (difference: the black of the texture leaves the ivory as it is,
   its specks become warm grey), at its own size and at half size, so it reads clearly through the acrylic panels */
body.lp-ch #m-bg {
  height: calc(100vh + 600px); height: calc(100lvh + 600px); /* motion.js wraps the drift every 600 px here */
  background: url(/assets/Noise.png) repeat 0 0 / 600px 600px, url(/assets/Noise.png) repeat 150px 90px / 300px 300px, #faf7f1;
  background-blend-mode: difference, difference;
}
body.lp-ch #m-bg[data-tone="forest"] { background-color: #f6f1e7; }
body.lp-ch #m-bg[data-tone="night"] { background-color: #f3ece0; }
body.lp-ch #m-bg[data-tone="black"] { background-color: #efe7d9; }
body.lp-ch #m-glow {
  background:
    radial-gradient(38vmax 38vmax at 20% 26%, rgba(196, 22, 28, .05), transparent 70%),
    radial-gradient(30vmax 30vmax at 80% 74%, rgba(60, 40, 20, .05), transparent 70%);
}

/* ---- scrubbed with the scroll (motion.js writes --e, --p, --s, --m, --h) -------------------
   Like Apple's product pages: each effect follows the scroll both ways instead of playing once.
   Without motion.js (no JavaScript, reduced motion, print) none of these rules apply. */
.motion .lp-panel.m-reveal.m-scrub-panel, .motion .lp-panel.m-reveal.m-in.m-scrub-panel {
  opacity: calc(.12 + .88 * var(--e, 1));
  transform: translateY(calc((1 - var(--e, 1)) * 90px)) scale(calc(.9 + .1 * var(--e, 1)));
  transform-origin: 50% 0; filter: none; transition: none; will-change: transform, opacity;
}
/* lead paragraphs: words light up as the paragraph rises through the viewport */
.motion .m-scrub-words .m-lw { opacity: clamp(.16, calc((var(--p, 1) * (var(--n) + 6) - var(--i)) / 6), 1); }
/* screenshots open like a laptop lid and grow to full size */
.lp figure:has(> .m-scrub-shot) { perspective: 1600px; }
.motion .lp-shot.m-scrub-shot {
  transform: rotateX(calc((1 - var(--s, 1)) * 28deg)) scale(calc(.78 + .22 * var(--s, 1)));
  transform-origin: 50% 100%; opacity: calc(.25 + .75 * var(--s, 1)); will-change: transform, opacity;
}
/* the landing hero recedes as the page scrolls away from it */
.motion .lp .lp-hero.m-scrub-hero {
  transform: translateY(calc(var(--h, 0) * -70px)) scale(calc(1 - var(--h, 0) * .05));
  opacity: calc(1 - var(--h, 0) * .8); transform-origin: 50% 0;
}
/* giant display type that slides across as the page scrolls (decorative, aria-hidden) */
.lp .lp-marquee { width: 100vw; margin: 8px 0 0 calc(50% - 50vw); overflow: hidden; pointer-events: none; user-select: none; }
.lp .lp-marquee span {
  display: inline-block; white-space: nowrap; font-size: clamp(72px, 15vw, 230px); font-weight: 700; line-height: 1.05;
  letter-spacing: -.03em; padding: 0 4vw;
  transform: translateX(calc(18vw - var(--m, .5) * 46vw));
  background: linear-gradient(90deg, #f0f9f5 0%, #ffb405 100%); -webkit-background-clip: text; background-clip: text; color: transparent;
}
body.lp-ch .lp-marquee span { background-image: linear-gradient(90deg, #1c1c1e 10%, #c4161c 75%); }
@media print { .lp .lp-marquee { display: none; } }

/* ---- scroll story (motion.js marks the step at the middle of the screen .is-active): on large
   screens the screenshots sit in a pinned frame and change as the steps pass, the next one rising
   and settling, the previous one lifting away -------------------------------------------------- */
@media (min-width: 900px) {
  .motion .lp .lp-story { display: grid; grid-template-columns: minmax(220px, 4fr) minmax(0, 8fr); gap: 0 36px; }
  .motion .lp .lp-story-inline { display: none; }
  .motion .lp .lp-story-step { min-height: 62vh; display: flex; flex-direction: column; justify-content: center; border-top: 0;
    opacity: .28; transform: translateX(-10px); transition: opacity .6s var(--m-ease-soft), transform .9s var(--m-ease); }
  .motion .lp .lp-story-step.is-active { opacity: 1; transform: none; }
  .motion .lp .lp-story-media { display: grid; grid-template-columns: minmax(0, 1fr); position: sticky; top: 12vh; height: 76vh; align-items: center; perspective: 1600px; }
  .motion .lp .lp-story-media::before {
    content: ""; grid-area: 1 / 1; justify-self: center; width: 78%; height: 62%; border-radius: 50%;
    background: radial-gradient(closest-side, color-mix(in srgb, var(--lp-accent) 22%, transparent), transparent); filter: blur(24px);
  }
  .motion .lp .lp-story-media figure { grid-area: 1 / 1; width: 100%; margin: 0; opacity: 0; transform-origin: 50% 100%;
    transform: translateY(64px) scale(.9) rotateX(10deg); transition: opacity .7s var(--m-ease-soft), transform 1.2s var(--m-ease); will-change: transform, opacity; }
  .motion .lp .lp-story-media figure.is-past { transform: translateY(-44px) scale(.95); }
  .motion .lp .lp-story-media figure.is-active { opacity: 1; transform: none; }
}
@media print { .lp .lp-story-media { display: none !important; } .lp .lp-story-inline { display: block !important; } }
/* Swiss pages: the screenshot takes three quarters of the story and runs into the panel's right margin; its height
   is capped so the whole window always fits on the screen */
@media (min-width: 900px) {
  .motion body.lp-ch .lp-story { grid-template-columns: minmax(220px, 3fr) minmax(0, 9fr); gap: 0 32px; }
  .motion body.lp-ch .lp-story-media { margin-right: -40px; height: 80vh; top: 10vh; }
  .motion body.lp-ch .lp-story-media img { max-height: 78vh; object-fit: contain; }
}
