/*
Slider

A track, a range and one thumb per value. Thumbs and the range are placed
from custom properties the template writes (fractions 0..1 from the start
edge), so a value change moves them by inset alone and the slider box never
changes size. Dragging, hover and focus change color and box-shadow only.

  .slider                        root (data-orientation, data-disabled, data-dragging)
  .slider-control                the pressable area around the track
  .slider-track                  the full range
  .slider-range                  the filled part (--slider-start, --slider-end)
  .slider-thumb                  one thumb (--slider-p, data-index)
  .slider-thumb-input            its range input, the focusable slider, unseen over it
  .slider-mark                   aria-hidden tick inside the track at a value (--slider-p)
  .slider-mark-labels            aria-hidden row of mark labels under the control
  .slider-mark-label             one label, centered on its mark (--slider-shift at the ends)
  .slider-value                  SliderValue's <output>
  .slider[data-thumb-alignment="edge"]   thumbs stay inside the track at min and max

Sizes derive from --icon-size until the contract names slider tokens
(notes/theme-requests.md): the thumb is one icon, the track 3/8 of it.
*/

@layer components {

.slider {
  --slider-inset: calc(var(--slider-thumb) / 2);

  position:     relative;
  display:      flex;
  align-items:  center;
  width:        100%;
  flex-wrap:    wrap;
  min-width:    0;
  touch-action: none;
  user-select:  none;
}

.slider[data-thumb-alignment="edge"] {
  --slider-inset: calc(var(--slider-thumb) / 2);
}

.slider[data-orientation="vertical"] {
  display:        inline-flex;
  flex-direction: column;
  width:          auto;
  height:         100%;
  min-height:     calc(var(--control-height-lg) * 2 + var(--control-height));
}

.slider-control {
  position:     relative;
  display:      flex;
  flex:         1 1 auto;
  align-items:  center;
  width:        100%;
  height:       var(--slider-thumb);
  touch-action: none;
  cursor:       pointer;
}

.slider[data-orientation="vertical"] .slider-control {
  flex-direction:  column;
  justify-content: center;
  width:           var(--slider-thumb);
  height:          100%;
}

.slider:is([data-disabled], :not(:has(.slider-thumb-input:enabled))) .slider-control {
  cursor: not-allowed;
}

.slider-track {
  position:      relative;
  flex:          1 1 auto;
  width:         100%;
  height:        var(--slider-thickness);
  overflow:      hidden;
  border-radius: var(--radius-pill);
  background:    var(--bg-muted);
}

.slider[data-orientation="vertical"] .slider-track {
  width:  var(--slider-thickness);
  height: 100%;
}

/* The point at fraction p along the track: centered thumbs reach the ends,
   edge-aligned thumbs stop half a thumb inside them. */
.slider-range {
  position:   absolute;
  background: var(--accent);
  inset-block: 0;
  inset-inline-start: calc(100% * var(--slider-start));
  inset-inline-end:   calc(100% * (1 - var(--slider-end)));
}

.slider[data-thumb-alignment="edge"] .slider-range {
  inset-inline-start: calc(var(--slider-inset) + (100% - var(--slider-thumb)) * var(--slider-start));
  inset-inline-end:   calc(var(--slider-inset) + (100% - var(--slider-thumb)) * (1 - var(--slider-end)));
}

.slider[data-orientation="vertical"] .slider-range {
  inset-inline: 0;
  inset-block-start: auto;
  bottom: calc(100% * var(--slider-start));
  top:    calc(100% * (1 - var(--slider-end)));
}

.slider[data-orientation="vertical"][data-thumb-alignment="edge"] .slider-range {
  bottom: calc(var(--slider-inset) + (100% - var(--slider-thumb)) * var(--slider-start));
  top:    calc(var(--slider-inset) + (100% - var(--slider-thumb)) * (1 - var(--slider-end)));
}

.slider-thumb {
  position:      absolute;
  top:           50%;
  inset-inline-start: calc(100% * var(--slider-p));
  display:       block;
  box-sizing:    border-box;
  width:         var(--slider-thumb);
  height:        var(--slider-thumb);
  border:        var(--control-border-width) solid var(--accent);
  border-radius: var(--radius-pill);
  background:    var(--bg);
  box-shadow:    var(--shadow-xs);
  transform:     translate(-50%, -50%);
  cursor:        grab;
  transition:    box-shadow var(--duration-fast) var(--ease-out);
}

.slider-thumb:dir(rtl),
.slider[dir="rtl"] .slider-thumb {
  transform: translate(50%, -50%);
}

.slider[data-thumb-alignment="edge"] .slider-thumb {
  inset-inline-start: calc(var(--slider-inset) + (100% - var(--slider-thumb)) * var(--slider-p));
}

.slider[data-orientation="vertical"] .slider-thumb {
  top:    auto;
  left:   50%;
  right:  auto;
  bottom: calc(100% * var(--slider-p));
  transform: translate(-50%, 50%);
}

.slider[data-orientation="vertical"][data-thumb-alignment="edge"] .slider-thumb {
  bottom: calc(var(--slider-inset) + (100% - var(--slider-thumb)) * var(--slider-p));
}

.slider[data-dragging] .slider-thumb,
.slider[data-dragging] .slider-control {
  cursor: grabbing;
}

.slider-thumb:hover:not([data-disabled]) {
  box-shadow: var(--shadow-xs), 0 0 0 calc(var(--focus-ring-width) * 2) color-mix(in srgb, var(--focus) 30%, transparent);
}

/* The thumb's range input is the focusable slider (Base UI): it covers the
   thumb, unseen, so a screen reader's focus box is the thumb's. Presses go
   to the control's drag, never to the native range. */
.slider-thumb-input {
  position:       absolute;
  top:            calc(var(--control-border-width) * -1);
  left:           calc(var(--control-border-width) * -1);
  box-sizing:     border-box;
  width:          calc(100% + var(--control-border-width) * 2);
  height:         calc(100% + var(--control-border-width) * 2);
  margin:         0;
  padding:        0;
  border:         0;
  opacity:        0;
  appearance:     none;
  background:     transparent;
  pointer-events: none;
}

.slider-thumb:has(> .slider-thumb-input:focus-visible) {
  outline:    none;
  box-shadow: var(--focus-ring);
}

.slider-thumb[data-active]:has(> .slider-thumb-input:focus-visible),
.slider[data-dragging] .slider-thumb[data-active] {
  z-index: 1;
}

/* Dimmed by the effective disabled state: its own prop, a disabled Field
   or a native <fieldset disabled>, which disable every thumb's input. One
   thumb in disabledThumbs leaves the others enabled and the slider bright. */
.slider:is([data-disabled], :not(:has(.slider-thumb-input:enabled))) {
  opacity: var(--opacity-disabled);
}

.slider-thumb[data-disabled] {
  cursor: not-allowed;
}

.slider:is([data-disabled], :not(:has(.slider-thumb-input:enabled))) .slider-thumb {
  box-shadow: none;
}

.slider[data-invalid] .slider-thumb {
  border-color: var(--danger);
}

.slider[data-invalid] .slider-range {
  background: var(--danger);
}

/* Marks: small dots inside the track, at the same point a thumb with that
   value would sit. They are clamped inside the track so the ones at min and
   max are whole, and draw over the range in its ink color. */
.slider-mark {
  --slider-mark: calc(var(--slider-thickness) * 0.5);
  --slider-at: calc(100% * var(--slider-p));

  position:      absolute;
  top:           50%;
  inset-inline-start: clamp(0px, calc(var(--slider-at) - var(--slider-mark) / 2), calc(100% - var(--slider-mark)));
  width:         var(--slider-mark);
  height:        var(--slider-mark);
  border-radius: var(--radius-pill);
  background:    var(--ink-dim);
  transform:     translateY(-50%);
  pointer-events: none;
}

.slider-mark[data-in-range] {
  background: var(--accent-ink);
}

.slider[data-thumb-alignment="edge"] .slider-mark,
.slider[data-thumb-alignment="edge"] .slider-mark-label {
  --slider-at: calc(var(--slider-inset) + (100% - var(--slider-thumb)) * var(--slider-p));
}

.slider[data-orientation="vertical"] .slider-mark {
  top:    auto;
  left:   50%;
  bottom: clamp(0px, calc(var(--slider-at) - var(--slider-mark) / 2), calc(100% - var(--slider-mark)));
  transform: translateX(-50%);
}

.slider[data-invalid] .slider-mark[data-in-range] {
  background: var(--bg);
}

/* Mark labels: a row under the control, each label centered on its mark;
   the labels at min and max align to the ends so none leaves the slider. */
.slider-mark-labels {
  position:    relative;
  flex:        0 0 100%;
  height:      calc(var(--text-ui-sm) * 1.5);
  font-family: var(--font-ui);
  font-size:   var(--text-ui-sm);
  line-height: 1.5;
  color:       var(--ink-dim);
  font-variant-numeric: tabular-nums;
  pointer-events: none;
}

.slider-mark-label {
  --slider-at: calc(100% * var(--slider-p));

  position:    absolute;
  top:         0;
  inset-inline-start: var(--slider-at);
  white-space: nowrap;
  transform:   translateX(calc(var(--slider-shift) * -100%));
}

.slider-mark-label:dir(rtl),
.slider[dir="rtl"] .slider-mark-label {
  transform: translateX(calc(var(--slider-shift) * 100%));
}

.slider[data-orientation="vertical"] .slider-mark-labels {
  position: absolute;
  inset-block: 0;
  inset-inline-start: 100%;
  flex:     none;
  width:    max-content;
  height:   auto;
}

.slider[data-orientation="vertical"] .slider-mark-label {
  top:       auto;
  inset-inline-start: var(--space-2);
  bottom:    var(--slider-at);
  transform: translateY(calc(var(--slider-shift) * 100%));
}

.slider-value {
  font-variant-numeric: tabular-nums;
}

.slider-input {
  display: none;
}

}
