/* AQcredix — the cinematic reveal vocabulary.
 *
 * WHAT THIS ADDS, AND WHAT IT DELIBERATELY DOES NOT. motion.js already provides inertial
 * scroll, parallax, page transitions, split-text headings and a reveal that fires
 * automatically on every .section and .card. None of that is duplicated here. What was
 * missing is the *manner* of the reveal: a wipe rather than a fade, an image resolving from
 * blur rather than appearing, and a section whose parts arrive in a deliberate order instead
 * of all at once.
 *
 * THIS FILE HOLDS STATES; cinematic.js HOLDS MOVEMENT. That split is deliberate and it is
 * the fix for the bug this system had three times over. A CSS transition only runs if the
 * browser resolves the start value in one style change event and the end value in a later
 * one — so a reveal driven by a class flip is a reveal that works *if the timing happens to
 * work out*. On a page that renders its hero from data after DOMContentLoaded, injects its
 * portrait on img.onload, and has motion.js holding an opacity:0 on <body> for the first two
 * frames, the timing kept not working out: one element animated and its neighbours simply
 * appeared, with nothing wrong in the computed styles because nothing WAS wrong declaratively.
 *
 * So the transitions are gone from the variants below. What is left is two states — the
 * hidden one an element sits in before its turn, and the resting one it occupies afterwards —
 * and cinematic.js moves between them with element.animate(), which is handed both keyframes
 * explicitly and therefore cannot be skipped. It also cannot be masked: in the cascade,
 * transitions outrank animations, so leaving a transition declared here would have quietly
 * overridden the very thing meant to replace it.
 *
 * The numbers still live here, and cinematic.js reads them out of these custom properties at
 * start-up. Retuning the whole system is still five numbers in one place. (It reads them once,
 * so the mobile block below applies to the load that starts on a phone — reveals run at page
 * entry, not on resize, so recomputing them would buy nothing.)
 *
 * OPT-IN, ALWAYS. Every effect needs an attribute. Nothing changes appearance until it is
 * asked for, so this can be added a section at a time and can never silently alter a page.
 *
 * ONLY COMPOSITED PROPERTIES. transform, opacity, clip-path and filter. No layout property
 * is animated, so nothing here forces a reflow mid-scroll.
 *
 * ------------------------------------------------------------------------------
 * TUNING — change these and everything follows. They are the only numbers worth
 * touching; each effect is expressed in terms of them, here and in cinematic.js.
 * ------------------------------------------------------------------------------ */
:root{
  --cine-dur: 900ms;          /* how long one element takes to arrive          */
  --cine-stagger: 90ms;       /* gap between children in a sequence            */
  --cine-shift: 26px;         /* how far an element travels                    */
  --cine-blur: 12px;          /* how soft an image starts                      */
  --cine-scale: 1.06;         /* how much an image settles by                  */
  /* A long, flat tail is what separates a cinematic move from a UI animation: it arrives
     quickly and then decelerates for a long time. */
  --cine-ease: cubic-bezier(.16, 1, .3, 1);
}

/* Hidden until revealed. Scoped under html.aq-cine so that if the script fails to load,
   nothing is ever left invisible — the page simply renders without the effect. */
html.aq-cine [data-cine]{ will-change: transform, opacity; }

/* ---------------------------------------------------------------- 1. rise
   The default. Opacity plus a short lift — the quietest of the set, for body copy and
   anything that appears often enough that a stronger move would become noise. */
html.aq-cine [data-cine="rise"]{
  opacity:0;
  transform:translate3d(0, var(--cine-shift), 0);
}

/* ---------------------------------------------------------------- 2. wipe
   A clip-path reveal: the element is uncovered from below rather than faded in. This is the
   one that reads as editorial, and it is why headings get it and paragraphs do not. */
html.aq-cine [data-cine="wipe"]{
  clip-path:inset(0 0 100% 0);
  opacity:0;
  transform:translate3d(0, calc(var(--cine-shift) * .5), 0);
}

/* ---------------------------------------------------------------- 3. image
   Resolving from blur and settling out of a slight over-scale, behind a mask that lifts.
   The over-scale matters: an image that scales UP into place looks like a zoom, one that
   settles DOWN from slightly too large looks like focus being found. */
html.aq-cine [data-cine="image"]{
  clip-path:inset(0 0 100% 0);
  opacity:0;
  transform:scale(var(--cine-scale));
  filter:blur(var(--cine-blur));
}

/* ---------------------------------------------------------------- 3b. drop
   Arrives from above. Used for one element per page at most — the thing that should feel
   like it is coming to rest, rather than joining the sequence. */
html.aq-cine [data-cine="drop"]{
  opacity:0;
  transform:translate3d(0, calc(var(--cine-shift) * -1.4), 0);
  clip-path:inset(100% 0 0 0);
}

/* ---------------------------------------------------------------- 3c. rise-slow
   The portrait. It comes up from below the fold of the composition and is the last thing to
   settle, so it is slower than everything else and travels further — the eye should follow it
   up rather than notice it has appeared. No blur and no scale here: the reference simply
   raises the figure, and adding focus-pull on top made it busy. The distance is a percentage
   of the figure's own height, so it holds at any size. */
