/* ===========================================================
   Infloowo motion system
   -----------------------------------------------------------
   Pages opt in with head-motion.php (in <head>) and then use
   data attributes only; pages never contain animation CSS.

     data-motion="slide-down | fade | drift | drop | rise | pop | draw"
                                 effect(s), space-separated ("rise draw")
     data-motion-on="load | view"  page-load sequence, or once when
                                 scrolled into view (default: view)
     data-motion-delay="ms"      delay from sequence start / entering view
     data-motion-group="effect"  on a parent: children get the effect,
                                 staggered by --motion-stagger each
     data-motion-duration="ms"   override the effect's default duration
     data-motion-distance="px"   override the effect's default distance
     data-motion-draw-delay="ms" delay for "draw" when combined

   Rules: only transform and opacity animate (no layout shift).
   Hidden start states exist only under html.motion-js, which the
   inline script in head-motion.php adds, so with JavaScript off
   everything is visible; html.motion-all-in (2 s safety net)
   reveals everything if js/motion.js fails. Behaviour lives in
   js/motion.js.
   =========================================================== */

:root {
  /* Durations */
  --motion-fast: 150ms;
  --motion-base: 250ms;
  --motion-slow: 450ms;
  --motion-header: 350ms;
  --motion-card: 200ms;          /* card hover lift */
  --motion-draw-duration: 400ms;

  /* Curves */
  --motion-ease-out: cubic-bezier(0.2, 0.7, 0.2, 1);
  --motion-ease-overshoot: cubic-bezier(0.34, 1.56, 0.64, 1);

  /* Distances */
  --motion-rise: 28px;
  --motion-drop: 8px;
  --motion-drift: 10px;
  --motion-header-distance: 60px;
  --motion-pop-scale: 0.9;

  /* Sequencing */
  --motion-stagger: 60ms;
}

/* ---- Effect defaults (duration / easing per effect) -------------- */
:is([data-motion~="slide-down"], [data-motion-group~="slide-down"] > *) { --motion-effect-duration: var(--motion-header); }
:is([data-motion~="fade"], [data-motion-group~="fade"] > *),
:is([data-motion~="drift"], [data-motion-group~="drift"] > *),
:is([data-motion~="drop"], [data-motion-group~="drop"] > *) { --motion-effect-duration: var(--motion-base); }
:is([data-motion~="rise"], [data-motion-group~="rise"] > *) { --motion-effect-duration: var(--motion-slow); }
:is([data-motion~="pop"], [data-motion-group~="pop"] > *) {
  --motion-effect-duration: var(--motion-slow);
  --motion-effect-easing: var(--motion-ease-overshoot);
}

/* ---- Start states (only with JS on, until revealed) -------------- */
html.motion-js:not(.motion-all-in) :is([data-motion]:not([data-motion="draw"]), [data-motion-group] > *):not(.motion-in) {
  opacity: 0;
}
html.motion-js:not(.motion-all-in) :is([data-motion~="slide-down"], [data-motion-group~="slide-down"] > *):not(.motion-in) {
  transform: translateY(calc(-1 * var(--motion-distance, var(--motion-header-distance))));
}
html.motion-js:not(.motion-all-in) :is([data-motion~="drift"], [data-motion-group~="drift"] > *):not(.motion-in) {
  transform: translateX(calc(-1 * var(--motion-distance, var(--motion-drift))));
}
html.motion-js:not(.motion-all-in) :is([data-motion~="drop"], [data-motion-group~="drop"] > *):not(.motion-in) {
  transform: translateY(calc(-1 * var(--motion-distance, var(--motion-drop))));
}
html.motion-js:not(.motion-all-in) :is([data-motion~="rise"], [data-motion-group~="rise"] > *):not(.motion-in) {
  transform: translateY(var(--motion-distance, var(--motion-rise)));
}
html.motion-js:not(.motion-all-in) :is([data-motion~="pop"], [data-motion-group~="pop"] > *):not(.motion-in) {
  transform: scale(var(--motion-pop-scale));
}

/* ---- Entrance (JS adds .motion-in; .motion-done drops these
        transitions afterwards so later hovers aren't delayed) ----- */
