/*
 * donut: one pen draws the whole ring. The segments are static; a mask circle draws round once on
 * --ease-inout, so the sweep is one continuous stroke that lifts at every gap. The centre total
 * (.count) counts on the same clock and curve, so the number is always where the pen is.
 * Each segment takes --v (its share, 0 to 100) and --at (the sum of the shares before it).
 */
.donut { display: block; overflow: visible; }
.donut-track,
.donut-seg { fill: none; stroke-width: var(--donut-w, 22); }
.donut-track { stroke: var(--donut-track, currentColor); opacity: var(--donut-track-o, .1); }
.donut-seg {
  stroke: var(--c, currentColor);
  stroke-dasharray: calc(var(--v) - var(--donut-gap, .9)) 100;
  stroke-dashoffset: calc(var(--at) * -1);
}
.donut-pen { fill: none; stroke: #fff; stroke-width: calc(var(--donut-w, 22) + 24); stroke-dasharray: 100 100; }
.js .donut-pen { stroke-dashoffset: 100; }
.js .go .donut-pen,
.js .go.donut .donut-pen { animation: pen var(--dur-draw) var(--ease-inout) both; }
@keyframes pen { from { stroke-dashoffset: 100; } to { stroke-dashoffset: 0; } }

/* donut-tip: optional nib that rides the front of the pen and lifts off when the ring closes. Starts at 3 o'clock in the ring's own space. */
.donut-tip { transform-box: view-box; transform-origin: 100px 100px; opacity: 0; }
.js .go .donut-tip,
.js .go.donut .donut-tip { animation: donut-tip var(--dur-draw) var(--ease-inout) both; }
@keyframes donut-tip {
  from { transform: rotate(0deg); opacity: 1; }
  92%  { opacity: 1; }
  to   { transform: rotate(360deg); opacity: 0; }
}

/* donut-key: the one segment the reader should look at. It thickens on the spring once the pen has passed it. */
.donut-key { stroke-width: calc(var(--donut-w, 22) + 8); }
.js .donut-key { stroke-width: var(--donut-w, 22); }
.js .go .donut-key,
.js .go.donut .donut-key { animation: donut-key var(--dur-base) var(--ease-spring) var(--dur-draw) both; }
@keyframes donut-key {
  from { stroke-width: var(--donut-w, 22); }
  to   { stroke-width: calc(var(--donut-w, 22) + 8); }
}

/*
 * gauge: a half ring that fills while a needle swings to --v (0 to 100) and lands with a slight
 * overshoot (--ease-paper). Not the full spring: a needle that reads 82 on its way to 68 looks like a wrong number. The fill, the needle and a .count beside it share one clock.
 * Start it after something else with --gauge-delay.
 */
.gauge { display: block; overflow: visible; }
.gauge-track,
.gauge-fill { fill: none; stroke-width: var(--gauge-w, 14); }
.gauge-track { stroke: var(--gauge-track, currentColor); opacity: .1; }
.gauge-fill { stroke: var(--c, currentColor); stroke-dasharray: var(--v) 100; }
.gauge-pen { fill: none; stroke: #fff; stroke-width: calc(var(--gauge-w, 14) + 12); stroke-dasharray: 100 100; stroke-dashoffset: calc(100 - var(--v)); }
.gauge-needle { transform-box: view-box; transform-origin: 100px 100px; transform: rotate(calc(var(--v) * 1.8deg - 90deg)); }
.js .gauge-pen { stroke-dashoffset: 100; }
.js .gauge-needle { transform: rotate(-90deg); }
.js .go .gauge-pen,
.js .go.gauge .gauge-pen { animation: gauge-pen var(--gauge-dur, 1200ms) var(--ease-paper) var(--gauge-delay, 0ms) both; }
.js .go .gauge-needle,
.js .go.gauge .gauge-needle { animation: gauge-needle var(--gauge-dur, 1200ms) var(--ease-paper) var(--gauge-delay, 0ms) both; }
@keyframes gauge-pen { from { stroke-dashoffset: 100; } to { stroke-dashoffset: calc(100 - var(--v)); } }
@keyframes gauge-needle {
  from { transform: rotate(-90deg); }
  to   { transform: rotate(calc(var(--v) * 1.8deg - 90deg)); }
}

/*
 * count: a number that counts up to --to with no script. The digits are a CSS counter driven by a
 * registered integer property. The real number stays in the markup (.count-text) for screen
 * readers and copy; the counted copy has empty alt text. Match the clock with --count-dur,
 * --count-ease and --count-delay. Browsers without @property jump straight to the end.
 */
@property --count { syntax: "<integer>"; inherits: false; initial-value: 0; }
.count { position: relative; }
.count-text {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap;
}
.count::after { --count: var(--to, 0); counter-reset: count var(--count); content: counter(count) / ""; }
.js .count::after { --count: 0; }
.js .go .count::after,
.js .count.go::after { animation: count var(--count-dur, var(--dur-draw)) var(--count-ease, var(--ease-inout)) var(--count-delay, 0ms) both; }
@keyframes count { from { --count: 0; } to { --count: var(--to, 0); } }

/* count-k: thousands get a comma (3,200). Needs CSS round() and mod(); older browsers show 3200. */
@counter-style count-blank { system: cyclic; symbols: "\200B"; }
@counter-style count-lead { system: extends decimal; range: 1 infinite; fallback: count-blank; }
@counter-style count-comma { system: cyclic; symbols: ","; range: 1 infinite; fallback: count-blank; }
@counter-style count-plain { system: extends decimal; range: 0 infinite; fallback: count-blank; }
@counter-style count-pad3 { system: extends decimal; pad: 3 "0"; range: 0 infinite; fallback: count-blank; }
@supports (z-index: mod(7, 3)) {
  .count-k::after {
    --k: round(down, var(--count) / 1000);
    --r: mod(var(--count), 1000);
    --on: min(var(--k), 1);
    counter-reset: k var(--k) lo calc(var(--r) - var(--on) * 100000) pad calc(var(--r) - (1 - var(--on)) * 100000);
    content: counter(k, count-lead) counter(k, count-comma) counter(lo, count-plain) counter(pad, count-pad3) / "";
  }
}

@media (prefers-reduced-motion: reduce) {
  .js .donut-pen, .js .go .donut-pen, .js .go.donut .donut-pen { animation: none; stroke-dashoffset: 0; }
  .js .donut-key, .js .go .donut-key, .js .go.donut .donut-key { animation: none; stroke-width: calc(var(--donut-w, 22) + 8); }
  .js .gauge-pen, .js .go .gauge-pen, .js .go.gauge .gauge-pen { animation: none; stroke-dashoffset: calc(100 - var(--v)); }
  .js .gauge-needle, .js .go .gauge-needle, .js .go.gauge .gauge-needle { animation: none; transform: rotate(calc(var(--v) * 1.8deg - 90deg)); }
  .js .go .donut-tip, .js .go.donut .donut-tip { animation: none; }
  .js .count::after, .js .go .count::after, .js .count.go::after { animation: none; --count: var(--to, 0); }
}
