/* ==========================================================================
   Alexio Ramirez — motion.
   Loads last, after site.css and hero.css. It is a layer over a finished
   page: drop the <link> and everything still works, it just stops moving.

   Three rules hold throughout:
   - load choreography only in the hero; everything below waits for the
     IntersectionObserver in js/site.js to add .is-in
   - scroll-linked depth is CSS scroll timelines only, never a scroll listener
   - reduced motion gets the finished frame (site.css zeroes the durations,
     js/site.js adds .is-in to everything up front)

   Moving parts use the individual `translate` / `scale` properties, not
   `transform`, so they stack on top of the transforms the page already has
   (hover zooms, the rotated signatures, the `drift` scroll animation)
   instead of overwriting them.
   ========================================================================== */

:root {
  --ease-io:  cubic-bezier(.76, 0, .24, 1);    /* curtains and hairlines */
  --ease-ink: cubic-bezier(.5, .05, .3, 1);    /* the pen */
}

/* a data-delay also feeds the elements that animate INSIDE the revealed one */
[data-delay="1"] { --d: .1s; }
[data-delay="2"] { --d: .2s; }
[data-delay="3"] { --d: .3s; }

@keyframes fade-up   { from { opacity: 0; translate: 0 18px; } }
@keyframes slide-in  { from { opacity: 0; translate: -12px 0; } }
@keyframes draw-x    { from { scale: 0 1; } }
@keyframes plate-in  { from { opacity: 0; scale: 1.08; } }
@keyframes broker-in { from { opacity: 0; scale: 1.035; } }

/* A line rises out from behind its own baseline. The element travels down
   by its own height while its clip grows from the top by the same amount,
   so the visible window stays still on the page and only the type moves.
   The negative insets at rest keep tracking and swashes from being cut. */
@keyframes line-up {
  from { translate: 0 100%; clip-path: inset(-.3em -.6em 100% -.6em); }
  to   { translate: 0 0;    clip-path: inset(-.3em -.6em -.3em -.6em); }
}

/* the signature is written left to right, along its own rotated baseline */
@keyframes ink {
  from { clip-path: inset(-60% 100% -60% -12%); }
  to   { clip-path: inset(-60% -12% -60% -12%); }
}

/* ============================================================== nav ===== */

.nav__in { animation: slide-down 1s var(--ease) .15s both; }
@keyframes slide-down { from { opacity: 0; translate: 0 -10px; } }

/* ============================================================= hero ===== */
/* 0.0s  the room fades up and settles from 108%
   0.3s  Alexio settles into place, from the floor up
   0.35s kicker, its rule draws
   0.45s the three headline lines, 110ms apart
   0.95s subline, then the actions, then the credential rail
   1.55s the signature is written                                      */

.bh-hero__bg     { transform-origin: 60% 45%;  animation: plate-in 2.6s var(--ease) both; }
.bh-hero__broker { transform-origin: 72% 100%; animation: broker-in 2s var(--ease) .3s both; }

.bh-hero .bh-kicker { opacity: 1; animation: slide-in 1s var(--ease) .35s both; }
.bh-hero .bh-kicker__rule {
  transform-origin: 0 50%;
  animation: draw-x 1.3s var(--ease-io) .5s both;
}

.bh-hero .bh-title > span { opacity: 1; animation: line-up 1.2s var(--ease) both; }
.bh-hero .bh-title > span:nth-child(1) { animation-delay: .45s; }
.bh-hero .bh-title > span:nth-child(2) { animation-delay: .56s; }
.bh-hero .bh-title > span:nth-child(3) { animation-delay: .67s; }

.bh-hero .bh-sub  { opacity: 1; animation: fade-up 1.1s var(--ease) .95s both; }
.bh-hero .bh-ctas { opacity: 1; animation: fade-up 1.1s var(--ease) 1.08s both; }

.bh-hero .bh-cred { opacity: 1; animation: none; }
.bh-hero .bh-cred__rule {
  transform-origin: 0 50%;
  animation: draw-x 1.4s var(--ease-io) 1.15s both;
}
.bh-hero .bh-cred__row { animation: fade-up 1s var(--ease) 1.35s both; }

.bh-hero .bh-signature { opacity: 1; animation: ink 1.9s var(--ease-ink) 1.55s both; }

/* js/site.js holds the hero on its first frame until both plates are
   decoded, so the entrance never plays over an empty room. !important
   because every rule above resets the play state through the shorthand. */
.bh-hero.is-loading * { animation-play-state: paused !important; }

/* ------------------------------------------------- hero depth on scroll */
/* Room, copy and man leave at three speeds. The plates are frame-aligned
   and the window was reconstructed behind him, so sliding them apart shows
   more room, never a hole. Every offset is 0 at the top of the page, so the
   frame that gets shared on a call is the approved concept exactly.
   56.3vw is the hero's own height (1672 / 941). */

