/* The effects: every rule that names a slot. Loaded after the core
   sheet; a rule here answers the attribute the script sets on the row
   for a slot in use (data-light, data-bubble, data-give, data-snap,
   data-bounce) and nothing else. A tool at the Plain tier never loads
   this sheet. */

/* ---- the give ---------------------------------------------------------
   a push past an end is an elastic give on that end: the bar grows by it
   (a negative margin, into the gap and the neighbour), the neighbour is
   squeezed from its outer edge by the same amount, so the gap between
   them never changes; on release the give goes to 0 on the clock. The
   label gives from its left edge, so its first letter stays and the
   words give ground toward the bar (owner, 2026-08-26); the value field
   from its right. Words inside the bar have no column to give: they
   travel with the bar (the Bar Field's rule) */
.ds-slider[data-give] {
  --give-l: 0px;
  --give-r: 0px;
}
.ds-slider[data-give][data-label="beside"] .ds-slider__label {
  transform-origin: left center;
  transform: scaleX(calc(1 - var(--give-l) / var(--ds-slider-label)));
  transition: transform var(--ds-motion-clock) var(--ds-motion-ease);
}
.ds-slider[data-give] .ds-slider__bar {
  margin-left: calc(var(--give-l) * -1);
  margin-right: calc(var(--give-r) * -1);
  transition:
    margin var(--ds-motion-clock) var(--ds-motion-ease),
    transform var(--ds-motion-clock) var(--ds-motion-ease);
}
.ds-slider[data-give][data-value="beside"] .ds-slider__field {
  transform-origin: right center;
  transform: scaleX(calc(1 - var(--give-r) / var(--ds-slider-value)));
  transition:
    transform var(--ds-motion-clock) var(--ds-motion-ease),
    --field-tint var(--ds-motion-clock) var(--ds-motion-ease);
}
/* while the page recolours (lib/core.js), the give's bar and field keep
   their own transitions and ride the clock for their colours too (the
   give's lists above outrank the core sheet's recolour rule) */
html[data-recolour] .ds-slider[data-give] .ds-slider__bar {
  transition:
    margin var(--ds-motion-clock) var(--ds-motion-ease),
    transform var(--ds-motion-clock) var(--ds-motion-ease),
    background-color var(--ds-motion-clock) var(--ds-motion-ease),
    box-shadow var(--ds-motion-clock) var(--ds-motion-ease);
}
html[data-recolour]
  .ds-slider[data-give][data-value="beside"]
  .ds-slider__field {
  transition:
    transform var(--ds-motion-clock) var(--ds-motion-ease),
    --field-tint var(--ds-motion-clock) var(--ds-motion-ease),
    background-color var(--ds-motion-clock) var(--ds-motion-ease),
    color var(--ds-motion-clock) var(--ds-motion-ease),
    box-shadow var(--ds-motion-clock) var(--ds-motion-ease);
}
/* the words inside the bar travel with its edge (round 6, iteration
   18; owner: "Overstretch should also move text / value with it so
   margins are correct"): the two layers of words and the value's field
   stand on the bar's grown box, so the label stays 8 from the start
   and the value 8 from the end; the fill's cut keeps its own clock */
.ds-slider[data-give] .ds-slider__inside {
  left: calc(var(--give-l) * -1);
  right: calc(var(--give-r) * -1);
  transition:
    left var(--ds-motion-clock) var(--ds-motion-ease),
    right var(--ds-motion-clock) var(--ds-motion-ease);
}
.ds-slider[data-give] .ds-slider__inside--fill {
  transition:
    clip-path var(--ds-motion-follow) var(--arrive),
    left var(--ds-motion-clock) var(--ds-motion-ease),
    right var(--ds-motion-clock) var(--ds-motion-ease);
}
.ds-slider[data-give] .ds-slider__field[data-inside] {
  right: calc(var(--give-r) * -1);
  transition: right var(--ds-motion-clock) var(--ds-motion-ease);
}
/* while the hand pushes, the give is the hand's: no transition */
.ds-slider[data-give][data-held] .ds-slider__bar,
.ds-slider[data-give][data-held] .ds-slider__label,
.ds-slider[data-give][data-held] .ds-slider__field,
.ds-slider[data-give][data-held] .ds-slider__inside {
  transition-duration: 0s;
}
/* the finger's grip: a held bar grows to 48 high about its centre, by
   its height (its corners stay what they are: owner, 2026-10-04), over
   its neighbours and without moving the row (the growth is taken back
   as margin), on the clock and the curve with the stretch, so the bar
   lets go of its grip and of its stretch as one move (round 4: the grip
   had come home on the follow clock, half the stretch's, the owner's
   "asynched again") */