html.motion-js :is([data-motion], [data-motion-group] > *).motion-in:not(.motion-done) {
  --motion-total-delay: calc(var(--motion-delay-base, 0ms) + var(--motion-index, 0) * var(--motion-stagger));
  transition:
    opacity var(--motion-duration, var(--motion-effect-duration, var(--motion-base))) var(--motion-ease-out) var(--motion-total-delay),
    transform var(--motion-duration, var(--motion-effect-duration, var(--motion-base))) var(--motion-effect-easing, var(--motion-ease-out)) var(--motion-total-delay);
}

/* ---- "draw": a bar sweeps across the element's top border -------- */
.card-accent-blue { --motion-draw-color: var(--color-logo-blue); }
.card-accent-orange { --motion-draw-color: var(--color-accent); }

html.motion-js:not(.motion-all-in):not(.motion-calm) [data-motion~="draw"]:not(.motion-done) {
  border-top-color: transparent; /* the bar stands in for it until drawn */
}
html.motion-js:not(.motion-all-in):not(.motion-calm) [data-motion~="draw"]:not(.motion-done)::before {
  content: "";
  position: absolute;
  z-index: 1;
  top: calc(-1 * var(--motion-draw-width, 3px));
  left: calc(-1 * var(--bs-card-border-width, 1px));
  right: calc(-1 * var(--bs-card-border-width, 1px));
  height: var(--motion-draw-width, 3px);
  background: var(--motion-draw-color, currentColor);
  border-top-left-radius: inherit;
  border-top-right-radius: inherit;
  transform: scaleX(0);
  transform-origin: left center;
  pointer-events: none;
}
html.motion-js:not(.motion-all-in):not(.motion-calm) [data-motion~="draw"].motion-in:not(.motion-done)::before {
  transform: scaleX(1);
  transition: transform var(--motion-draw-duration) var(--motion-ease-out)
    var(--motion-draw-delay, var(--motion-total-delay, 0ms));
}

/* ---- Calm mode: reduced motion, or a repeat view in the same
        session. Fade only, fast, no delays. ----------------------- */
html.motion-js.motion-calm :is([data-motion], [data-motion-group] > *):not(.motion-in) {
  transform: none;
}
html.motion-js.motion-calm :is([data-motion], [data-motion-group] > *).motion-in:not(.motion-done) {
  transition: opacity var(--motion-fast) linear 0ms;
}

/* ---- Safety net reveal: quick fade, no movement ------------------ */
html.motion-all-in :is([data-motion], [data-motion-group] > *):not(.motion-done) {
  transition: opacity var(--motion-fast) linear;
}

/* ---- Loading indicator: thin bar at the very top, only while the
        page is loading and only after 300 ms (fast loads never see
        it). Fixed position and pointer-events: none, so it never
        shifts layout or blocks a click. ------------------------- */
html.motion-js:not(.motion-ready):not(.motion-all-in) body::before {
  content: "";
  position: fixed;
  top: 0;
  left: 0;
  z-index: 2000;
  width: 40%;
  height: 3px;
  background: var(--color-accent);
  opacity: 0;
  pointer-events: none;
  animation:
    motion-loader-in var(--motion-base) linear 300ms forwards,
    motion-loader-sweep 1.1s var(--motion-ease-out) 300ms infinite;
}
@keyframes motion-loader-in { to { opacity: 1; } }
@keyframes motion-loader-sweep {
  from { transform: translateX(-100%); }
  to { transform: translateX(250%); }
}
@media (prefers-reduced-motion: reduce) {
  html.motion-js:not(.motion-ready):not(.motion-all-in) body::before {
    width: 100%;
    animation: motion-loader-in var(--motion-fast) linear 300ms forwards;
  }
}

/* ===========================================================
   Global interactions — any page that loads this file
   =========================================================== */

/* ---- Buttons: lift + soft shadow on hover, slight press ---------- */
.btn:not(.btn-link) {
  position: relative; /* anchors the shadow layer; no layout effect */
  transition:
    color 0.15s ease-in-out,
    background-color 0.15s ease-in-out,
    border-color 0.15s ease-in-out,
    box-shadow 0.15s ease-in-out,
    transform var(--motion-fast) var(--motion-ease-out);
}
/* Shadow on its own layer so it fades by opacity. Outer shadow only,
   so the transparent layer never covers the label.
   The layer only exists while hovered (see the hover block below):
   a shadow that is always present, even invisible, draws outside the
   element's box. Inside the white sticky header that made Chrome/Edge
   paint a white bar under the header during the entrance animations,
   so nothing here may overhang an element at rest. */

