/* ==========================================================================
   LORQEN — FX
   The premium motion layer
   --------------------------------------------------------------------------
   This file contains no colour and no type. It is the MECHANICS of the
   motion — what moves, from where, in what order — expressed entirely
   against custom properties that each theme sets in its own tokens.css.
   Three themes load this file and none of them move alike, because every
   number in it is a knob:

     --fx-ease        the master curve. Change this one value and the whole
                      site changes character.
     --fx-travel      how far a block rises into place.
     --fx-travel-x    how far a side-entering block comes in from.
     --fx-scale-in    the scale a block starts at (1 = no scale).
     --fx-blur-in     the blur a block starts at (0 = none).
     --fx-stagger     the gap between two members of a group.
     --fx-dur-in      how long a block takes to arrive.
     --fx-line-dur    how long a headline line takes.
     --fx-curtain-*   the page transition.

   It loads AFTER base.css and deliberately outranks the shared foundation's
   fourteen-pixel fade — which is the note the client sent back — while
   leaving every structural and accessibility mechanic in that file intact.
   The selectors carry `html.fx.js body` for exactly that reason: the
   foundation's rule is `html.js :is(…)`, and a tie would be settled by file
   order alone, which is too fragile a thing to rest a whole design on.
   ========================================================================== */

/* :where(:root), not :root.

   This block is a FALLBACK, not a setting. Each theme declares its own values
   for all of these in its tokens.css — and tokens.css is loaded FIRST, before
   this file, because it has to be: the component core between them reads it.
   Two :root blocks setting the same custom property are a specificity tie,
   and a tie is settled by source order, so a plain :root here would silently
   beat every theme's own numbers and all three themes would move identically.
   Which is precisely the note this whole file exists to answer.

   :where() zeroes the specificity, so any real declaration anywhere wins and
   these values only apply where a theme has genuinely said nothing. */
:where(:root) {
  /* Defaults. Every theme overrides these; nothing here is a design choice,
     it is only what keeps the file valid on its own. */
  --fx-ease:       cubic-bezier(0.22, 1, 0.36, 1);   /* expo-out           */
  --fx-ease-soft:  cubic-bezier(0.33, 1, 0.68, 1);
  --fx-ease-in:    cubic-bezier(0.55, 0, 1, 0.45);
  --fx-ease-rule:  cubic-bezier(0.65, 0, 0.35, 1);

  --fx-dur-in:     0.9s;
  --fx-dur-fast:   0.32s;
  --fx-dur-hover:  0.45s;
  --fx-line-dur:   1.05s;
  --fx-rule-dur:   1.1s;
  --fx-stagger:    90ms;
  --fx-line-stagger: 110ms;

  --fx-travel:     44px;
  --fx-travel-x:   58px;
  --fx-scale-in:   1;
  --fx-blur-in:    0px;
  --fx-rot-in:     0deg;

  --fx-curtain-bg:   #000;
  --fx-curtain-dur:  0.72s;
  --fx-curtain-out:  0.46s;
  --fx-curtain-ease: cubic-bezier(0.76, 0, 0.24, 1);

  --fx-parallax-scale: 1.14;
  --fx-zoom:           1.06;
  --fx-lift:           -6px;
}

/* ==========================================================================
   1. THE CURTAIN
   A single panel that covers the viewport. It is closed when the document
   arrives, retracts off the finished page, and closes again over the page
   being left. The mark rides on it so the brand, not a blank rectangle, is
   the thing on screen during the handover.
   ========================================================================== */

.fx-curtain {
  position: fixed;
  inset: 0;
  z-index: 400;
  pointer-events: none;
  display: grid;
  place-items: center;
  overflow: hidden;
}

.fx-curtain__panel {
  position: absolute;
  inset: 0;
  background: var(--fx-curtain-bg);
  transform-origin: center top;
  transform: scaleY(1);
  transition: transform var(--fx-curtain-dur) var(--fx-curtain-ease);
  will-change: transform;
}

.fx-curtain__mark {
  position: relative;
  z-index: 1;
  width: var(--fx-mark-size, 74px);
  height: var(--fx-mark-size, 74px);
  background: var(--fx-mark-color, currentColor);
  -webkit-mask: var(--fx-mark-src, none) center / contain no-repeat;
          mask: var(--fx-mark-src, none) center / contain no-repeat;
  opacity: 1;
  transform: scale(1);
  transition: opacity 0.34s var(--fx-ease), transform 0.5s var(--fx-ease);
}

