/* ============================================================================
   crm-motion.css — the Geex motion layer
   ----------------------------------------------------------------------------
   Loaded LAST in every shell (guest, app, admin) and ported into the Chrome
   extension. It adds movement only; it never sets a colour, a font or a size,
   so it can never fight the design tokens.

   Three jobs, in the owner's words: "more animation on login and on page to
   page movement".
     1. Each page fades in, so a navigation reads as a continuation, not a blink.
     2. Cards lift in, and form rows arrive one after another.
     3. Controls answer the pointer — focus, hover, press.

   Rules this file obeys (GEEX-UI-DESIGN-GUIDE §2.3, §13):
     - No raw hex. Nothing here carries colour at all.
     - No font-family.
     - Micro-interactions are timed off the existing --crm-tx token. Entrances
       need longer than .14s, so they use the --crm-anim-* tokens added below.
     - Every keyframe declares only `from`. The implicit `to` is the element's
       OWN computed style, and the fill-mode is `backwards`, never `forwards`.
       That matters: an animation that ENDS on `transform: translateY(0)` leaves
       a permanent transform, and a permanent transform makes the element a
       containing block for `position: fixed` descendants — which silently
       breaks every modal and popover declared inside a card. Ending on the
       element's own style leaves nothing behind.
     - The whole file is switched off under prefers-reduced-motion, and in print.
   ========================================================================== */


/* ── 1. MOTION TOKENS ────────────────────────────────────────────────────── */
/* --crm-tx (.14s ease) already exists in crm-ui.css §1 and stays the unit for
   anything the pointer triggers. These add the entrance timings only.        */
:root {
  --crm-anim-ease:     cubic-bezier(.22, .61, .36, 1);   /* settle, no bounce   */
  --crm-anim-ease-out: cubic-bezier(.16, 1, .3, 1);      /* softer, longer tail */

  --crm-anim-page:  .30s;   /* whole-page fade — short, or navigation drags  */
  --crm-anim-enter: .44s;   /* a card or a row arriving                      */
  --crm-anim-step:  55ms;   /* gap between staggered siblings                */

  --crm-anim-rise:  10px;   /* how far an entering element travels           */
  --crm-anim-lift:  1px;    /* how far a control lifts under the pointer     */
  --crm-anim-press: .97;    /* scale on :active                              */
}


/* ── 2. KEYFRAMES ───────────────────────────────────────────────────────── */
/* `from` only — see the header note on why there is no `to`. */
@keyframes crm-m-fade  { from { opacity: 0; } }
@keyframes crm-m-rise  { from { opacity: 0; transform: translateY(var(--crm-anim-rise)); } }
@keyframes crm-m-drop  { from { opacity: 0; transform: translateY(calc(var(--crm-anim-rise) * -1)); } }
@keyframes crm-m-slide { from { opacity: 0; transform: translateX(calc(var(--crm-anim-rise) * -1)); } }
@keyframes crm-m-pop   { from { opacity: 0; transform: scale(.96); } }


/* ── 3. PAGE ENTRANCE ───────────────────────────────────────────────────── */
/* The layouts tag their main content region with .crm-page. Deliberately NOT
   on <body>: an opacity below 1 on <body> makes it a stacking context, and for
   the length of the animation every position:fixed child (the mobile bottom
   nav, an auto-opened modal) would anchor to <body> instead of the viewport.
   Animating the content region looks the same on screen and does none of that. */
.crm-page {
  animation: crm-m-fade var(--crm-anim-page) var(--crm-anim-ease) backwards;
}

/* The chrome arrives with the page rather than after it, so the shell does not
   appear to assemble itself in pieces. */
.crm-chrome-top  { animation: crm-m-drop  var(--crm-anim-page) var(--crm-anim-ease) backwards; }
.crm-chrome-side { animation: crm-m-slide var(--crm-anim-page) var(--crm-anim-ease) backwards; }


/* ── 4. CARDS — fade + lift, staggered ──────────────────────────────────── */
/* Covers the Geex card and the three legacy card shapes still in the portal
   (.card, .plan-card, .stat), so a page gets the entrance without being
   rewritten first. */
.crm-page .crm-card,
.crm-page > .card,
.crm-page .crm-stat,
.crm-page .stat,
.crm-page .plan-card,
.crm-page .crm-empty,
.crm-page > .row > [class^="col"] > .card,
.crm-page > .row > [class*=" col"] > .card {
  animation: crm-m-rise var(--crm-anim-enter) var(--crm-anim-ease-out) backwards;
}

