/* Marketing motion layer
 * =====================================================================
 * Loaded only by the marketing layout.
 *
 * The reference sites in the brand team's research frame (Tines, Thoughtly,
 * Attio, Fluz, Womp) build every product demo out of real DOM + CSS — not one
 * of them ships GSAP, Framer Motion, Lottie or Rive. This file is the same
 * bet: all motion here is CSS, so there is no animation runtime to download,
 * parse or keep in sync with the markup.
 *
 * Two rules hold throughout:
 *
 *   1. Motion is NARRATIVE, not decorative. Ambient drift belongs to the
 *      background stickers (the `float` / `bob` utilities in tailwind.config).
 *      Everything here exists to show a thing happening — a message arriving,
 *      a field filling, a status flipping.
 *
 *   2. Nothing animates that the visitor cannot see. Sequences are gated on
 *      `[data-in-view]` (set by in_view_controller) so offscreen sections cost
 *      nothing, and every block is wrapped in a reduced-motion guard that
 *      leaves the finished state on screen.
 *
 * Naming: `mav-` prefix, BEM-ish. Sequenced children carry `--seq` to order
 * themselves without a class per index.
 */

/* ---------------------------------------------------------------------------
 * Keyframes
 * ------------------------------------------------------------------------- */

@keyframes mav-rise-kf {
  from { opacity: 0; transform: translateY(22px); }
  to   { opacity: 1; transform: translateY(0); }
}

/* A message bubble landing in a thread. */
@keyframes mav-msg-in {
  from { opacity: 0; transform: translateY(10px) scale(0.96); }
  to   { opacity: 1; transform: translateY(0) scale(1); }
}

@keyframes mav-caret {
  0%, 49%   { opacity: 1; }
  50%, 100% { opacity: 0; }
}

/* Status dot for anything live: a solid core with a ring pushing out of it. */
@keyframes mav-ping {
  0%   { transform: scale(1); opacity: 0.55; }
  75%  { transform: scale(2.4); opacity: 0; }
  100% { transform: scale(2.4); opacity: 0; }
}

/* An outcome landing — the qualified chip, the bound policy. */
@keyframes mav-pop {
  0%   { opacity: 0; transform: scale(0.7); }
  60%  { opacity: 1; transform: scale(1.08); }
  100% { opacity: 1; transform: scale(1); }
}

@keyframes mav-marquee {
  from { transform: translateX(0); }
  to   { transform: translateX(-50%); }
}

/* ---------------------------------------------------------------------------
 * Scroll-driven reveal
 *
 * Native scroll-driven animation, the technique Tines and Thoughtly both use:
 * the timeline is the element's own progress through the viewport, so there is
 * no scroll listener and nothing to jank. `animation-duration` is a dummy —
 * `animation-timeline` overrides it — but it is set anyway because some early
 * implementations required a non-auto duration to start the animation.
 *
 * Browsers without `animation-timeline` never enter the @supports block and
 * simply render the element in its natural, finished state. That is why the
 * rule sets opacity only INSIDE the guard: an unsupported browser must never
 * be left with an invisible element. Same reason scroll_reveal_controller stays — it
 * covers the same ground with JS where it is already wired up.
 * ------------------------------------------------------------------------- */

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .mav-rise {
      animation: mav-rise-kf both;
      animation-duration: 1ms;
      animation-timing-function: linear;
      animation-timeline: view();
      animation-range: entry 0% entry 55%;
    }
  }
}

/* ---------------------------------------------------------------------------
 * Sequenced demo content
 *
 * `.mav-seq` marks a demo panel; DESCENDANTS carrying `--seq` play in order once
 * the panel is in view. Children are hidden ONLY while the panel is armed
 * (`data-seq-armed`, set by in_view_controller on connect) so that a visitor
 * without JS — where the attribute never appears — sees the whole panel.
 *
 * Matching is by descendant, not direct child: scenes wrap their content in a
 * layout div, so the sequenced items are rarely immediate children. A `>` here
 * silently matched nothing and the scene just appeared fully formed.
 * ------------------------------------------------------------------------- */

/* `:is()` so the flag may sit on the panel itself or on any ancestor —
 * in_view_controller marks a wrapper around the panel. */
@media (prefers-reduced-motion: no-preference) {
  :is([data-seq-armed] .mav-seq, [data-seq-armed].mav-seq) [style*="--seq"] { opacity: 0; }

  :is([data-in-view] .mav-seq, [data-in-view].mav-seq) [style*="--seq"] {
    animation: mav-msg-in 520ms cubic-bezier(0.22, 1, 0.36, 1) both;
    animation-delay: calc(var(--seq) * var(--seq-step, 460ms));
  }

  :is([data-in-view] .mav-seq, [data-in-view].mav-seq) .mav-seq__pop {
    animation-name: mav-pop;
    animation-duration: 460ms;
  }
}