/* Arrived: the panel lifts off the top and the mark goes with it. */
html.fx-arrived .fx-curtain__panel { transform: scaleY(0); }
html.fx-arrived .fx-curtain__mark  { opacity: 0; transform: scale(0.82); transition-delay: 0s; }

/* Leaving: it closes from the bottom, which is the opposite direction to
   the way it opened — the page is put away rather than un-done. */
html.fx-leaving .fx-curtain__panel {
  transform-origin: center bottom;
  transform: scaleY(1);
  transition-duration: var(--fx-curtain-out);
}
html.fx-leaving .fx-curtain__mark {
  opacity: 1;
  transform: scale(1);
  transition-delay: 0.14s;
}

/* Without the curtain script the panel must never exist over the page. */
html:not(.fx-curtain-on) .fx-curtain { display: none; }

/* ==========================================================================
   1b. DAMPED SCROLL
   Set by the script only when it has actually taken the wheel over. The
   foundation's `scroll-behavior: smooth` and a damping loop are two answers
   to the same question, and running both makes the page crawl: the
   two-argument scrollTo() obeys the property, so each frame starts a smooth
   animation the next frame interrupts. On touch, under reduced motion, and
   with JavaScript off this class is never added and the CSS smooth scroll
   stays exactly as the other twelve themes have it.
   ========================================================================== */

html.fx-damped { scroll-behavior: auto; }

/* ==========================================================================
   2. BLOCK REVEAL
   The shared foundation moves a block fourteen pixels and fades it. This
   moves it --fx-travel, optionally scales it, optionally unblurs it, and
   honours two things the page templates have always carried and no theme
   has ever read: the DIRECTION on data-reveal, and the DELAY in --d.
   ========================================================================== */

html.fx.js body :is([data-reveal], .fx-split) {
  opacity: 0;
  transform:
    translate3d(var(--fx-off-x, 0px), var(--fx-off-y, var(--fx-travel)), 0)
    scale(var(--fx-scale-in))
    rotate(var(--fx-rot-in));
  filter: blur(var(--fx-blur-in));
  transition:
    opacity   var(--fx-dur-in) var(--fx-ease),
    transform var(--fx-dur-in) var(--fx-ease),
    filter    var(--fx-dur-in) var(--fx-ease);
  transition-delay: var(--d, calc(var(--enter-i, 0) * var(--fx-stagger)));
  will-change: transform, opacity;
}

html.fx.js body [data-reveal="left"]  { --fx-off-x: calc(var(--fx-travel-x) * -1); --fx-off-y: 0px; }
html.fx.js body [data-reveal="right"] { --fx-off-x: var(--fx-travel-x);            --fx-off-y: 0px; }
html.fx.js body [data-reveal="none"]  { --fx-off-x: 0px; --fx-off-y: 0px; }

html.fx.js body :is([data-reveal], .fx-split).is-in {
  opacity: 1;
  transform: none;
  filter: none;
  will-change: auto;
}

/* ==========================================================================
   3. HEADLINE LINES
   The hero headline is already built as one mask per line in the shared
   markup. The foundation slides each line up by 150% on a 90ms stagger.
   This adds the two things that make it read as typeset rather than
   animated: a longer curve, and a slight lateral drift and skew so the line
   arrives on a diagonal instead of straight up a lift shaft.
   ========================================================================== */

html.fx.js .reveal-lines .rl > span {
  transform: translate3d(var(--fx-line-x, 0px), 150%, 0) skewY(var(--fx-line-skew, 0deg));
  transition: transform var(--fx-line-dur) var(--fx-ease);
  transition-delay: calc(var(--d, 0ms) + var(--rl-i, 0) * var(--fx-line-stagger));
}

html.fx.js .reveal-lines.is-in .rl > span { transform: none; }

/* Section headings, split into words and grouped into visual lines by the
   script. No mask here on purpose: a mask clips to the line box and a
   display face's declared descent is routinely shallower than the ink of
   its own j, y and g — the hero can afford the padding trick that fixes
   that because its markup is authored; a heading split at runtime cannot,
   because inflating an inline box changes where the browser breaks it. */
html.fx.js body .fx-split { opacity: 1; transform: none; filter: none; }

html.fx.js .fx-split .fx-w {
  display: inline-block;
  opacity: 0;
  transform: translate3d(0, var(--fx-word-travel, 0.5em), 0);
  transition:
    opacity   var(--fx-line-dur) var(--fx-ease),
    transform var(--fx-line-dur) var(--fx-ease);
  transition-delay: calc(var(--fx-l, 0) * var(--fx-line-stagger));
}