/* Stagger the first handful of siblings, then stop. An uncapped cascade on an
   admin list with twenty cards would still be arriving a second later. */
.crm-page > .row > [class^="col"]:nth-child(1) > .card,
.crm-page > .row > [class*=" col"]:nth-child(1) > .card,
.crm-page > .card:nth-of-type(1),
.crm-page .crm-card:nth-of-type(1)   { animation-delay: 40ms; }

.crm-page > .row > [class^="col"]:nth-child(2) > .card,
.crm-page > .row > [class*=" col"]:nth-child(2) > .card,
.crm-page > .card:nth-of-type(2),
.crm-page .crm-card:nth-of-type(2)   { animation-delay: calc(40ms + var(--crm-anim-step)); }

.crm-page > .row > [class^="col"]:nth-child(3) > .card,
.crm-page > .row > [class*=" col"]:nth-child(3) > .card,
.crm-page > .card:nth-of-type(3),
.crm-page .crm-card:nth-of-type(3)   { animation-delay: calc(40ms + var(--crm-anim-step) * 2); }

.crm-page > .row > [class^="col"]:nth-child(4) > .card,
.crm-page > .row > [class*=" col"]:nth-child(4) > .card,
.crm-page > .card:nth-of-type(4),
.crm-page .crm-card:nth-of-type(4)   { animation-delay: calc(40ms + var(--crm-anim-step) * 3); }

/* KPI tiles are small and sit in a row — a tighter step reads better there. */
.crm-page .crm-stat:nth-child(1), .crm-page .stat:nth-child(1) { animation-delay: 30ms; }
.crm-page .crm-stat:nth-child(2), .crm-page .stat:nth-child(2) { animation-delay: 70ms; }
.crm-page .crm-stat:nth-child(3), .crm-page .stat:nth-child(3) { animation-delay: 110ms; }
.crm-page .crm-stat:nth-child(4), .crm-page .stat:nth-child(4) { animation-delay: 150ms; }


/* ── 5. STAGGERED ROWS ──────────────────────────────────────────────────── */
/* Opt-in anywhere: <div class="crm-stagger"> … </div> and its direct children
   arrive in order. Ten steps is the cap; an eleventh child simply arrives with
   the tenth rather than trailing off. */
.crm-stagger > * {
  animation: crm-m-rise var(--crm-anim-enter) var(--crm-anim-ease-out) backwards;
  animation-delay: calc(var(--crm-anim-step) * 10);
}
.crm-stagger > :nth-child(1)  { animation-delay: 0ms; }
.crm-stagger > :nth-child(2)  { animation-delay: calc(var(--crm-anim-step) * 1); }
.crm-stagger > :nth-child(3)  { animation-delay: calc(var(--crm-anim-step) * 2); }
.crm-stagger > :nth-child(4)  { animation-delay: calc(var(--crm-anim-step) * 3); }
.crm-stagger > :nth-child(5)  { animation-delay: calc(var(--crm-anim-step) * 4); }
.crm-stagger > :nth-child(6)  { animation-delay: calc(var(--crm-anim-step) * 5); }
.crm-stagger > :nth-child(7)  { animation-delay: calc(var(--crm-anim-step) * 6); }
.crm-stagger > :nth-child(8)  { animation-delay: calc(var(--crm-anim-step) * 7); }
.crm-stagger > :nth-child(9)  { animation-delay: calc(var(--crm-anim-step) * 8); }
.crm-stagger > :nth-child(10) { animation-delay: calc(var(--crm-anim-step) * 9); }


/* ── 6. THE AUTH CARD ───────────────────────────────────────────────────── */
/* This is the screen the owner singled out, so it gets the most care: the card
   pops in, then its contents arrive top to bottom, then the form rows inside
   the form arrive on the same rhythm. */
.login-form-panel {
  animation: crm-m-pop var(--crm-anim-enter) var(--crm-anim-ease-out) backwards;
}

.login-form-inner > * {
  animation: crm-m-rise var(--crm-anim-enter) var(--crm-anim-ease-out) backwards;
  animation-delay: calc(var(--crm-anim-step) * 4);
}
.login-form-inner > :nth-child(1) { animation-delay: 80ms; }
.login-form-inner > :nth-child(2) { animation-delay: calc(80ms + var(--crm-anim-step) * 1); }
.login-form-inner > :nth-child(3) { animation-delay: calc(80ms + var(--crm-anim-step) * 2); }
.login-form-inner > :nth-child(4) { animation-delay: calc(80ms + var(--crm-anim-step) * 3); }
.login-form-inner > :nth-child(5) { animation-delay: calc(80ms + var(--crm-anim-step) * 4); }

