/* ==========================================================================
   HireJuno — motion.css
   The motion layer: scroll reveals, hover/press feedback, skeletons,
   page + step transitions, focus rings.

   LOAD ORDER (required):
     brand.css -> redesign.css -> [page css] -> tokens.css -> motion.css

   RULES THIS FILE OBEYS
     1. State transitions are 150–300ms. No exceptions.
     2. Only `transform`, `scale`, `translate`, `rotate`, `opacity`, `filter`
        and `box-shadow` are animated. Never width/height/top/left/margin —
        those trigger layout and cause jank + CLS.
     3. No hover moves a box in a way that shifts its siblings. Hovers use
        transform, which paints outside layout flow.
     4. Everything is off under `prefers-reduced-motion: reduce`, and any
        element that was hidden pending an animation becomes VISIBLE.

     Documented exception — CONTINUOUS indicators (skeleton shimmer, spinner,
     live pulse, ticker) are loops, not state transitions. A 300ms spinner is
     a strobe. These run 900–1600ms, are listed in §4, and are also fully
     disabled under reduced motion.

   JS REQUIREMENT
     Scroll reveals need ~14 lines of IntersectionObserver. The snippet is in
     design-system/hirejuno/IMPLEMENTATION.md §5. It sets `html.hj-js`.
     Until that class exists NOTHING is hidden — if JS fails or is blocked,
     every reveal element renders normally. Content is never JS-dependent.
   ========================================================================== */

:root {
  /* Durations — state transitions only */
  --hj-dur-instant: 100ms;  /* colour-only swaps, focus ring        */
  --hj-dur-fast:    150ms;  /* hover, press, tint                   */
  --hj-dur:         200ms;  /* default: buttons, cards, inputs      */
  --hj-dur-slow:    260ms;  /* reveals, panels, step changes        */
  --hj-dur-slowest: 300ms;  /* modals, sheets, page enter — ceiling */

  /* Easing */
  --hj-ease:          cubic-bezier(.22, .61, .36, 1);   /* default decelerate */
  --hj-ease-out:      cubic-bezier(.16, 1, .3, 1);      /* expressive out     */
  --hj-ease-in-out:   cubic-bezier(.4, 0, .2, 1);       /* symmetric          */
  --hj-ease-emphasis: cubic-bezier(.2, .7, .3, 1);      /* snappy, tactile    */

  /* Reveal geometry — override per element */
  --hj-reveal-y: 14px;
  --hj-reveal-x: 0px;
  --hj-reveal-scale: 1;
  --hj-stagger-step: 55ms;
  --hj-stagger-max: 440ms;
}


/* ==========================================================================
   1. SCROLL REVEALS  (IntersectionObserver-driven)
   Usage:  <div class="hj-reveal hj-reveal--up">…</div>
   JS adds `.is-in` when the element enters the viewport.
   ========================================================================== */

.hj-reveal { will-change: auto; }

/* Only hide once JS has confirmed it can un-hide. */
html.hj-js .hj-reveal:not(.is-in) {
  opacity: 0;
  translate: var(--hj-reveal-x) var(--hj-reveal-y);
  scale: var(--hj-reveal-scale);
}

html.hj-js .hj-reveal {
  transition:
    opacity   var(--hj-dur-slow) var(--hj-ease),
    translate var(--hj-dur-slow) var(--hj-ease),
    scale     var(--hj-dur-slow) var(--hj-ease);
  transition-delay: var(--hj-reveal-delay, 0ms);
}

/* Directions */
.hj-reveal--up    { --hj-reveal-y: 14px; --hj-reveal-x: 0; }
.hj-reveal--down  { --hj-reveal-y: -14px; --hj-reveal-x: 0; }
.hj-reveal--left  { --hj-reveal-x: 20px; --hj-reveal-y: 0; }
.hj-reveal--right { --hj-reveal-x: -20px; --hj-reveal-y: 0; }
.hj-reveal--fade  { --hj-reveal-y: 0; --hj-reveal-x: 0; }
.hj-reveal--pop   { --hj-reveal-y: 6px; --hj-reveal-scale: .96; }
.hj-reveal--far   { --hj-reveal-y: 28px; }