/* ---------------------------------------------------------------------------
 * Live-state vocabulary
 *
 * Attio, Tines and Thoughtly all pair a status word with a pulsing dot; it is
 * the cheapest way to make a static panel read as "running right now".
 * ------------------------------------------------------------------------- */

.mav-dot {
  position: relative;
  display: inline-flex;
  flex: none;
  width: 0.5rem;
  height: 0.5rem;
}

.mav-dot__core,
.mav-dot__ring {
  position: absolute;
  inset: 0;
  border-radius: 9999px;
  background-color: currentColor;
}

.mav-dot__ring { opacity: 0.55; }

@media (prefers-reduced-motion: no-preference) {
  .mav-dot--live .mav-dot__ring { animation: mav-ping 1.8s cubic-bezier(0, 0, 0.2, 1) infinite; }
}

/* ---------------------------------------------------------------------------
 * Conversation primitives
 * ------------------------------------------------------------------------- */

.mav-caret::after {
  content: "";
  display: inline-block;
  width: 2px;
  height: 1em;
  margin-left: 2px;
  vertical-align: text-bottom;
  background-color: currentColor;
}

@media (prefers-reduced-motion: no-preference) {
  .mav-caret::after { animation: mav-caret 1.05s step-end infinite; }
}

/* Logo strip that scrolls forever. The track holds the logo list TWICE; the
 * animation moves it exactly -50%, so the second copy lands where the first
 * began and the loop is seamless. Hovering parks it so a logo can be read. */
.mav-marquee { overflow: hidden; -webkit-mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); mask-image: linear-gradient(90deg, transparent, #000 8%, #000 92%, transparent); }

/* Each copy is pinned to exactly one container width, so the track is 200% and
 * the -50% shift lands the second copy precisely where the first started. With
 * `width: max-content` instead, a logo list narrower than its container leaves
 * a visible gap at the loop point — the seam only disappears when a copy is at
 * least as wide as the window it scrolls through. */
.mav-marquee__track { display: flex; width: 100%; }
.mav-marquee__copy { flex: 0 0 100%; display: flex; align-items: center; justify-content: space-around; gap: 0 2rem; }

@media (prefers-reduced-motion: no-preference) {
  .mav-marquee__track { animation: mav-marquee var(--mav-marquee-duration, 38s) linear infinite; }
  .mav-marquee:hover .mav-marquee__track,
  .mav-marquee:focus-within .mav-marquee__track { animation-play-state: paused; }
}

/* ---------------------------------------------------------------------------
 * Brand mark
 *
 * The logo sits flat in `currentColor` and lights up in the brand gradient on
 * hover. The hover target is the LINK, not the SVG — hovering the click target
 * and having the mark stay flat because the pointer is in the link's padding
 * would read as a dead spot.
 * ------------------------------------------------------------------------- */

.mav-logo__gradient {
  opacity: 0;
  transition: opacity 380ms cubic-bezier(0.22, 1, 0.36, 1);
}

/* Baseline: a plain light-up. This is what reduced-motion users and any browser
 * that skips the block below actually get. */
.mav-logo-link:hover .mav-logo__gradient,
.mav-logo-link:focus-visible .mav-logo__gradient,
.mav-logo:hover .mav-logo__gradient {
  opacity: 1;
}

/* With motion allowed the mark reads as switched ON rather than merely lit: it
 * comes up once, then breathes.
 *
 * Two animations, not one. A single infinite keyframe cannot both rise from
 * zero and then loop without returning to zero — every cycle would blink the
 * logo out completely. So `activate` runs once with `both` to hold the mark up,
 * and `pulse` starts after it with no fill, taking over the property only once
 * its delay has elapsed. */
@keyframes mav-logo-activate {
  from { opacity: 0; }
  to   { opacity: 1; }
}

@keyframes mav-logo-pulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}

@media (prefers-reduced-motion: no-preference) {
  :is(.mav-logo-link:hover, .mav-logo-link:focus-visible, .mav-logo:hover) .mav-logo__gradient {
    animation:
      mav-logo-activate 240ms ease-out both,
      mav-logo-pulse 1.5s ease-in-out 240ms infinite;
  }
}

@media (prefers-reduced-motion: reduce) {
  .mav-logo__gradient { transition: none; }
}

