/* ============================================================
   ds-kit - the ambient layer, cursor v3
   The jelly ring with its aim dot, lit up with speed; its tube
   trail in the comet blue, leaving the mark 4px narrower than it is across the
   motion and stopping 2px short of its keyline; the parting dot grid, its droplets at
   speed and its click ripple plus splash. Everything the cursor
   draws lives on one canvas inside #cursor, so every stroke keeps its
   declared width whatever the ring's shape does. Load after components.css;
   cursor.js boots it. Rules and mechanisms: cursor.md.
   Contrast, from the repo root, `node tools/contrast.mjs "<fg>" "<bg>"`, on
   --bg / --surface-1 / --surface-2 / --surface-3 / --well / --sky-peak:
     dark  ink        oklch(0.76 0.110 238)  7.49 / 6.48 / 5.48 / 4.48 / 8.31 / 6.33
     dark  lit ink    oklch(0.90 0.080 238)  11.73 / 10.15 / 8.59 / 7.01 / 13.02 / 9.92
     dark  ripple     oklch(0.82 0.115 238)  the trail core's row
     dark  keyline    --bg on the --accent fill 7.49, on --accent-press 6.02
     dark  refuse     oklch(0.72 0.140 15)   5.98 --bg, 5.18 --surface-1
     dark  trail core oklch(0.82 0.115 238)  9.15 / 7.91 / 6.70 / 5.47 / 10.15 / 7.73
     dark  trail edge --accent, the ink's row
     dark  spray      oklch(0.88 0.070 238)  11.13 / 9.62 / 8.14 / 6.65 / 12.34 / 9.40
     dark  spray hot  oklch(0.80 0.130 39)   7.93 / 6.86 / 5.81 / 4.74 / 8.80 / 6.71
     light ink        oklch(0.50 0.130 238)  5.06 / 5.69 / 4.76 / 4.34 / 4.62 / 5.68
     light lit ink    oklch(0.55 0.160 238)  4.01 / 4.51 / 3.78 / 3.44 / 3.66 / 4.50
     light ripple     the ink's row
     light keyline    --bg on the --accent fill 4.09, on --accent-press 6.50
     light refuse     oklch(0.52 0.190 15)   5.36 --bg, 6.02 --surface-1
     light trail core oklch(0.42 0.120 238)  7.10 / 7.98 / 6.68 / 6.10 / 6.48 / 7.97
     light trail edge --accent               4.09 / 4.60 / 3.85 / 3.52 / 3.74 / 4.60
     light spray      oklch(0.45 0.130 238)  6.21 / 6.98 / 5.85 / 5.33 / 5.67 / 6.98
     light spray hot  oklch(0.50 0.140 39)   5.61 / 6.30 / 5.28 / 4.81 / 5.12 / 6.30
   The tube paints at 0.9 alpha, 0.6 over text; composited on --surface-3, its
   worst surface, `node tools/contrast.mjs "<fg> / 0.9" "<surface-3>"`: dark
   core 4.76, edge 3.95; light core 5.01, edge 3.10. Over the accent fill the trail core
   reads 1.22 dark and 1.73 light, as v2's comet reads 1.00 / 1.24:
   the mark and the droplets carry the page keyline there, the tube does not.
   The bloom is the glow ink at 0.105 at full speed on its own node, blended
   `screen` in dark and `darken` in light, the halo's two directions, so it
   lightens what it covers and never shades it. Its peak is the halo's bound:
   the highest opacity at which no text pair reads lower than under the ring's
   12% hover fill, the bloom and the fill each blended over the glyph and its
   ground alike, from the tool's exports. Dark holds to 0.105, where --accent
   on --surface-3 reads 3.687 against its floor of 3.667 and --text-4 on
   --surface-1 3.987 against 3.878 (plain 4.729). Light, where the ring fills with
   --cursor-ink, holds to 0.480, where --on-accent on --accent reads 3.966
   against 3.967; at 0.105 --text-4 on --surface-1 reads 5.380 against 4.772
   (plain 5.555).
   ============================================================ */

#bg-dots { position: fixed; inset: 0; z-index: -1; pointer-events: none; }

/* `--cursor-v3` is the module's proof that this sheet applies: no colour token a page may
   reset, and declared by no v1 or v2 file */