@media (pointer: coarse) {
  .ds-slider[data-give] .ds-slider__bar {
    transition:
      margin var(--ds-motion-clock) var(--ds-motion-ease),
      height var(--ds-motion-clock) var(--ds-motion-ease);
  }
  html[data-recolour] .ds-slider[data-give] .ds-slider__bar {
    transition:
      margin var(--ds-motion-clock) var(--ds-motion-ease),
      height var(--ds-motion-clock) var(--ds-motion-ease),
      background-color var(--ds-motion-clock) var(--ds-motion-ease),
      box-shadow var(--ds-motion-clock) var(--ds-motion-ease);
  }
  .ds-slider[data-give] .ds-slider__bar[data-dragging] {
    height: calc(var(--ds-grid-cell) * 12);
    margin-block: calc(
      (var(--ds-slider-height) - var(--ds-grid-cell) * 12) / 2
    );
  }
  .ds-slider[data-give]:has(.ds-slider__bar[data-dragging]) {
    z-index: 2;
  }
}

/* ---- the bounce ---------------------------------------------------------
   a stepped, snapped or typed value lands on the bounce curve, its
   handle past the target: the row's arrival curve, which the fill, the
   light and the words inside the bar read */
.ds-slider[data-bounce] {
  --arrive: var(--ds-motion-bounce);
}

/* ---- the magnetic snap --------------------------------------------------
   with the Shift grid held under a drag, the fill eases into the line on
   the follow clock (the pull the hand feels), on the curve, never the
   bounce; whatever is cut where the fill is cut eases with it: the
   light's two tiers (their clips, and a Range's side masks) and a
   Range's end marks, so nothing jumps while the fill eases */
.ds-slider[data-snap][data-held] .ds-slider__bar[data-grid] .ds-slider__fill,
.ds-slider[data-snap][data-held]
  .ds-slider__bar[data-grid]
  .ds-slider__fill::after,
.ds-slider[data-snap][data-held]
  .ds-slider__bar[data-grid]
  .ds-slider__light::before,
.ds-slider[data-snap][data-held]
  .ds-slider__bar[data-grid]
  .ds-slider__light::after,
.ds-slider[data-snap][data-held] .ds-slider__bar[data-grid] .ds-range__end,
.ds-slider[data-snap][data-held]
  .ds-slider__bar[data-grid]
  ~ .ds-slider__inside--fill {
  --arrive: var(--ds-motion-ease);
  transition-duration: var(--ds-motion-follow);
}

/* ---- the spotlight ------------------------------------------------------
   one white pool under the hand in two tiers (owner, 2026-10-05: "two
   tiers of spotlight, for selected part of slider and dimmer version for
   not selected"): full on the fill (the chosen part), a fifth of it on
   the track (the rest), the two cut at the value as the fill is, so the
   pool reads brighter where the value is. One pool, one radius from the
   bar's width with a floor; the live values are written per move on the
   layer, where the gradient resolves, and never transition (the per-move
   value is the animation). The fill's own state tint stays under it.
   The cuts follow the fill's on the follow clock (at once held); a
   Range lights the sides of its run instead (below) */