/* ---- Cards that are links or hold a primary action (never form
        cards): lift + deeper shadow on hover --------------------- */
:is(a.card, .card:has(.btn-primary, .btn-outline-primary)):not(form):not(:has(form, input, select, textarea)) {
  transition: transform var(--motion-card) var(--motion-ease-out);
}

/* ---- Nav links: underline slides in from the left ---------------- */
.navbar .nav-link {
  position: relative;
}
.navbar .nav-link::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0.25rem;
  height: 2px;
  border-radius: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left center;
  pointer-events: none;
  transition: transform var(--motion-base) var(--motion-ease-out);
}
@media (min-width: 768px) {
  .navbar-expand-md .nav-link::after {
    left: var(--bs-navbar-nav-link-padding-x, 0.5rem);
    right: var(--bs-navbar-nav-link-padding-x, 0.5rem);
  }
}
/* Keyboard focus shows the same underline (plus Bootstrap's ring). */
.navbar .nav-link:focus-visible::after {
  transform: scaleX(1);
}

/* ---- Form fields: focus ring fades (Bootstrap animates the
        box-shadow; this just ties it to the motion token) -------- */
.form-control,
.form-select {
  transition-duration: var(--motion-fast);
}

/* ---- Sticky header: soft shadow fades in once scrolled ----------- */
/* The shadow layer exists only while scrolled. A permanent layer hanging
   12px below the header made Chrome (GPU compositing) paint a white strip
   under the header during the entrance animations, when the header is a
   separate layer overlapping the animated content. */
header.sticky-top.is-scrolled::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 100%;
  height: 12px;
  background: linear-gradient(rgba(16, 24, 40, 0.08), rgba(16, 24, 40, 0));
  pointer-events: none;
  animation: motion-fade-in var(--motion-base) var(--motion-ease-out) both;
}
@keyframes motion-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}

/* ---- Hover states: only on devices that can really hover (no
        sticky hover on touch), and never with reduced motion ---- */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  .btn:not(.btn-link):not(:disabled):not(.disabled):hover {
    transform: translateY(-2px);
  }
  /* Shadow layers exist only while hovered, and fade in by opacity. */
  .btn:not(.btn-link):not(.dropdown-toggle):not(:disabled):not(.disabled):hover::after,
  :is(a.card, .card:has(.btn-primary, .btn-outline-primary)):not(form):not(:has(form, input, select, textarea)):hover::after {
    content: "";
    position: absolute;
    inset: 0;
    border-radius: inherit;
    pointer-events: none;
    animation: motion-fade-in var(--motion-fast) var(--motion-ease-out) both;
  }
  .btn:not(.btn-link):not(.dropdown-toggle):not(:disabled):not(.disabled):hover::after {
    box-shadow: 0 6px 14px rgba(16, 24, 40, 0.16);
  }
  :is(a.card, .card:has(.btn-primary, .btn-outline-primary)):not(form):not(:has(form, input, select, textarea)):hover {
    transform: translateY(-4px);
  }
  :is(a.card, .card:has(.btn-primary, .btn-outline-primary)):not(form):not(:has(form, input, select, textarea)):hover::after {
    box-shadow: 0 14px 30px rgba(16, 24, 40, 0.12);
    animation-duration: var(--motion-card);
  }
  .navbar .nav-link:hover::after {
    transform: scaleX(1);
  }
}

/* Press feedback works on touch too (it ends when the finger lifts). */
@media (prefers-reduced-motion: no-preference) {
  .btn:not(.btn-link):not(:disabled):not(.disabled):active {
    transform: translateY(0) scale(0.97);
  }
}

/* Reduced motion: no movement anywhere; underline fades instead. */
@media (prefers-reduced-motion: reduce) {
  .btn:not(.btn-link) {
    transition: color 0.15s ease-in-out, background-color 0.15s ease-in-out, border-color 0.15s ease-in-out;
  }
  .navbar .nav-link::after {
    transform: none;
    opacity: 0;
    transition: opacity var(--motion-fast) linear;
  }
  .navbar .nav-link:focus-visible::after {
    opacity: 1;
  }
}
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: reduce) {
  .navbar .nav-link:hover::after {
    opacity: 1;
  }
}