/* ---------------------------------------------------------------------------
 * Clipping that does not break scroll-driven animation
 *
 * `overflow: hidden` makes an element a SCROLL CONTAINER. Any `view()` timeline
 * inside then resolves against that container instead of the document — and
 * since the container never scrolls, the timeline is degenerate and the
 * animation reports `finished` at 100% forever. The effect silently does
 * nothing, which is exactly how the dealt cards failed the first time.
 *
 * `overflow: clip` clips identically without creating a scroll container. The
 * two declarations are a progressive-enhancement pair: browsers too old for
 * `clip` keep `hidden`, and they are also too old for scroll-driven animation,
 * so they lose nothing.
 *
 * Use this on any marketing section that both clips decor and contains a
 * scroll-driven animation.
 * ------------------------------------------------------------------------- */

.mav-clip {
  overflow: hidden;
  overflow: clip;
}

/* ---------------------------------------------------------------------------
 * Dealt cards
 *
 * The testimonial cards drop into place as the section scrolls past, tied to
 * scroll position rather than fired once on entry — scrolling back up deals
 * them out again, which is the fun of it.
 *
 * The fall lives on a WRAPPER, never on the card itself. An animation running
 * with `both` fill is always the active value for the properties it touches,
 * so a fall on the card would permanently outrank the card's own hover
 * transform and kill the lift-and-straighten. Wrapper falls, card hovers.
 *
 * Each card sets --fall-from and --fall-tilt, and staggers itself by taking a
 * different `animation-range`, since a scroll timeline has no delay.
 * ------------------------------------------------------------------------- */

@keyframes mav-card-deal {
  from {
    opacity: 0;
    transform: translateY(var(--fall-from, -160px)) rotate(var(--fall-tilt, -12deg)) scale(0.92);
  }
  to {
    opacity: 1;
    transform: translateY(0) rotate(0deg) scale(1);
  }
}

@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    .mav-deal {
      animation: mav-card-deal both;
      animation-duration: 1ms;
      animation-timing-function: linear;
      animation-timeline: view();
      animation-range: entry 0% entry var(--fall-end, 62%);
    }
  }
}

/* Wide marquee items — phrases rather than logos. The logo variant pins each
 * copy to exactly one container width, which squashes content that is wider
 * than the container. Here the copies size to their content and the track grows
 * with them, so the -50% shift still lands precisely one copy along. */
.mav-marquee--wide .mav-marquee__track { width: max-content; }
.mav-marquee--wide .mav-marquee__copy {
  flex: 0 0 auto;
  width: max-content;
  justify-content: flex-start;
}

/* ---------------------------------------------------------------------------
 * Journey stage (lg and up)
 * Below `lg` the journey cards are a horizontal snap track. From `lg` the
 * journey-track controller stacks them in one grid cell and marks each card
 * with its offset from the focused one: the middle card is the focus, the two
 * beside it sit back, smaller and softly blurred, and the rest are hidden.
 * The transforms live here rather than in utilities because they are driven
 * by an attribute the controller sets, not by markup.
 */
@media (min-width: 1024px) {
  [data-journey-stage] > [data-journey-track-target="card"] {
    transition: transform 520ms cubic-bezier(0.22, 1, 0.36, 1), opacity 420ms ease, filter 420ms ease;
    will-change: transform;
  }

  [data-journey-stage] > [data-position="0"]  { transform: none; z-index: 3; opacity: 1; filter: none; }
  [data-journey-stage] > [data-position="-1"] { transform: translateX(-64%) scale(0.81); z-index: 2; opacity: 0.2; filter: none; box-shadow: none; cursor: pointer; }
  [data-journey-stage] > [data-position="1"]  { transform: translateX(64%) scale(0.81);  z-index: 2; opacity: 0.2; filter: none; box-shadow: none; cursor: pointer; }
  [data-journey-stage] > [data-position="-2"] { transform: translateX(-118%) scale(0.72); z-index: 1; opacity: 0; pointer-events: none; }
  [data-journey-stage] > [data-position="2"]  { transform: translateX(118%) scale(0.72);  z-index: 1; opacity: 0; pointer-events: none; }
  [data-journey-stage] > [data-journey-track-target="card"]:not([data-position]) { transform: scale(0.6); opacity: 0; pointer-events: none; }
}

@media (prefers-reduced-motion: reduce) {
  [data-journey-stage] > [data-journey-track-target="card"] { transition: none; }
}

/* In-page jump navs (What is Mav): glide to the section rather than jump.
   Scoped to pages that carry one, and only for visitors who allow motion. */
@media (prefers-reduced-motion: no-preference) {
  html:has(nav[data-smooth-scroll]) { scroll-behavior: smooth; }
}