@supports (animation-timeline: scroll()) {
  @media (min-width: 761px) and (prefers-reduced-motion: no-preference) {
    @keyframes depth-far  { to { translate: 0 17vw; } }
    @keyframes depth-mid  { to { translate: 0 8vw; opacity: 0; } }
    @keyframes depth-near { to { translate: 0 3.5vw; } }

    .bh-hero__bg {
      animation: plate-in 2.6s var(--ease) both, depth-far linear both;
      animation-timeline: auto, scroll(root);
      animation-range: normal, 0 56.3vw;
    }
    .bh-hero__broker {
      animation: broker-in 2s var(--ease) .3s both, depth-near linear both;
      animation-timeline: auto, scroll(root);
      animation-range: normal, 0 56.3vw;
    }
    .bh-hero .bh-signature {
      animation: ink 1.9s var(--ease-ink) 1.55s both, depth-near linear both;
      animation-timeline: auto, scroll(root);
      animation-range: normal, 0 56.3vw;
    }
    .bh-copy,
    .bh-hero .bh-cred {
      animation: depth-mid linear both;
      animation-timeline: scroll(root);
      animation-range: 0 56.3vw;
    }
  }
}

/* ------------------------------------------ nav: reading progress ----- */
/* a gold hairline along the bottom of the solid bar, as long as the page
   is read; hidden while the bar is still transparent over the hero */

@supports (animation-timeline: scroll()) {
  @media (prefers-reduced-motion: no-preference) {
    @keyframes progress { from { scale: 0 1; } to { scale: 1 1; } }
    .nav::after {
      content: ''; position: absolute; left: 0; right: 0; bottom: -1px; height: 1px;
      background: var(--gold);
      transform-origin: 0 50%;
      opacity: 0; transition: opacity .4s var(--ease);
      animation: progress linear both;
      animation-timeline: scroll(root);
    }
    .nav.is-stuck::after { opacity: .8; }
  }
}

/* ========================================================== reveals ===== */

[data-reveal] {
  transform: translateY(26px);
  transition: opacity 1s var(--ease), transform 1.1s var(--ease);
}

/* section headings: line by line, split on the <br> by js/site.js */
.h2.is-lines { opacity: 1; transform: none; }
.h2.is-lines .ln {
  display: block;
  translate: 0 100%;
  clip-path: inset(-.3em -.6em 100% -.6em);
  transition: translate 1.2s var(--ease), clip-path 1.2s var(--ease);
  transition-delay: calc(var(--i, 0) * .1s);
}
.h2.is-lines.is-in .ln { translate: 0 0; clip-path: inset(-.3em -.6em -.3em -.6em); }

/* photographs: a curtain lifts off the frame while the image inside
   settles from 116%. Every one of these frames already clips its image.
   The clip never goes on the element being observed: IntersectionObserver
   counts an element's own clip-path, so a fully clipped target never
   intersects and never gets .is-in. Hence the img, not the .deal__shot. */
.deal__shot[data-reveal],
.about__shot[data-reveal] { opacity: 1; transform: none; }
.deal__shot img,
.about__shot img,
.card figure,
.clip__img { clip-path: inset(100% 0 0 0); }
.card figure,
.clip__img { transition: clip-path 1.4s var(--ease-io) var(--d, 0s); }
.deal__shot.is-in img,
.about__shot.is-in img,
.card.is-in figure,
.clip.is-in .clip__img { clip-path: inset(0); }

.deal__shot img,
.about__shot img,
.card img,
.clip__img img { scale: 1.16; }

.deal__shot.is-in img,
.about__shot.is-in img,
.card.is-in img,
.clip.is-in .clip__img img { scale: 1; }

/* each keeps the transitions site.css gave it for hover */
.deal__shot img  { transition: transform 1.4s var(--ease), scale 2.2s var(--ease), clip-path 1.5s var(--ease-io); }
.about__shot img { transition: scale 2.2s var(--ease), clip-path 1.5s var(--ease-io); }
.card img        { transition: transform 1.2s var(--ease), filter .6s var(--ease), scale 2.2s var(--ease) var(--d, 0s); }
.clip__img img   { transition: transform 1.4s var(--ease), scale 2.2s var(--ease) var(--d, 0s); }

.card:nth-child(2) { --d: .1s; }
.card:nth-child(3) { --d: .2s; }
.card:nth-child(4) { --d: .3s; }

/* the deal copy comes in piece by piece once the curtain is moving */
.deal__copy[data-reveal] { opacity: 1; transform: none; }
.deal__copy[data-reveal] > * {
  opacity: 0; translate: 0 22px;
  transition: opacity 1s var(--ease) var(--k, 0s), translate 1.1s var(--ease) var(--k, 0s);
}
.deal__copy[data-reveal].is-in > * { opacity: 1; translate: 0 0; }
.deal__copy > :nth-child(1) { --k: .25s; }
.deal__copy > :nth-child(2) { --k: .33s; }
.deal__copy > :nth-child(3) { --k: .41s; }
.deal__copy > :nth-child(4) { --k: .5s; }

/* hairlines draw instead of appearing */
.deal__meta, .press__item, .cta__tel { position: relative; border-top-color: transparent; }
.deal__meta::before,
.press__item::before,
.cta__tel::before {
  content: ''; position: absolute; left: 0; right: 0; top: -1px; height: 1px;
  background: var(--line);
  transform-origin: 0 50%; scale: 0 1;
  transition: scale 1.4s var(--ease-io) var(--d, 0s);
}
.press__item::before { background: var(--line-2); }
.deal__meta::before  { transition-delay: .6s; }
.deal__copy.is-in .deal__meta::before,
.press__item.is-in::before,
.cta__tel.is-in::before { scale: 1 1; }