/* Stagger — put on the PARENT, children reveal in sequence.
   Capped at 8 so a 40-card grid never waits 2 seconds. */
.hj-reveal-group > *  { --hj-reveal-delay: 0ms; }
.hj-reveal-group > :nth-child(1) { --hj-reveal-delay: 0ms; }
.hj-reveal-group > :nth-child(2) { --hj-reveal-delay: calc(1 * var(--hj-stagger-step)); }
.hj-reveal-group > :nth-child(3) { --hj-reveal-delay: calc(2 * var(--hj-stagger-step)); }
.hj-reveal-group > :nth-child(4) { --hj-reveal-delay: calc(3 * var(--hj-stagger-step)); }
.hj-reveal-group > :nth-child(5) { --hj-reveal-delay: calc(4 * var(--hj-stagger-step)); }
.hj-reveal-group > :nth-child(6) { --hj-reveal-delay: calc(5 * var(--hj-stagger-step)); }
.hj-reveal-group > :nth-child(7) { --hj-reveal-delay: calc(6 * var(--hj-stagger-step)); }
.hj-reveal-group > :nth-child(n+8) { --hj-reveal-delay: var(--hj-stagger-max); }


/* ==========================================================================
   2. HOVER + PRESS FEEDBACK
   All transform-based. Nothing here can shift a sibling.
   ========================================================================== */

/* Base interactive transition — attach to any control. */
.hj-interactive {
  transition:
    translate  var(--hj-dur) var(--hj-ease-emphasis),
    scale      var(--hj-dur-fast) var(--hj-ease-emphasis),
    box-shadow var(--hj-dur) var(--hj-ease),
    background-color var(--hj-dur-fast) linear,
    border-color     var(--hj-dur-fast) linear,
    color            var(--hj-dur-fast) linear,
    opacity          var(--hj-dur-fast) linear;
}

/* Lift — the default card/tile hover. translate only. */
.hj-hover-lift { transition: translate var(--hj-dur) var(--hj-ease-emphasis), box-shadow var(--hj-dur) var(--hj-ease); }
.hj-hover-lift:hover { translate: 0 -3px; box-shadow: var(--hj-elev-3); }

/* Raise — shadow deepens, nothing moves. Use in dense grids/tables. */
.hj-hover-raise { transition: box-shadow var(--hj-dur) var(--hj-ease), border-color var(--hj-dur-fast) linear; }
.hj-hover-raise:hover { box-shadow: var(--hj-elev-2); border-color: var(--hj-border-strong); }

/* Press — tactile, native-app feel. `scale` never affects layout. */
.hj-press { transition: scale var(--hj-dur-fast) var(--hj-ease-emphasis); -webkit-tap-highlight-color: transparent; }
.hj-press:active { scale: .97; }
.hj-press-sm:active { scale: .985; }

/* Tint — colour-only hover for links/menu rows. */
.hj-hover-tint { transition: background-color var(--hj-dur-fast) linear, color var(--hj-dur-fast) linear; }
.hj-hover-tint:hover { background: var(--hj-surface-sunken); }

/* Arrow nudge — the "→" that slides on card hover. */
.hj-nudge > svg, .hj-nudge > .hj-arrow { transition: translate var(--hj-dur) var(--hj-ease-emphasis); }
.hj-nudge:hover > svg, .hj-nudge:hover > .hj-arrow { translate: 4px 0; }

/* Underline grow — scaleX, so no reflow and no text jump. */
.hj-underline { position: relative; text-decoration: none; }
.hj-underline::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: -2px; height: 2px;
  background: currentColor; transform-origin: left center;
  scale: 0 1; transition: scale var(--hj-dur) var(--hj-ease-emphasis);
}
.hj-underline:hover::after, .hj-underline:focus-visible::after { scale: 1 1; }