html.aq-cine [data-cine="riseslow"]{
  opacity:0;
  transform:translate3d(0, 26%, 0);
}

/* ---------------------------------------------------------------- 4. letters
   Per-word, for a short heading only. The script splits the text; each word carries its own
   index, which the stagger is computed from. */
html.aq-cine [data-cine="letters"]{ opacity:1; }
html.aq-cine [data-cine="letters"] .cine-w{
  display:inline-block;
  overflow:hidden;
  vertical-align:bottom;
}
html.aq-cine [data-cine="letters"] .cine-w i{
  display:inline-block;
  font-style:inherit;
  transform:translate3d(0, 105%, 0);
}

/* ---------------------------------------------------------------- arrived
   The resting state, and the reason the class can be applied BEFORE the animation starts:
   whatever happens to the animation — never started, cancelled by a resize, unsupported —
   what is left behind is the finished element rather than a hidden one. */
html.aq-cine [data-cine].is-cine-in{
  opacity:1;
  transform:none;
  clip-path:none;
  filter:none;
}
html.aq-cine [data-cine="letters"].is-cine-in .cine-w i{ transform:translate3d(0,0,0); }

/* --------------------------------------------- while the reveal is playing
   TRANSITIONS OUTRANK ANIMATIONS IN THE CASCADE. So any `transition` an element already
   carries for its own reasons quietly takes the property over and the reveal is replaced by
   whatever that transition says. This is not hypothetical: .fp-photo still has
   `transition: transform 220ms ease` from its days as a small circular avatar, and it was
   overriding the founder portrait's 1800ms rise with a 220ms snap — which is precisely why the
   figure was the one element on that page that never looked animated.

   cinematic.js puts this class on before it changes the state and takes it off when the last
   track finishes, so nothing can shadow the reveal while it runs and the element's own hover
   effects work normally again the moment it is over. */
html.aq-cine [data-cine].is-cine-run,
html.aq-cine [data-cine].is-cine-run .cine-w i{
  transition:none !important;
}

/* ------------------------------------------------------- the hard end state
   is-cine-shown is what the backstop applies: !important, so it outranks even a running
   animation, for the case where an element is somehow still holding its first keyframe.
   cinematic.js cancels the animations alongside it, so the two cannot disagree. */
html.aq-cine [data-cine].is-cine-shown,
html.aq-cine [data-cine="letters"].is-cine-shown .cine-w i{
  opacity:1 !important;
  transform:none !important;
  clip-path:none !important;
  filter:none !important;
  transition:none !important;
}

/* ------------------------------------------------------- 5. micro-interactions
   Applied to things that are already interactive. These stay CSS transitions: a hover is a
   state the pointer holds, not an entrance that has to be guaranteed to run. The easing is
   the same one the reveals use, which is most of what makes a set of small effects feel like
   one system. */
html.aq-cine [data-cine-hover]{
  transition:transform 420ms var(--cine-ease), opacity 420ms var(--cine-ease);
}
html.aq-cine [data-cine-hover]:hover{ transform:translate3d(0,-2px,0); }

/* An underline that draws from the left rather than appearing. */
html.aq-cine [data-cine-underline]{ position:relative; }
html.aq-cine [data-cine-underline]::after{
  content:""; position:absolute; left:0; right:0; bottom:-2px; height:1px;
  background:currentColor; opacity:.55;
  transform:scaleX(0); transform-origin:left center;
  transition:transform 520ms var(--cine-ease);
}
html.aq-cine [data-cine-underline]:hover::after,
html.aq-cine [data-cine-underline]:focus-visible::after{ transform:scaleX(1); }

/* ---------------------------------------------------------------- honesty
   Reduced motion gets the content, in place, immediately. Not a shorter animation — none.
   The script also returns before observing anything, so nothing is left hidden waiting for a
   scroll. */
@media (prefers-reduced-motion: reduce){
  html.aq-cine [data-cine],
  html.aq-cine [data-cine="letters"] .cine-w i{
    opacity:1 !important;
    transform:none !important;
    clip-path:none !important;
    filter:none !important;
    transition:none !important;
    animation:none !important;
  }
  html.aq-cine [data-cine-hover],
  html.aq-cine [data-cine-underline]::after{ transition:none !important; }
}

/* A phone scrolls faster relative to the viewport, so the same durations read as sluggish.
   Shorter and shallower, rather than switched off. */
@media (max-width: 720px){
  :root{
    --cine-dur: 700ms;
    --cine-stagger: 65ms;
    --cine-shift: 18px;
    --cine-blur: 8px;
    --cine-scale: 1.04;
  }
}

/* Printing a page mid-reveal would print blank elements. */
@media print{
  html.aq-cine [data-cine]{
    opacity:1 !important; transform:none !important;
    clip-path:none !important; filter:none !important;
  }
}