.ds-slider__light {
  position: absolute;
  inset: 0;
  border-radius: inherit;
  pointer-events: none;
  --light-x: 50%;
  --light-y: 50%;
  --light-w: 0px;
  --light-on: 0;
  /* the track's tier, a share of the fill's */
  --light-rest: 0.2;
  --light-r: max(calc(var(--ds-grid-cell) * 24), calc(var(--light-w) * 1.1));
  opacity: var(--light-on);
  /* its strength comes and goes on the clock and the curve the tints
     and the stretch move on, at once under a press (the motion audit,
     2026-10-12: the pool lit in one frame as the hand came onto the bar,
     and went out at a let-go while the stretch settled) */
  transition: opacity var(--ds-motion-clock) var(--ds-motion-ease);
}
.ds-slider[data-held] .ds-slider__light {
  transition-duration: 0s;
}
.ds-slider__light::before,
.ds-slider__light::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  background: radial-gradient(
    var(--light-r) circle at var(--light-x) var(--light-y),
    rgb(255 255 255 / 0.28) 0%,
    rgb(255 255 255 / 0.14) 32%,
    rgb(255 255 255 / 0.05) 64%,
    transparent 100%
  );
  transition: clip-path var(--ds-motion-follow) var(--arrive);
}
/* the fill's tier: up to the value */
.ds-slider__light::before {
  clip-path: inset(0 calc(100% - var(--to-edge)) 0 var(--from-edge));
}
/* the track's tier: from the value on, dimmer */
.ds-slider__light::after {
  clip-path: inset(0 0 0 var(--edge));
  opacity: var(--light-rest);
}
/* a fill that starts past the bar's start has track on both sides: the
   dimmer tier lies under the whole bar, the fill's over it */
.ds-slider[data-origin] .ds-slider__light::after {
  clip-path: none;
}
.ds-slider[data-held] .ds-slider__light::before,
.ds-slider[data-held] .ds-slider__light::after {
  transition-duration: 0s;
}

/* ---- the Range's sides ----------------------------------------------------
   the owner, 2026-09-07: "For range slide: active area should have
   gradients on the edges as it did. Problem was with inactive area which
   had displaced spotlight"; asked for again on 2026-10-06 (the tool
   plan, §3.1, superseding his "one piece" of the same 2026-09-07 and the
   slider-master row C22). The one pool, on the one bar-wide layer, in
   the bar's own coordinates (no moving segment carries a light, so
   neither of 2026-09-07's bugs can come back: the second frame, the
   corners that slid):
   - inside the run (::before, cut at the run as the fill is) the pool is
     multiplied by two side masks, each strength 1 at its edge and fading
     inward over --light-band (0.45 of the bar, the Bar Field's 320 of
     686) on the Bar Field's ten-stop exponential (1, .664, .439, .288,
     .187, .119, .074, .043, .015, 0 at 0, .1 ... .7, .85, 1 of the
     band), composited by mask-composite: add (one side over the other,
     a + b(1 - a), where a run narrower than two bands lets them meet);
     the side nearer the hand is the brighter by the pool itself, and
     the run's middle has no floor;
   - outside the run, on both sides (::after), the ordinary unselected
     light: the pool at --light-rest, cut away over the run by a mask.
   Each mask is a static gradient twice the bar wide with its edge at
   its middle, so a percentage of mask-position puts that middle on the
   run's edge (a position's percentage is of the bar less the image: of
   minus the bar, here), rounded to the device pixel as --from-edge and
   --to-edge are; the masks move by mask-position on the follow clock
   and the row's arrival curve with the clip, and at once held. Nothing
   inside a mask's gradient moves: WebKit does not interpolate a custom
   property there (docs/tone-of-voice/motion.md, "The mask is carried,
   not transitioned"). Beyond each edge the side's mask stays 1, so a
   clip and a mask a hair apart never leave a dark seam */