/* The rows of the form itself. The form is already the last staggered child
   above, so these start after it and the cascade stays continuous. */
.login-form-panel form > * {
  animation: crm-m-rise var(--crm-anim-enter) var(--crm-anim-ease-out) backwards;
  animation-delay: calc(var(--crm-anim-step) * 5);
}
.login-form-panel form > :nth-child(1) { animation-delay: 180ms; }
.login-form-panel form > :nth-child(2) { animation-delay: calc(180ms + var(--crm-anim-step) * 1); }
.login-form-panel form > :nth-child(3) { animation-delay: calc(180ms + var(--crm-anim-step) * 2); }
.login-form-panel form > :nth-child(4) { animation-delay: calc(180ms + var(--crm-anim-step) * 3); }
.login-form-panel form > :nth-child(5) { animation-delay: calc(180ms + var(--crm-anim-step) * 4); }

/* The honeypot and the CSRF / reCAPTCHA hidden inputs are form children too.
   Animating a hidden input is harmless, but animating .hp would give it a
   transform, and .hp is parked off-screen by `left:-9999px` — leave both be. */
.login-form-panel form > input[type="hidden"],
.login-form-panel form > .hp { animation: none; }

.login-footer {
  animation: crm-m-fade var(--crm-anim-enter) var(--crm-anim-ease) backwards;
  animation-delay: 320ms;
}


/* ── 7. MICRO-INTERACTIONS ──────────────────────────────────────────────── */
/* Everything below is pointer- or focus-driven, so all of it is timed off
   --crm-tx. The fallback value keeps the file standalone for the extension,
   which does not load crm-ui.css. */

/* Buttons: lift under the pointer, press in on click. :not(:disabled) so a
   dead button stays visibly dead. */
.btn,
.crm-icon-btn,
.crm-btn-icon,
.btn-brand,
.btn-outline-brand {
  transition: transform var(--crm-tx, .14s ease),
              box-shadow var(--crm-tx, .14s ease),
              background-color var(--crm-tx, .14s ease),
              border-color var(--crm-tx, .14s ease),
              color var(--crm-tx, .14s ease),
              filter var(--crm-tx, .14s ease);
}
.btn:hover:not(:disabled):not(.disabled),
.crm-icon-btn:hover:not(:disabled),
.crm-btn-icon:hover:not(:disabled),
.btn-brand:hover:not(:disabled),
.btn-outline-brand:hover:not(:disabled) {
  transform: translateY(calc(var(--crm-anim-lift) * -1));
}
.btn:active:not(:disabled):not(.disabled),
.crm-icon-btn:active:not(:disabled),
.crm-btn-icon:active:not(:disabled),
.btn-brand:active:not(:disabled),
.btn-outline-brand:active:not(:disabled),
.btn-login:active,
.btn-google:active {
  transform: scale(var(--crm-anim-press));
}

/* A text link styled as a button should not slide — there is no surface to
   lift, so the move reads as a rendering glitch. */
.btn-link:hover:not(:disabled) { transform: none; }

/* Fields: the focus ring grows in rather than snapping on. */
.form-control,
.form-select,
textarea.form-control,
.form-check-input {
  transition: border-color var(--crm-tx, .14s ease),
              box-shadow var(--crm-tx, .14s ease),
              background-color var(--crm-tx, .14s ease);
}

/* Links and nav items. */
a,
.nav-link {
  transition: color var(--crm-tx, .14s ease),
              background-color var(--crm-tx, .14s ease),
              opacity var(--crm-tx, .14s ease);
}
/* The sidebar item nudges toward its label on hover — the one place a
   horizontal move reads as navigation rather than fidget. */
.crm-chrome-side a.nav {
  transition: color var(--crm-tx, .14s ease),
              background-color var(--crm-tx, .14s ease),
              transform var(--crm-tx, .14s ease);
}
.crm-chrome-side a.nav:hover { transform: translateX(2px); }

/* Table rows. Background only — moving a row shifts its neighbours' baselines. */
.table tbody tr,
.crm-table tbody tr {
  transition: background-color var(--crm-tx, .14s ease);
}

/* The "?" help badge. It already transitions its background in crm-ui.css §2b;
   this adds a ring so the target feels live before the tip appears.
   Deliberately a ring and not `transform: scale()` — the tooltip bubble is the
   badge's own ::after, so scaling the badge scales the tip's text with it.
   The ring colour comes from the token with no literal fallback: if
   --crm-primary-rgb is absent the declaration is simply dropped, which is the
   right failure mode and keeps this file free of colour values. */