/* the About signature is written, like the hero's. The rotation is
   restated because `[data-reveal].is-in { transform: none }` used to
   straighten it the moment it revealed. The pen runs on the inner span,
   for the same IntersectionObserver reason as the photographs. */
.about__sig[data-reveal],
.about__sig[data-reveal].is-in { opacity: 1; transform: rotate(-4deg); }
.about__sig > span {
  display: inline-block;
  clip-path: inset(-60% 100% -60% -12%);
  transition: clip-path 2s var(--ease-ink) .5s;
}
.about__sig.is-in > span { clip-path: inset(-60% -12% -60% -12%); }

/* the contact form fills in row by row */
.form[data-reveal] { opacity: 1; transform: none; }
.form[data-reveal] > * {
  opacity: 0; translate: 0 18px;
  transition: opacity .9s var(--ease) var(--k, 0s), translate 1s var(--ease) var(--k, 0s);
}
.form[data-reveal].is-in > * { opacity: 1; translate: 0 0; }
.form > :nth-child(2) { --k: .07s; }
.form > :nth-child(3) { --k: .14s; }
.form > :nth-child(4) { --k: .21s; }
.form > :nth-child(5) { --k: .3s; }

/* the submit button fills with gold from the left instead of switching */
.form__go {
  background: linear-gradient(var(--gold), var(--gold)) 0 0 / 0% 100% no-repeat, var(--cream);
}
.form__go:hover {
  background: linear-gradient(var(--gold), var(--gold)) 0 0 / 100% 100% no-repeat, var(--cream);
}
.form[data-reveal] > .form__go {
  transition: opacity .9s var(--ease) var(--k), translate 1s var(--ease) var(--k),
              background-size .6s var(--ease), border-color .6s var(--ease),
              color .3s var(--ease), transform .12s ease;
}

/* ======================================================== marketing ===== */
/* A new channel wipes up OVER the one on screen. The outgoing panel stays
   visible underneath until the wipe has covered it, then resets itself so
   it can wipe in again next time. */

.mk__stage[data-reveal] { opacity: 1; transform: none; }

.mk__panel { opacity: 1; visibility: hidden; transition: visibility 0s linear 1.3s; }
.mk__panel.is-on { visibility: visible; z-index: 1; transition: none; }

.mk__panel img {
  clip-path: inset(100% 0 0 0);
  transition: clip-path 0s linear 1.3s;
}
.mk__stage.is-in .mk__panel.is-on img {
  clip-path: inset(0);
  transition: clip-path 1.25s var(--ease-io);
}

.mk__cap { opacity: 0; translate: 0 10px; transition: opacity .25s var(--ease), translate .25s var(--ease); }
.mk__stage.is-in .mk__panel.is-on .mk__cap {
  opacity: 1; translate: 0 0;
  transition: opacity .9s var(--ease) .45s, translate 1s var(--ease) .45s;
}

/* the selected channel draws a gold rule along its top hairline */
.mk__tab { position: relative; }
.mk__tab::after {
  content: ''; position: absolute; left: 0; right: 0; top: -1px; height: 1px;
  background: var(--gold);
  transform-origin: 0 50%; scale: 0 1;
  transition: scale .9s var(--ease-io);
}
.mk__tab[aria-selected="true"]::after { scale: 1 1; }

/* ===================================================== testimonials ===== */
/* the old quote blurs out first, the new one comes in after it */

.says__q {
  filter: blur(6px);
  transition: opacity .45s var(--ease), transform .5s var(--ease),
              filter .45s var(--ease), visibility 0s linear .5s;
}
.says__q.is-on {
  filter: blur(0);
  transition: opacity 1s var(--ease) .35s, transform 1.1s var(--ease) .35s,
              filter 1s var(--ease) .35s, visibility 0s;
}
.says__q footer { opacity: 0; transition: opacity .3s var(--ease); }
.says__q.is-on footer { opacity: 1; transition: opacity 1s var(--ease) .75s; }

/* the active dot is the timer: it fills while the quotes are rotating, and
   stands full while they are held (hover, reduced motion, off screen) */
.says__dots button { position: relative; overflow: hidden; }
.says__dots button[aria-selected="true"] { background: var(--line-2); }
.says__dots button::after {
  content: ''; position: absolute; inset: 0;
  background: var(--gold);
  transform-origin: 0 50%; scale: 0 1;
}
.says__dots button[aria-selected="true"]::after { scale: 1 1; }
.says__dots.is-playing button[aria-selected="true"]::after { animation: dot-fill 6.5s linear both; }
@keyframes dot-fill { from { scale: 0 1; } to { scale: 1 1; } }

/* ======================================================= breakpoints ==== */

@media (max-width: 1000px) {
  .about__sig[data-reveal],
  .about__sig[data-reveal].is-in { transform: rotate(-3deg); }
}