.ds-range .ds-slider__light {
  --light-band: calc(var(--light-w) * 0.45);
}
.ds-range .ds-slider__light::before,
.ds-range .ds-slider__light::after {
  mask-size: 200% 100%;
  mask-repeat: no-repeat;
  mask-composite: add;
  mask-position:
    calc(round(nearest, calc(var(--from) * -100%), var(--ds-hair)) + 100%) 0,
    calc(round(nearest, calc(var(--to) * -100%), var(--ds-hair)) + 100%) 0;
  transition:
    clip-path var(--ds-motion-follow) var(--arrive),
    mask-position var(--ds-motion-follow) var(--arrive);
}
.ds-range .ds-slider__light::before {
  mask-image:
    linear-gradient(
      to right,
      #000 50%,
      rgb(0 0 0 / 0.664) calc(50% + var(--light-band) * 0.1),
      rgb(0 0 0 / 0.439) calc(50% + var(--light-band) * 0.2),
      rgb(0 0 0 / 0.288) calc(50% + var(--light-band) * 0.3),
      rgb(0 0 0 / 0.187) calc(50% + var(--light-band) * 0.4),
      rgb(0 0 0 / 0.119) calc(50% + var(--light-band) * 0.5),
      rgb(0 0 0 / 0.074) calc(50% + var(--light-band) * 0.6),
      rgb(0 0 0 / 0.043) calc(50% + var(--light-band) * 0.7),
      rgb(0 0 0 / 0.015) calc(50% + var(--light-band) * 0.85),
      transparent calc(50% + var(--light-band))
    ),
    linear-gradient(
      to left,
      #000 50%,
      rgb(0 0 0 / 0.664) calc(50% + var(--light-band) * 0.1),
      rgb(0 0 0 / 0.439) calc(50% + var(--light-band) * 0.2),
      rgb(0 0 0 / 0.288) calc(50% + var(--light-band) * 0.3),
      rgb(0 0 0 / 0.187) calc(50% + var(--light-band) * 0.4),
      rgb(0 0 0 / 0.119) calc(50% + var(--light-band) * 0.5),
      rgb(0 0 0 / 0.074) calc(50% + var(--light-band) * 0.6),
      rgb(0 0 0 / 0.043) calc(50% + var(--light-band) * 0.7),
      rgb(0 0 0 / 0.015) calc(50% + var(--light-band) * 0.85),
      transparent calc(50% + var(--light-band))
    );
}
.ds-range .ds-slider__light::after {
  clip-path: none;
  mask-image:
    linear-gradient(to right, #000 50%, transparent 50%),
    linear-gradient(to left, #000 50%, transparent 50%);
}

/* ---- the bubble readout -------------------------------------------------
   the value above the bar while it is engaged: one line of the sans in
   capitals (owner, 2026-10-04), its window's type (16, or 14 in a
   compact window), on a 24 box (six cells), 8 in at either side, 8
   above the bar (the script seats it), its corner the slider's own (the
   pill when the bar is one), the text colour's ground and the page's
   ink; under a finger a 32 box. Its digits are
   tabular, so a change of value never jogs the text; when the text
   grows a digit the box's width follows on the follow clock (the script
   writes the width). Its transform and opacity are written per frame
   by the script; nothing here transitions them. It sits under the bar
   in the wrap's stack, so it emerges from behind the bar */
.ds-slider__bubble {
  /* how far above the bar it sits (the script reads it) */
  --bubble-gap: var(--ds-space-8);
  position: absolute;
  z-index: 0;
  top: 50%;
  left: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  box-sizing: border-box;
  height: calc(var(--ds-grid-cell) * 6);
  padding: 0 var(--ds-space-8);
  border-radius: min(var(--slider-corner), calc(var(--ds-grid-cell) * 3));
  background: var(--ds-color-text);
  color: var(--ds-color-page);
  font-family: var(--ds-type-family);
  /* its window's type: 16, or 14 in a compact window */
  font-size: var(--type, var(--ds-text-16));
  line-height: 1;
  font-variant-numeric: tabular-nums;
  text-transform: uppercase;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  overflow: hidden;
  transition: width var(--ds-motion-follow) var(--ds-motion-ease);
  will-change: transform, opacity;
}
.ds-slider__bubble[hidden] {
  display: none;
}
@media (pointer: coarse) {
  .ds-slider__bubble {
    height: calc(var(--ds-grid-cell) * 8);
    border-radius: min(var(--slider-corner), calc(var(--ds-grid-cell) * 4));
  }
}

@media (forced-colors: active) {
  .ds-slider__bubble {
    forced-color-adjust: none;
    background: Highlight;
    color: HighlightText;
  }
}