:root {
  --cursor-v3: 1;
  --cursor-ink: var(--accent);
  --cursor-lit: oklch(0.90 0.080 238);
  --cursor-glow: oklch(0.90 0.080 238);
  --cursor-glow-blend: screen;
  --cursor-keyline: var(--bg);
  --cursor-refuse: var(--danger);
  --cursor-ripple: oklch(0.82 0.115 238);
  --cursor-trail-core: oklch(0.82 0.115 238);
  --cursor-trail-edge: var(--accent);
  --cursor-spray: oklch(0.88 0.070 238);
  --cursor-spray-hot: oklch(0.80 0.130 39);
  --cursor-size-idle: 18px;
  --cursor-size-hover: 24px;
  --cursor-size-press: 19px;
  --cursor-size-press-idle: 14px;
  --cursor-size-blocked: 24px;
}
[data-theme="light"] {
  --cursor-ink: oklch(0.50 0.130 238);
  --cursor-lit: oklch(0.55 0.160 238);
  --cursor-glow: oklch(0.89 0.070 238);
  --cursor-glow-blend: darken;
  --cursor-keyline: var(--bg);
  --cursor-refuse: var(--danger);
  --cursor-ripple: oklch(0.50 0.130 238);
  --cursor-trail-core: oklch(0.42 0.120 238);
  --cursor-trail-edge: var(--accent);
  --cursor-spray: oklch(0.45 0.130 238);
  --cursor-spray-hot: oklch(0.50 0.140 39);
}

/* The host is its own view-transition group: the theme swap would otherwise
   freeze the cursor inside the root's old snapshot while the live one fades in,
   so for the crossfade there would be two cursors and a ghost. It never fades
   as a whole: the mark fades on the canvas, so a droplet still flying when the
   pointer leaves the window plays out. */
#cursor {
  position: fixed;
  inset: 0;
  z-index: 10000;
  pointer-events: none;
  view-transition-name: cursor;
}
#cursor-paint { position: absolute; top: 0; left: 0; }
::view-transition-old(cursor) { display: none; }
::view-transition-new(cursor) { animation: none; }

/* The light-up's bloom, under the mark: a 68px box scaled to the speed, its
   blend the one direction per theme that cannot darken what it covers, as the
   legacy halo blends. cursor.js places and fades it every frame. */
#cursor-glow {
  position: fixed;
  top: 0;
  left: 0;
  z-index: 9989;
  width: 68px;
  height: 68px;
  pointer-events: none;
  opacity: 0;
  mix-blend-mode: var(--cursor-glow-blend);
  background: radial-gradient(circle closest-side, var(--cursor-glow), transparent);
}

/* A cache can serve another version's cursor.js beside this file. A v1 or v2 module arms
   `custom-cursor` for a mark this sheet does not style, so the native cursor hides only for
   a v3 mark: under `cursor-v3`, which only this sheet's own module sets, or beside
   `#cursor-paint`, which an older v3 module builds without setting the class. The `:has()`
   guard is a rule of its own: an engine without `:has()` drops the whole selector list. Both
   hide only inside the module's own gate query, where the mark can show, and the rule below
   hides #cursor outside it: whatever a module does with `custom-cursor` when the gate changes,
   the native cursor and the mark never hide together. */
@media (hover: hover) and (pointer: fine) and (prefers-reduced-motion: no-preference) {
  html.custom-cursor.cursor-v3, html.custom-cursor.cursor-v3 * { cursor: none !important; -webkit-user-drag: none; }
  html.custom-cursor:has(#cursor-paint), html.custom-cursor:has(#cursor-paint) * { cursor: none !important; -webkit-user-drag: none; }
}
/* a v1 or v2 module's ring, comet and halo stay out of the page flow beside this sheet:
   unstyled, the comet's viewport-sized canvas and the halo would push the page taller */
#cursor-ring, #cursor-comet, #cursor-halo { display: none !important; }
/* a native drag ghost carries its own OS cursor, class or no class */
a, img { -webkit-user-drag: none; }
@media (hover: none), (pointer: coarse), (prefers-reduced-motion: reduce) { #cursor, #cursor-glow { display: none; } }