/* Media zoom — image scales INSIDE a clipped frame. Frame size is fixed,
   so the grid never reflows. */
.hj-zoom-frame { overflow: hidden; }
.hj-zoom-frame > img, .hj-zoom-frame > .hj-zoom-target { transition: scale var(--hj-dur-slowest) var(--hj-ease); }
.hj-zoom-frame:hover > img, .hj-zoom-frame:hover > .hj-zoom-target { scale: 1.045; }


/* ==========================================================================
   3. FOCUS RINGS
   The ring itself appears INSTANTLY (a11y — never delay focus feedback).
   Only the soft halo fades in, at 100ms.
   ========================================================================== */
.hj-focus-halo { transition: box-shadow var(--hj-dur-instant) linear; }
.hj-focus-halo:focus-visible { box-shadow: 0 0 0 4px var(--hj-focus-halo); }

/* Input focus — border tint + halo, no size change.
   NOTE: this deliberately never writes `outline: none`. The keyboard ring
   from tokens.css §11 must survive, because a border-colour change alone is
   defeated by any inline `border:` shorthand on the element, and a halo alone
   is defeated by any component that sets its own box-shadow. The outline is
   the one indicator nothing else can accidentally clobber. */
.hj-field {
  transition: border-color var(--hj-dur-fast) linear, box-shadow var(--hj-dur-instant) linear;
}
.hj-field:focus-visible {
  border-color: var(--hj-accent-strong);
  box-shadow: 0 0 0 3px var(--hj-focus-halo);
  outline-offset: 1px;   /* tuck the ring against the halo */
}
/* Pointer/touch focus: softer treatment, no hard ring. */
.hj-field:focus:not(:focus-visible) {
  outline: none;
  border-color: var(--hj-accent-strong);
  box-shadow: 0 0 0 3px var(--hj-focus-halo);
}
.hj-field:user-invalid, .hj-field.is-invalid {
  border-color: var(--hj-danger);
  box-shadow: 0 0 0 3px rgb(185 48 48 / .18);
}


/* ==========================================================================
   4. LOADING STATES  (continuous — see header note on the 150–300ms rule)
   ========================================================================== */

/* Skeleton — shimmer is a translated overlay, NOT an animated gradient
   position, so it stays on the compositor. */
.hj-skeleton {
  position: relative; overflow: hidden;
  background: var(--hj-surface-sunken);
  border-radius: var(--hj-radius-sm);
  color: transparent !important;
  user-select: none; pointer-events: none;
}
.hj-skeleton::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg,
    transparent 0%,
    rgb(255 255 255 / .55) 50%,
    transparent 100%);
  translate: -100% 0;
  animation: hj-shimmer 1400ms var(--hj-ease-in-out) infinite;
}

@keyframes hj-shimmer { to { translate: 100% 0; } }

.hj-skeleton--text   { height: 1em; margin-block: .25em; border-radius: var(--hj-radius-xs); }
.hj-skeleton--title  { height: 1.6em; width: 60%; border-radius: var(--hj-radius-xs); }
.hj-skeleton--line   { height: 1em; width: 100%; }
.hj-skeleton--line-short { height: 1em; width: 72%; }
.hj-skeleton--circle { border-radius: 50%; aspect-ratio: 1; width: 48px; }
.hj-skeleton--card   { height: 168px; border-radius: var(--hj-radius-lg); }
.hj-skeleton--pill   { height: 30px; width: 88px; border-radius: var(--hj-radius-pill); }

/* Spinner — rotate only. */
.hj-spinner {
  display: inline-block; width: 1.1em; height: 1.1em; flex: none;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%; opacity: .8;
  animation: hj-spin 900ms linear infinite;
}
@keyframes hj-spin { to { rotate: 360deg; } }

/* Button in-flight: swap label for spinner without changing button width. */
.hj-btn-loading { position: relative; pointer-events: none; }
.hj-btn-loading > * { opacity: 0; transition: opacity var(--hj-dur-fast) linear; }
.hj-btn-loading::after {
  content: ""; position: absolute; inset: 50% auto auto 50%; translate: -50% -50%;
  width: 1.1em; height: 1.1em;
  border: 2px solid currentColor; border-right-color: transparent; border-radius: 50%;
  animation: hj-spin 900ms linear infinite;
}

