/* ===========================================================================
   Motion
   ---------------------------------------------------------------------------
   Two rules, both from Apple's guidance and both easy to break by accident:

   1. Motion that answers an action is welcome, because it shows what changed.
      Motion nobody asked for is a distraction. So there is exactly ONE
      continuous animation in the whole product — the intake pulse, which is
      load-bearing: it stops when the pipeline stops, and that is the fastest
      way to see that the pipeline stopped.

   2. A view entrance is ONE orchestrated moment, not a fade-and-slide on
      every card. Rows stagger in off a single shared delay ramp that caps
      quickly, so a 50-row table does not take 5 seconds to arrive.
   ========================================================================= */

@keyframes pulse-out {
  0%   { transform: scale(1);   opacity: 0.8; }
  70%  { transform: scale(2.6); opacity: 0; }
  100% { transform: scale(2.6); opacity: 0; }
}

@keyframes shimmer {
  from { background-position: 200% 0; }
  to   { background-position: -200% 0; }
}

@keyframes rise {
  from { opacity: 0; transform: translateY(8px); }
  to   { opacity: 1; transform: none; }
}

@keyframes rise-sm {
  from { opacity: 0; transform: translateY(4px); }
  to   { opacity: 1; transform: none; }
}

@keyframes fade { from { opacity: 0; } to { opacity: 1; } }

@keyframes sheet-in {
  from { opacity: 0; transform: translateY(12px) scale(0.985); }
  to   { opacity: 1; transform: none; }
}

@keyframes toast-in {
  from { opacity: 0; transform: translateY(10px) scale(0.97); }
  to   { opacity: 1; transform: none; }
}

/* Drawing a path on load: used once, for the login signal trace. */
@keyframes trace {
  from { stroke-dashoffset: var(--trace-len, 2000); }
  to   { stroke-dashoffset: 0; }
}

@keyframes sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(100%); }
}

/* ---- view entrance ----------------------------------------------------
   The view sets --i on its children; the ramp caps at 10 so long lists stay
   snappy. One moment, then the screen is still. */
.enter {
  animation: rise var(--d-slow) var(--e-standard) backwards;
  animation-delay: calc(min(var(--i, 0), 10) * 28ms);
}
.enter-sm {
  animation: rise-sm var(--d-base) var(--e-standard) backwards;
  animation-delay: calc(min(var(--i, 0), 10) * 20ms);
}
.enter-fade { animation: fade var(--d-base) var(--e-standard) backwards; }

.sheet { animation: sheet-in var(--d-base) var(--e-standard); }
.scrim { animation: fade var(--d-fast) var(--e-standard); }
.toast { animation: toast-in var(--d-base) var(--e-spring); }

/* Leaving should not bounce. */
.scrim[data-closing="1"] { animation: fade var(--d-fast) var(--e-exit) reverse forwards; }
.scrim[data-closing="1"] .sheet { animation: sheet-in var(--d-fast) var(--e-exit) reverse forwards; }

/* A value that just changed gets a brief lift, so you can see WHICH number
   moved in a screen full of numbers. */
@keyframes bump {
  0%   { color: inherit; }
  18%  { color: var(--signal); transform: translateY(-1px); }
  100% { color: inherit; transform: none; }
}
.bumped { animation: bump var(--d-slow) var(--e-standard); }

/* ===========================================================================
   Reduced motion
   ---------------------------------------------------------------------------
   Large movement is replaced with a plain fade — not switched off entirely,
   because the state changes still need to be legible. The one exception is
   the pulse ring, which is removed: a repeating scale is exactly what the
   setting exists to stop.
   ========================================================================= */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
    scroll-behavior: auto !important;
  }
  .enter, .enter-sm { animation: fade 160ms linear backwards !important; animation-delay: 0ms !important; }
  .sheet, .toast { animation: fade 160ms linear !important; }
  .pulse__ring { display: none; }
  .skeleton { animation: none !important; background: var(--shelf); }
  .btn:active { transform: none; }
  /* The pivot's arrow travel and lift are the affordance, not decoration, so
     they still happen — instantly, under the blanket duration override above.
     What is dropped is the hover translate, which is motion for its own sake. */
  .pivot__go:hover { transform: none; }
  .pivot__go:active { transform: none; }
  /* The live dot is a state, not decoration, so it stays — but it stops
     moving and keeps its lit value rather than its dim one. */
  .lane__pulse { animation: none !important; opacity: 1 !important; transform: none !important; }
}

/* ===========================================================================
   Sign in
   ---------------------------------------------------------------------------
   The one place a single orchestrated flourish is justified: the product
   coming online. A trace draws itself across the panel once — the shape of a
   real intake curve, not an abstract swoosh — then the form arrives. After
   that the screen is still.
   ========================================================================= */
.auth {
  min-height: 100vh;
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: var(--s5);
  background: var(--abyss);
}

.auth__card {
  width: min(420px, 100%);
  background: var(--slate);
  border: 1px solid var(--hair);
  border-radius: var(--r-sheet);
  box-shadow: var(--lift-3);
  overflow: hidden;
  animation: sheet-in var(--d-slow) var(--e-spring);
}

.auth__trace { display: block; width: 100%; height: 96px; border-bottom: 1px solid var(--hair); background: var(--shelf); }
.auth__trace path.line {
  fill: none;
  stroke: var(--signal);
  stroke-width: 1.75;
  stroke-linecap: round;
  stroke-dasharray: var(--trace-len, 2000);
  animation: trace 1.6s var(--e-standard) backwards;
}
.auth__trace path.area { fill: url(#authFill); opacity: 0; animation: fade var(--d-slow) var(--e-standard) 1s forwards; }

.auth__body { padding: var(--s6) var(--s5) var(--s5); }
.auth__title { font-size: var(--t-t2); font-weight: var(--w-semibold); letter-spacing: -0.014em; }
.auth__why { font-size: var(--t-foot); color: var(--mist); margin-top: var(--s2); }
.auth__form { margin-top: var(--s5); display: grid; gap: var(--s3); }
.auth__err {
  font-size: var(--t-foot);
  color: var(--alarm);
  background: var(--alarm-soft);
  border: 1px solid var(--alarm);
  border-radius: var(--r-control);
  padding: var(--s2) var(--s3);
  animation: rise-sm var(--d-fast) var(--e-standard);
}
.auth__foot { margin-top: var(--s5); padding-top: var(--s4); border-top: 1px solid var(--hair); font-size: var(--t-cap); color: var(--dim); }

.auth__children > * { animation: rise var(--d-slow) var(--e-standard) backwards; }
.auth__children > *:nth-child(1) { animation-delay: 0.5s; }
.auth__children > *:nth-child(2) { animation-delay: 0.58s; }
.auth__children > *:nth-child(3) { animation-delay: 0.66s; }
.auth__children > *:nth-child(4) { animation-delay: 0.74s; }

/* A button that is working says so in place, rather than swapping to a
   spinner somewhere else on the screen. */
.btn[data-busy="1"] { color: transparent; pointer-events: none; }
.btn[data-busy="1"]::before {
  content: "";
  position: absolute;
  inset: 0;
  overflow: hidden;
  border-radius: inherit;
  background: linear-gradient(90deg, transparent, rgba(255, 255, 255, 0.22), transparent);
  animation: sweep 1.1s linear infinite;
}
