/*
Progress

A determinate or indeterminate bar. The indicator is a full-width bar
moved with transform, so a new value animates without layout, and the
track never changes size whatever the value or state.

  .progress                       column: header, track
  .progress-header                label at the inline start, value at the end
  .progress-track[role=progressbar]
  .progress-indicator             --progress-color fills it
  .progress.is-sm | is-lg         track height
  [data-state="indeterminate"]    a sliding segment
*/

@layer components {

.progress {
  display:        flex;
  flex-direction: column;
  gap:            var(--space-2);
  width:          100%;
  min-width:      0;
  font-family:    var(--font-ui);
  font-size:      var(--text-ui);
}

.progress.is-sm { --progress-height: var(--progress-height-sm); }
.progress.is-lg { --progress-height: var(--progress-height-lg); }

/* The label starts from zero width and takes what the value leaves, so it
   wraps beside the value as before. A value too wide to sit beside any
   label (a long format in a narrow card) wraps onto its own line instead
   of running out of the progress; its width is its widest text, so that
   choice never changes as the value moves. */
.progress-header {
  display:         flex;
  flex-wrap:       wrap;
  align-items:     baseline;
  justify-content: space-between;
  gap:             var(--space-3);
  min-height:      1lh;   /* an empty value (indeterminate) keeps the line */
}

.progress-label {
  flex:          1 1 0;
  min-width:     0;
  font-weight:   var(--weight-medium);
  color:         var(--ink);
  overflow-wrap: anywhere;
}

.progress-value {
  flex:                 0 1 auto;
  max-width:            100%;
  display:              flex;
  flex-direction:       column;
  align-items:          flex-end;
  margin-inline-start:  auto;
  text-align:           end;
  color:                var(--ink-soft);
  font-variant-numeric: tabular-nums;
}

/* The value text changes width as it moves (5% to 100%). The text at the
   ends of the range sits in two zero-height lines under it, so the value
   is always as wide as its widest text and the label beside it never
   rewraps mid-task. */
.progress-value::before,
.progress-value::after {
  order:       1;   /* after the text, so the value's baseline is the text's */
  height:      0;
  overflow:    hidden;
  visibility:  hidden;
  white-space: nowrap;
  max-width:   100%;   /* sizes the value, never overflows a wrapped one */
}

.progress-value::before { content: attr(data-reserve-min); }
.progress-value::after  { content: attr(data-reserve-max); }

.progress-track {
  --progress-dir: 1;   /* -1 under rtl: the fill starts at the inline start */

  position:      relative;
  overflow:      hidden;
  height:        var(--progress-height);
  border-radius: var(--radius-pill);
  background:    var(--bg-muted);
}

.progress-indicator {
  position:         absolute;
  inset:            0;
  border-radius:    inherit;
  background:       var(--progress-color, var(--accent));
  transform:        translateX(calc((var(--progress-percent, 0) * 1% - 100%) * var(--progress-dir)));
  transition:       transform var(--duration-enter) var(--ease-enter);
}

.progress[data-state="indeterminate"] .progress-indicator {
  inset-inline-end: auto;
  width:      40%;
  transform:  translateX(calc(-100% * var(--progress-dir)));
  transition: none;
  animation:  progress-slide calc(var(--duration-loop) * 3) var(--ease-in-out) infinite;
}

@media (prefers-reduced-motion: reduce) {
  .progress-indicator {
    transition: none;
  }

  .progress[data-state="indeterminate"] .progress-indicator {
    width:     100%;
    transform: none;
    animation: progress-pulse calc(var(--duration-loop) * 4) var(--ease-in-out) infinite;
  }
}

.progress-track:dir(rtl) {
  --progress-dir: -1;
}

/* Forced colors (Windows High Contrast) paints every background as the
   page color, so the track and the fill would both vanish and the value
   with them. There the track keeps an outline (no layout space) and the
   fill opts out of the color override to paint in the system highlight
   color, as a native <progress> does. */
@media (forced-colors: active) {
  .progress-track {
    outline:        var(--surface-border-width) solid;
    outline-offset: calc(var(--surface-border-width) * -1);
  }

  .progress-indicator {
    forced-color-adjust: none;
    background:          Highlight;
  }
}

@keyframes progress-slide {
  from { transform: translateX(calc(-100% * var(--progress-dir))); }
  to   { transform: translateX(calc(250% * var(--progress-dir))); }
}

@keyframes progress-pulse {
  50% { opacity: 0.4; }
}

}