.crm-help-tip {
  transition: background var(--crm-tx, .14s ease),
              box-shadow var(--crm-tx, .14s ease);
}
.crm-help-tip:hover,
.crm-help-tip:focus-visible { box-shadow: 0 0 0 3px rgba(var(--crm-primary-rgb), .18); }

/* Cards respond to the pointer only where they are a link or a chosen plan —
   a hover lift on a static card is noise. */
a > .crm-card,
a > .card,
.plan-card.featured {
  transition: transform var(--crm-tx, .14s ease),
              box-shadow var(--crm-tx, .14s ease);
}
a:hover > .crm-card,
a:hover > .card {
  transform: translateY(calc(var(--crm-anim-lift) * -2));
}

/* Flash messages slide in — they appear after an action, so they should read
   as a response rather than as something that was always there. */
.alert { animation: crm-m-drop var(--crm-anim-enter) var(--crm-anim-ease-out) backwards; }


/* ── 8. UTILITIES ───────────────────────────────────────────────────────── */
/* For one-off elements that want the entrance without a structural class. */
.crm-anim-fade { animation: crm-m-fade var(--crm-anim-enter) var(--crm-anim-ease) backwards; }
.crm-anim-rise { animation: crm-m-rise var(--crm-anim-enter) var(--crm-anim-ease-out) backwards; }
.crm-anim-pop  { animation: crm-m-pop  var(--crm-anim-enter) var(--crm-anim-ease-out) backwards; }

.crm-anim-d1 { animation-delay: calc(var(--crm-anim-step) * 1); }
.crm-anim-d2 { animation-delay: calc(var(--crm-anim-step) * 2); }
.crm-anim-d3 { animation-delay: calc(var(--crm-anim-step) * 3); }
.crm-anim-d4 { animation-delay: calc(var(--crm-anim-step) * 4); }
.crm-anim-d5 { animation-delay: calc(var(--crm-anim-step) * 5); }


/* ── 9. OFF SWITCHES ────────────────────────────────────────────────────── */
/* prefers-reduced-motion is not a request for *less* motion, it is a request
   for none — vestibular disorders are triggered by the movement itself. So this
   removes the animations outright rather than shortening them, and zeroes the
   transforms so nothing is left mid-travel. Everything stays fully visible:
   every keyframe above starts from opacity 0, so killing the animation without
   forcing opacity back to 1 would be the one way to hide content for good. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-delay: 0s !important;
    animation-duration: .001s !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001s !important;
    transition-delay: 0s !important;
    scroll-behavior: auto !important;
  }
  .crm-page,
  .crm-chrome-top,
  .crm-chrome-side,
  .crm-page .crm-card,
  .crm-page > .card,
  .crm-page .crm-stat,
  .crm-page .stat,
  .crm-page .plan-card,
  .crm-page .crm-empty,
  .crm-page > .row > [class^="col"] > .card,
  .crm-page > .row > [class*=" col"] > .card,
  .crm-stagger > *,
  .login-form-panel,
  .login-form-inner > *,
  .login-form-panel form > *,
  .login-footer,
  .alert,
  .crm-anim-fade,
  .crm-anim-rise,
  .crm-anim-pop {
    animation: none !important;
    opacity: 1 !important;
    transform: none !important;
  }
  /* .hp is the honeypot — it must stay invisible even here. */
  .login-form-panel form > .hp { opacity: 0 !important; }

  .btn:hover, .btn:active,
  .crm-icon-btn:hover, .crm-icon-btn:active,
  .crm-btn-icon:hover, .crm-btn-icon:active,
  .btn-brand:hover, .btn-brand:active,
  .btn-outline-brand:hover, .btn-outline-brand:active,
  .btn-login:hover, .btn-login:active,
  .btn-google:active,
  .crm-chrome-side a.nav:hover,
  a:hover > .crm-card,
  a:hover > .card {
    transform: none !important;
  }
}

/* A printed page is a still, so anything caught mid-animation prints that way.
   Killing animation and transition outright is enough — the fill-mode is
   `backwards`, so the moment an animation is gone the element sits at its own
   style. opacity is deliberately NOT forced here: a blanket `opacity:1` on `*`
   would also un-hide the `.hp` honeypot and the password-toggle affordances. */
@media print {
  *,
  *::before,
  *::after {
    animation: none !important;
    transition: none !important;
  }
}
