/* ═══════════════════════════════════════════════════════════════════════════
   LUSKI — honest pending states  (Phase 7)
   ───────────────────────────────────────────────────────────────────────────
   THE HONESTY CONTRACT
   Nothing here renders a percentage, a progress bar that fills, a skeleton, or
   a timed animation pretending to track work. The site does not know how long
   a fetch will take, so it says "working" and nothing more: one indeterminate
   hairline that sweeps out and back until the real promise settles. Every
   pending state begins on a real request and ends on that request's own
   resolution -- never on a timer.

   No artificial delay exists to make the indicator visible. The 120ms
   animation-delays below are the opposite: a FLASH GUARD, so a response that
   comes back in under 120ms paints nothing at all. The fetch still fires on
   the click tick.

   ── PROPERTY OWNERSHIP ─────────────────────────────────────────────────────
   arcade/interact.css owns transform, box-shadow, outline, opacity and cursor
   for buttons (read its header). Two narrow, deliberate overrides live here,
   both scoped to `.lp-btn.is-pending[disabled]` at (0,3,0) so they beat the
   (0,2,0) `.submit-btn:disabled` / `.tool-btn:disabled` disabled rule and
   nothing else:
     - opacity back to 1, because a button faded to .5 while it is the only
       thing on screen doing work reads as broken rather than busy;
     - cursor default rather than not-allowed, because the control is not
       refusing you, it is already doing what you asked.
   The button IS genuinely `disabled` throughout -- that is a correctness
   requirement (one click must not become two leads), not a look.

   No `transition:` SHORTHAND appears in this file, and this file declares no
   transition on images at all: a bare shorthand on `.cc-stage img` would reset
   transition-property and silently delete the `transform .5s` hover zoom that
   generate_cars.py sets there.
   ═══════════════════════════════════════════════════════════════════════════ */

/* The duplicate percentages are ordering-sensitive; the origin flip at 44.1%
   has to land between the two scale keyframes or the bar retracts from the
   wrong edge. Do not "tidy" these into one block per percentage. */
@keyframes lp-sweep {
  0%, 44%, 88.1%, 100% { transform-origin: left center; }
  0%, 100%, 88%        { transform: scaleX(0); }
  44.1%, 88%           { transform-origin: right center; }
  33%, 44%             { transform: scaleX(1); }
}

@keyframes lp-fade-in { to { opacity: 1; } }
@keyframes lp-line-in { from { opacity: 0; transform: translateY(3px); } to { opacity: 1; transform: none; } }
@keyframes lp-caret   { 50% { opacity: 0; } }

.lp-btn {
  position: relative;
}

/* Both pseudo-elements share this geometry. The explicit scaleX(0) base is
   mandatory: without it the runner paints at FULL WIDTH for the 80ms between
   its fade-in at 120ms and the sweep starting at 200ms, which reads as a
   completed progress bar. Centred with margin-left, not translate, so
   `transform` stays free for scaleX. */
.lp-btn.is-pending::before,
.lp-btn.is-pending::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: min(56px, 34%);
  height: 2px;
  margin-top: -1px;
  margin-left: max(-28px, -17%);
  border-radius: 1px;
  opacity: 0;
  transform: scaleX(0);
  transform-origin: left center;
  z-index: 1;
  pointer-events: none;
}

/* The static rail. */
.lp-btn.is-pending::before {
  background: rgba(255, 255, 255, .22);
  background: color-mix(in srgb, currentColor 22%, transparent);
  animation: lp-fade-in 80ms linear 120ms forwards;
}

/* The runner. currentColor because the button set is mixed -- white on ruby
   for .submit-btn/.notify-btn, --accent-ink for .tool-btn, black on white for
   checkout's .pay-btn -- and all three resolve to a visible hairline.
   1200ms `ease` (cubic-bezier(.25,.1,.25,1)), NOT var(--arc-ease)
   (cubic-bezier(.16,1,.3,1)): that curve is a decelerating entrance, and on a
   bar that has to travel back out again the retract reads as broken. */
.lp-btn.is-pending::after {
  background: currentColor;
  animation: lp-fade-in 80ms linear 120ms forwards,
             lp-sweep 1200ms ease 200ms infinite;
}

/* The label fades out but its box stays, so the button never resizes. Longhand
   only -- see the header. */
.lp-btn [data-lp-label] {
  transition-property: opacity;
  transition-duration: 120ms;
  transition-timing-function: linear;
}

.lp-btn.is-pending [data-lp-label] {
  opacity: 0;
  transition-delay: 120ms;
}

/* The two deliberate overrides of interact.css's disabled treatment. See the
   PROPERTY OWNERSHIP note in the header. (0,3,0) beats (0,2,0). */
.lp-btn.is-pending[disabled],
.lp-btn.is-pending[aria-disabled='true'] {
  opacity: 1;
  cursor: default;
}

/* Status line. Idle is display:none, matching the .tq-ticker it replaces, so
   no form gains a permanent empty row. */
.lp-line {
  display: none;
  margin-top: 11px;
  font-family: var(--arc-mono);
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .07em;
  text-transform: uppercase;
  text-align: center;
  color: var(--arc-accent);
}

.lp-line.is-on {
  display: block;
}

/* Matches dlg-text-in in arcade/dialogue-box.css exactly (300ms,
   var(--arc-ease), translateY(3px)) so a pending line and a Torque dialogue
   line read as the same voice. Replayed by replacing the node, not by
   rewriting textContent. */
.lp-line__text {
  display: inline-block;
  animation: lp-line-in 300ms var(--arc-ease);
}

.lp-line::after {
  content: '_';
  margin-left: 2px;
  animation: lp-caret 1000ms steps(1) infinite;
}

/* Image fade-in. Opacity is reachable ONLY through a JS-added class, so with
   JavaScript off or broken every photograph renders normally. The easing is
   appended to the owning rule in each page's own CSS, never declared here. */

@media (prefers-reduced-motion: reduce) {
  .lp-btn.is-pending::before,
  .lp-btn.is-pending::after,
  .lp-line__text,
  .lp-line::after {
    animation: none;
  }

  /* Held full and dimmed rather than hidden: the work is still happening and
     the control still has to say so. */
  .lp-btn.is-pending::before { opacity: 1; }
  .lp-btn.is-pending::after {
    opacity: .55;
    transform: scaleX(1);
    transform-origin: left center;
  }

  .lp-btn [data-lp-label] { transition-duration: 1ms; }
}