/* Live pulse dot — "3 pros online now". */
.hj-pulse { position: relative; }
.hj-pulse::before {
  content: ""; position: absolute; inset: 0; border-radius: inherit;
  background: currentColor; opacity: .45;
  animation: hj-pulse 1600ms var(--hj-ease-out) infinite;
}
@keyframes hj-pulse {
  0%   { scale: 1;   opacity: .45; }
  70%  { scale: 2.4; opacity: 0; }
  100% { scale: 2.4; opacity: 0; }
}

/* Progress bar — indeterminate, transform-driven. */
.hj-progress { height: 4px; background: var(--hj-surface-sunken); border-radius: var(--hj-radius-pill); overflow: hidden; }
.hj-progress::after {
  content: ""; display: block; height: 100%; width: 40%;
  background: var(--hj-accent-strong); border-radius: inherit;
  translate: -100% 0; animation: hj-indeterminate 1200ms var(--hj-ease-in-out) infinite;
}
@keyframes hj-indeterminate { to { translate: 350% 0; } }


/* ==========================================================================
   5. PAGE + STEP TRANSITIONS
   ========================================================================== */

/* Page enter — runs once on load. Put on <main>. */
.hj-page-enter { animation: hj-page-in var(--hj-dur-slowest) var(--hj-ease) both; }
@keyframes hj-page-in { from { opacity: 0; translate: 0 8px; } to { opacity: 1; translate: 0 0; } }

/* Multi-step flows (booking, signup). Only ONE step is visible; the
   outgoing step should be removed with [hidden] after the transition. */
.hj-step { transition: opacity var(--hj-dur-slow) var(--hj-ease), translate var(--hj-dur-slow) var(--hj-ease); }
.hj-step[hidden] { display: none; }
.hj-step--in-next  { animation: hj-step-in-next  var(--hj-dur-slow) var(--hj-ease) both; }
.hj-step--in-prev  { animation: hj-step-in-prev  var(--hj-dur-slow) var(--hj-ease) both; }
.hj-step--out-next { animation: hj-step-out-next var(--hj-dur) var(--hj-ease-in-out) both; }
.hj-step--out-prev { animation: hj-step-out-prev var(--hj-dur) var(--hj-ease-in-out) both; }
@keyframes hj-step-in-next  { from { opacity: 0; translate:  24px 0; } to { opacity: 1; translate: 0 0; } }
@keyframes hj-step-in-prev  { from { opacity: 0; translate: -24px 0; } to { opacity: 1; translate: 0 0; } }
@keyframes hj-step-out-next { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate: -24px 0; } }
@keyframes hj-step-out-prev { from { opacity: 1; translate: 0 0; } to { opacity: 0; translate:  24px 0; } }

/* Progress rail for a stepper — scaleX, never width. */
.hj-step-rail { position: relative; height: 4px; background: var(--hj-border); border-radius: var(--hj-radius-pill); overflow: hidden; }
.hj-step-rail::after {
  content: ""; position: absolute; inset: 0; background: var(--hj-accent-strong);
  transform-origin: left center; scale: var(--hj-step-progress, 0) 1;
  transition: scale var(--hj-dur-slowest) var(--hj-ease);
}