html.fx.js .fx-split.is-in .fx-w { opacity: 1; transform: none; }

/* ==========================================================================
   4. THE WIPE
   A retracting panel painted in the section's own ground, not a clip-path:
   a clipped element has a zero-area intersection rectangle and an
   IntersectionObserver watching it can never fire.
   ========================================================================== */

html.fx.js .wipe::after {
  transition: transform var(--fx-rule-dur) var(--fx-ease-rule);
  transition-delay: var(--d, 0ms);
}

/* ==========================================================================
   5. PARALLAX
   The offset is a custom property, not a transform, so the transform itself
   stays available to the stylesheet — which is what lets the hover zoom and
   the parallax drift share one element without fighting over it.
   ========================================================================== */

.fx-parallax {
  transform: translate3d(0, var(--fx-py, 0px), 0) scale(var(--fx-parallax-scale));
  transition: transform var(--fx-dur-hover) var(--fx-ease-soft);
  will-change: transform;
}

.zoom:hover .fx-parallax,
.zoom:focus-within .fx-parallax {
  transform:
    translate3d(0, var(--fx-py, 0px), 0)
    scale(calc(var(--fx-parallax-scale) * var(--fx-zoom)));
}

/* ==========================================================================
   6. HOVER CHOREOGRAPHY
   Every interactive thing on the page answers the pointer. This is the part
   the client felt the absence of most: twelve themes changed a colour on
   hover and nothing else moved.
   ========================================================================== */

/* --- Buttons -------------------------------------------------------------
   The shared component core swaps a button's background-color outright on
   hover. That is correct for a theme with no motion layer and wrong here:
   the fill would land before the sweep had left the floor and you would see
   both at once. So the mechanism moves into two pseudo-elements and the
   element's own background-color is taken out of play.

     ::before at z-index -2   the resting fill
     ::after  at z-index -1   the sweep
     the label                in flow, and therefore above both

   `isolation: isolate` makes the button its own stacking context, so those
   negative indices stay inside it. The COLOURS are not decided here — they
   are --fx-btn-rest and --fx-btn-sweep, which each theme sets. This file
   only decides how the fill arrives.
   ------------------------------------------------------------------------ */
html.fx body .btn {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background-color: transparent;
  transform: translate3d(var(--fx-mx, 0px), var(--fx-my, 0px), 0);
  transition:
    transform    var(--fx-dur-hover) var(--fx-ease),
    color        var(--fx-dur-fast)  var(--fx-ease),
    border-color var(--fx-dur-fast)  var(--fx-ease);
}

html.fx body .btn::before,
html.fx body .btn::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
}

html.fx body .btn::before { z-index: -2; background: var(--fx-btn-rest, transparent); }

html.fx body .btn::after {
  z-index: -1;
  background: var(--fx-btn-sweep, currentColor);
  transform: scaleY(0);
  transform-origin: center bottom;
  transition: transform var(--fx-dur-hover) var(--fx-ease);
}

html.fx body .btn:hover::after,
html.fx body .btn:focus-visible::after { transform: scaleY(1); }

html.fx body .btn:hover,
html.fx body .btn:focus-visible { color: var(--fx-btn-sweep-text, inherit); }

/* --- Links ---------------------------------------------------------------
   The core marks the current nav item with a small dot that scales in. This
   replaces it with a rule that DRAWS from the left and, on leaving, retracts
   to the right rather than reversing — so the mark never looks like it is
   undoing itself. The core's dot is sized by --mark-w and offset by a
   negative margin, both of which have to be explicitly unwound: leaving
   `width` set would beat `right: 0` and the rule would come out seven
   pixels long, which is exactly the bug this comment exists to prevent
   anyone reintroducing.
   ------------------------------------------------------------------------ */
html.fx body :is(.nav__link, .footer__list a, .footer__legal a, .link) {
  position: relative;
  transition: color var(--fx-dur-fast) var(--fx-ease);
}

html.fx body :is(.nav__link, .footer__list a, .footer__legal a, .link)::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  width: auto;
  margin: 0;
  bottom: var(--fx-underline-drop, -0.22em);
  height: var(--fx-underline-w, 1px);
  border-radius: 0;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform var(--fx-dur-hover) var(--fx-ease);
}

html.fx body :is(.nav__link, .footer__list a, .footer__legal a, .link):hover::after,
html.fx body :is(.nav__link, .footer__list a, .footer__legal a, .link):focus-visible::after,
html.fx body .nav__link[aria-current="page"]::after,
html.fx body .nav__link.is-active::after {
  transform: scaleX(1);
  transform-origin: left center;
}