/* Overlays */
.hj-overlay { animation: hj-fade-in var(--hj-dur) linear both; }
.hj-modal   { animation: hj-modal-in var(--hj-dur-slowest) var(--hj-ease-out) both; }
.hj-sheet   { animation: hj-sheet-in var(--hj-dur-slowest) var(--hj-ease-out) both; }
.hj-pop     { animation: hj-pop-in   var(--hj-dur) var(--hj-ease-emphasis) both; }
.hj-toast   { animation: hj-toast-in var(--hj-dur-slow) var(--hj-ease-out) both; }
@keyframes hj-fade-in  { from { opacity: 0; } to { opacity: 1; } }
@keyframes hj-modal-in { from { opacity: 0; translate: 0 12px; scale: .97; } to { opacity: 1; translate: 0 0; scale: 1; } }
@keyframes hj-sheet-in { from { translate: 0 100%; } to { translate: 0 0; } }
@keyframes hj-pop-in   { from { opacity: 0; translate: 0 -6px; scale: .96; } to { opacity: 1; translate: 0 0; scale: 1; } }
@keyframes hj-toast-in { from { opacity: 0; translate: 0 16px; } to { opacity: 1; translate: 0 0; } }

/* Accordion / disclosure chevron */
.hj-chevron { transition: rotate var(--hj-dur) var(--hj-ease-emphasis); }
details[open] > summary .hj-chevron, .is-open .hj-chevron { rotate: 180deg; }

/* Value swap — for a price that recalculates. Draws the eye without a jump. */
.hj-swap { animation: hj-swap var(--hj-dur-slow) var(--hj-ease) both; }
@keyframes hj-swap { from { opacity: 0; translate: 0 -5px; } to { opacity: 1; translate: 0 0; } }

/* Scroll progress bar — native scroll timeline, zero JS, zero jank. */
.hj-scroll-progress {
  position: fixed; inset: 0 0 auto 0; height: 3px; z-index: var(--hj-z-toast);
  background: var(--hj-accent-strong); transform-origin: left center; scale: 0 1;
}
@supports (animation-timeline: scroll()) {
  .hj-scroll-progress { animation: hj-scrub linear both; animation-timeline: scroll(root block); }
  @keyframes hj-scrub { to { scale: 1 1; } }
}


/* ==========================================================================
   6. REDUCED MOTION — the kill switch
   Anything hidden pending an animation becomes VISIBLE, not just static.
   ========================================================================== */
@media (prefers-reduced-motion: reduce) {

  /* Reveals: show everything immediately, no transition, no offset. */
  html.hj-js .hj-reveal,
  html.hj-js .hj-reveal:not(.is-in) {
    opacity: 1 !important;
    translate: none !important;
    scale: none !important;
    transition: none !important;
    transition-delay: 0ms !important;
  }

  /* No movement anywhere on hover/press. */
  .hj-hover-lift:hover,
  .hj-press:active, .hj-press-sm:active,
  .hj-nudge:hover > svg, .hj-nudge:hover > .hj-arrow,
  .hj-zoom-frame:hover > img, .hj-zoom-frame:hover > .hj-zoom-target {
    translate: none !important;
    scale: none !important;
  }
  /* Underline + stepper rail still need to READ as on/off — keep the end
     state, drop the animation. */
  .hj-underline::after { transition: none !important; }
  .hj-step-rail::after { transition: none !important; }

  /* Loops off. Skeletons stay as flat blocks; spinners stay as static rings
     (still visible, so "loading" is still communicated). */
  .hj-skeleton::after,
  .hj-pulse::before,
  .hj-progress::after { animation: none !important; }
  .hj-skeleton::after { opacity: 0; }
  .hj-progress::after { translate: 0 0; width: 100%; opacity: .5; }
  .hj-spinner, .hj-btn-loading::after { animation: none !important; opacity: .7; }

  /* Entrances resolve instantly at their END state. */
  .hj-page-enter, .hj-modal, .hj-sheet, .hj-pop, .hj-toast, .hj-overlay,
  .hj-swap, .hj-step, .hj-step--in-next, .hj-step--in-prev,
  .hj-step--out-next, .hj-step--out-prev {
    animation: none !important;
    opacity: 1 !important;
    translate: none !important;
    scale: none !important;
    transition: none !important;
  }

  .hj-chevron { transition: none !important; }
  .hj-scroll-progress { display: none; }

  /* Catch-all for the hj namespace, in case a page composes something new. */
  [class*="hj-"], [class*="hj-"]::before, [class*="hj-"]::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
    scroll-behavior: auto !important;
  }
}