/* --- Plates: they rise, and their edge sharpens -------------------------- */
:is(.card, .package, .mode, .selector__card, .callout, .quickbook__panel) {
  transition:
    transform    var(--fx-dur-hover) var(--fx-ease),
    box-shadow   var(--fx-dur-hover) var(--fx-ease),
    border-color var(--fx-dur-hover) var(--fx-ease),
    background   var(--fx-dur-hover) var(--fx-ease);
}

html.fx.js body :is(.card, .package, .mode, .selector__card).is-in:hover {
  transform: translate3d(0, var(--fx-lift), 0);
}

/* --- Ruled strips: they cannot lift, so they step in from the margin ----- */
:is(.assurance, .coverage, .region) {
  transition:
    padding-left var(--fx-dur-hover) var(--fx-ease),
    border-color var(--fx-dur-hover) var(--fx-ease),
    color        var(--fx-dur-hover) var(--fx-ease);
}
/* .pillar's own transition lives in signature.css now, on the soft curve. */

/* --- Media -------------------------------------------------------------- */
.zoom > *,
.media { transition: transform var(--fx-dur-hover) var(--fx-ease-soft); }

.zoom:hover > :not(.fx-parallax),
.zoom:focus-within > :not(.fx-parallax) { transform: scale(var(--fx-zoom)); }

/* --- Tilt ---------------------------------------------------------------- */
.fx-tilt {
  transform: perspective(900px) rotateX(var(--fx-rx, 0deg)) rotateY(var(--fx-ry, 0deg));
  transition: transform var(--fx-dur-hover) var(--fx-ease-soft);
  transform-style: preserve-3d;
}

/* ==========================================================================
   7. THE HEADER
   --fx-y runs 0 → 1 over the first 240 pixels of the page, so a theme can
   compress, tint or rule its header continuously rather than snapping
   between two states at a threshold.
   ========================================================================== */

.header {
  transition:
    transform  0.55s var(--fx-ease),
    background 0.55s var(--fx-ease),
    box-shadow 0.55s var(--fx-ease),
    border-color 0.55s var(--fx-ease),
    padding    0.55s var(--fx-ease);
}

/* ==========================================================================
   8. THE POINTER RING
   Damped, so it lags the cursor by a few frames and catches up — the lag is
   the effect. Never over touch, never over reduced motion, and the real
   cursor is only hidden where the ring is genuinely drawn in front of it.
   ========================================================================== */

.fx-ring {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 380;
  width: var(--fx-ring-size, 30px);
  height: var(--fx-ring-size, 30px);
  border: 1px solid var(--fx-ring-color, currentColor);
  border-radius: 50%;
  pointer-events: none;
  opacity: 0;
  transition:
    width      0.4s var(--fx-ease),
    height     0.4s var(--fx-ease),
    opacity    0.3s var(--fx-ease),
    background 0.3s var(--fx-ease),
    border-color 0.3s var(--fx-ease);
  mix-blend-mode: var(--fx-ring-blend, normal);
}

.fx-ring.is-live { opacity: var(--fx-ring-opacity, 0.7); }

.fx-ring.is-over {
  width:  calc(var(--fx-ring-size, 30px) * 2.1);
  height: calc(var(--fx-ring-size, 30px) * 2.1);
  background: var(--fx-ring-fill, transparent);
  opacity: 1;
}

/* ==========================================================================
   9. THE DRAWER
   Its items already carry --i in the markup. Twelve themes have not used it.
   ========================================================================== */

html.fx.js .drawer__item {
  opacity: 0;
  transform: translate3d(0, 18px, 0);
  transition:
    opacity   0.5s var(--fx-ease),
    transform 0.5s var(--fx-ease);
  transition-delay: calc(var(--i, 0) * 55ms + 120ms);
}

html.fx.js .drawer.is-open .drawer__item { opacity: 1; transform: none; }

/* ==========================================================================
   10. REDUCED MOTION
   Not "less motion" — none. The page renders in its finished state, the
   curtain never exists, and the wheel goes straight back to the browser.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .fx-curtain,
  .fx-ring { display: none !important; }

  html.fx.js body :is([data-reveal], .fx-split, .fx-split .fx-w, .drawer__item),
  html.fx.js .reveal-lines .rl > span {
    opacity: 1 !important;
    transform: none !important;
    filter: none !important;
    transition: none !important;
  }

  .fx-parallax { transform: none !important; }
  html.fx body .btn, .fx-tilt { transform: none !important; }
  html.fx body .btn::after { transition: none !important; }
}
