/* IntuiMotion — site-specific styles, built on shared/design-system.css
   tokens (linked before this file). This file intentionally does NOT
   redeclare neutrals, type, spacing, radius, motion, or base-layer rules
   (box-sizing, body, code, .wrap, scroll-behavior) — those live once in
   design-system.css so every site in the suite stays visually identical
   except for accent color and page-specific components. */

/* ---------- accent override: IntuiMotion identity color (royal purple) ----------
   Same three-layer pattern as design-system.css's own color block; see
   the header comment there for why the :where() guard matters. */

:root {
  --accent: #a78bfa;
  --accent-strong: #c9b6fd;
  --accent-soft: rgba(167, 139, 250, 0.2);
}

@media (prefers-color-scheme: light) {
  :root:where(:not([data-theme])) {
    --accent: #4c2f8c;
    --accent-strong: #35205f;
    --accent-soft: rgba(76, 47, 140, 0.1);
  }
}

:root[data-theme="dark"] {
  --accent: #a78bfa;
  --accent-strong: #c9b6fd;
  --accent-soft: rgba(167, 139, 250, 0.2);
}

:root[data-theme="light"] {
  --accent: #4c2f8c;
  --accent-strong: #35205f;
  --accent-soft: rgba(76, 47, 140, 0.1);
}

/* ---------- second identity hue: --accent2 (deep magenta) ----------
   Site-scoped only (per design-system.css's rule: only the --accent*
   trio may be overridden per-site, and only from a site's own
   style.css). This is a second, equally-shaped trio for the same
   purpose: giving THIS page a bit more color energy than a single
   accent allows, for decorative/motion use only. Never used for body
   text, which stays on the shared --text/--text-dim/--text-faint
   scale from design-system.css. Pairing it with the royal-purple
   --accent above (and the wine --accent3 below) reads as one
   deliberate warm violet-through-red identity, not a clash, even
   though it sits close in hue to the suite's semantic --bad and to
   design-system.css's own default accent (the wine reused verbatim
   below as --accent3): the separation from semantic meaning here is
   by usage (decorative/motion only), not by hue distance. Same
   three-layer theme-override shape as --accent above. */
:root {
  --accent2: #f050c8;
  --accent2-strong: #f79bde;
  --accent2-soft: rgba(240, 80, 200, 0.22);
}

@media (prefers-color-scheme: light) {
  :root:where(:not([data-theme])) {
    --accent2: #9c0f70;
    --accent2-strong: #750b53;
    --accent2-soft: rgba(156, 15, 112, 0.1);
  }
}

:root[data-theme="dark"] {
  --accent2: #f050c8;
  --accent2-strong: #f79bde;
  --accent2-soft: rgba(240, 80, 200, 0.22);
}

:root[data-theme="light"] {
  --accent2: #9c0f70;
  --accent2-strong: #750b53;
  --accent2-soft: rgba(156, 15, 112, 0.1);
}

/* ---------- third identity hue: --accent3 (wine/burgundy) ----------
   Site-scoped only, same shape and same design-system.css exception
   as --accent2 above. Not a new color: this is shared/design-system.
   css's own suite-default accent (a deep wine/burgundy — see the
   color-block comment there) brought back in deliberately as this
   page's third hue, values reused verbatim rather than invented
   fresh. Rounds the royal-purple / deep-magenta pair out into a warm
   three-color identity that stays in one family (violet through
   magenta through red) instead of reaching for an unrelated hue. It
   sits close in hue to the suite's semantic --bad, same caveat as
   --accent2 above — the separation from semantic meaning is by usage
   (decorative/motion only, e.g. the hero-flow wash below), never a
   status indicator. Never used for body copy. Same three-layer
   theme-override shape as above. */
:root {
  --accent3: #be3747;
  --accent3-strong: #e65a69;
  --accent3-soft: rgba(190, 55, 71, 0.18);
}

@media (prefers-color-scheme: light) {
  :root:where(:not([data-theme])) {
    --accent3: #962c3d;
    --accent3-strong: #772230;
    --accent3-soft: rgba(150, 44, 61, 0.10);
  }
}

:root[data-theme="dark"] {
  --accent3: #be3747;
  --accent3-strong: #e65a69;
  --accent3-soft: rgba(190, 55, 71, 0.18);
}

:root[data-theme="light"] {
  --accent3: #962c3d;
  --accent3-strong: #772230;
  --accent3-soft: rgba(150, 44, 61, 0.10);
}

/* ---------- off-white highlight: --tint-highlight ----------
   A small site-scoped exception alongside the --accent* trio above,
   not a redeclaration of design-system.css's shared --bg/--text scale
   (those stay identical across all four sites per design-system.css's
   own rule). A touch of off-white for decorative highlight use only —
   e.g. the brightest "core" point of a glow or gradient — never body
   copy or a background fill. Deliberately the same value in all four
   blocks below: unlike --accent/--accent2/--accent3, an off-white has
   no natural "light mode" inversion (a color dark/saturated enough to
   read against a light background has stopped being off-white). That
   makes this token dark-mode-effective only — against light mode's
   near-white --bg (#f7f7f5) and white --bg-card, #f5f1f8 reads as
   barely-there by design, not a bug. Any decorative use that needs a
   bright "core" point to still read in light mode (e.g. a glow or
   trail's brightest point) should fall back to --accent-strong there
   instead of this token. Still carries the full three-layer shape
   below for toggle-safety/consistency with every other token here. */
:root {
  --tint-highlight: #f5f1f8;
}

@media (prefers-color-scheme: light) {
  :root:where(:not([data-theme])) {
    --tint-highlight: #f5f1f8;
  }
}

:root[data-theme="dark"] {
  --tint-highlight: #f5f1f8;
}

:root[data-theme="light"] {
  --tint-highlight: #f5f1f8;
}

/* Links inside page prose only — scoped to `main` so it can't override
   .site-nav__links / .site-footer__links, which set their own color and
   text-decoration in design-system.css. */
main a {
  color: var(--accent-strong);
  text-decoration-color: var(--accent-soft);
  text-underline-offset: 0.15em;
}
main a:hover { text-decoration-color: currentColor; }

/* ---------- hero ---------- */

header.hero {
  position: relative;
  isolation: isolate;
  padding: var(--space-section) 0 var(--space-16);
  border-bottom: 1px solid var(--border);
  overflow: hidden;
}

header.hero::before {
  content: "";
  position: absolute;
  top: -16rem;
  left: 50%;
  width: 50rem;
  height: 30rem;
  transform: translateX(-50%);
  background: radial-gradient(closest-side, var(--accent-soft), transparent 70%);
  pointer-events: none;
  z-index: -1;
}

/* ---------- hero flow (fluid, gesture-like motion) ----------
   The one piece of motion on this page meant to read as "gesture" on
   sight: a slow-turning gradient cycling through the three identity
   hues (royal purple, deep magenta, wine), with a brief off-white
   spark stitching the seam where it wraps back to purple, standing in
   for the open hand becoming a pointer that the copy describes.
   Deliberately not filter: blur() — a large blurred layer gets
   repainted every frame of a 26s rotation, which is wasteful for a
   documentation page; the soft edge instead comes from a
   radial-gradient mask, which the
   compositor handles far more cheaply alongside the transform
   animation. Sits behind the static ::before wash above (z-index -2
   vs -1) so both layer together without fighting for the same space.
   No data-reveal here: it's ambient chrome, not content, so it runs
   from first paint rather than waiting on scroll. */
.hero-flow {
  position: absolute;
  top: -13rem;
  left: 50%;
  width: 42rem;
  height: 42rem;
  margin-left: -21rem;
  /* No border-radius here: the mask below already clips this to a
     circle (closest-side on a square box), so a border-radius would
     be dead weight implying a clip that isn't its job. */
  background: conic-gradient(from 0deg at 50% 50%,
    color-mix(in srgb, var(--accent) 55%, transparent),
    color-mix(in srgb, var(--accent2) 55%, transparent) 33%,
    color-mix(in srgb, var(--accent3) 55%, transparent) 66%,
    color-mix(in srgb, var(--accent3) 55%, transparent) 90%,
    color-mix(in srgb, var(--tint-highlight) 55%, transparent) 95%,
    color-mix(in srgb, var(--accent) 55%, transparent));
  -webkit-mask-image: radial-gradient(closest-side, black 0%, black 45%, transparent 100%);
  mask-image: radial-gradient(closest-side, black 0%, black 45%, transparent 100%);
  opacity: .55;
  pointer-events: none;
  z-index: -2;
  animation: hero-flow-drift 26s linear infinite;
}

/* Dark mode's --accent/--accent2 are pale (--accent3's wine is the
   one mid-toned exception), so the wash still reads gently overall
   there at .55 opacity; light mode's are deep, saturated brand colors
   (see the override blocks above), so the same conic-gradient needs a
   somewhat lower element opacity in light mode or it prints as a
   heavy stain on a near-white background instead of a light wash.
   Kept high enough (.42, not lower) that it still reads as real
   color rather than fading into gray haze — this is the page's one
   deliberately vivid, attention-grabbing element, and light mode
   needs to earn that too, not just dark mode. (The --tint-highlight
   spark is dark-mode-effective only — see its token comment above —
   so in light mode this wash quietly drops back to a two-hue-plus-
   wine cycle at the seam; that's expected, not a bug.) Same
   three-layer guard as every other themed value in this file. */
@media (prefers-color-scheme: light) {
  :root:where(:not([data-theme])) .hero-flow { opacity: .42; }
}
:root[data-theme="light"] .hero-flow { opacity: .42; }
:root[data-theme="dark"] .hero-flow { opacity: .55; }

@keyframes hero-flow-drift {
  to { transform: rotate(360deg); }
}

/* Belt-and-suspenders beyond design-system.css's shared kill-switch
   (which already forces animation-duration near 0 and
   iteration-count to 1 under reduced motion, so this rule is
   technically redundant): stopping the animation outright, rather
   than letting it complete one near-instant rotation, means the wash
   never visibly jumps for a reduced-motion viewer. */
@media (prefers-reduced-motion: reduce) {
  .hero-flow { animation: none; }
}

.eyebrow {
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--text-faint);
  margin: 0 0 var(--space-4);
}

h1 {
  font-size: var(--text-4xl);
  line-height: var(--leading-tight);
  letter-spacing: var(--tracking-tightest);
  font-weight: 600;
  margin: 0 0 var(--space-4);
}

.lede {
  font-size: var(--text-lg);
  line-height: var(--leading-normal);
  color: var(--text-dim);
  max-width: 38rem;
  margin: 0 0 var(--space-6);
}

.badges {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.badge {
  display: inline-flex;
  align-items: center;
  gap: 0.4em;
  font-size: var(--text-xs);
  padding: 0.32em 0.75em;
  border-radius: var(--radius-pill);
  border: 1px solid var(--border);
  background: var(--bg-raised);
  color: var(--text-dim);
  white-space: nowrap;
}

.badge .dot {
  width: 0.5em;
  height: 0.5em;
  border-radius: 50%;
  flex: none;
}

/* "Confirmed" mirrors this project's own accent (royal purple) rather
   than the suite's generic --good token, so the hero badge row reads
   as one coherent identity color instead of clashing with green. */
.dot-live { background: var(--accent); }
.dot-tested { background: var(--warn); }
.dot-progress { background: var(--text-faint); }

/* ---------- sections ---------- */

main { padding: 0 0 var(--space-16); }

section {
  padding: var(--space-20) 0;
  border-bottom: 1px solid var(--border);
}
section:last-of-type { border-bottom: none; }

h2 {
  font-size: var(--text-2xl);
  letter-spacing: var(--tracking-tighter);
  font-weight: 600;
  margin: 0 0 var(--space-3);
}

.section-intro {
  color: var(--text-dim);
  font-size: var(--text-md);
  margin: 0 0 var(--space-8);
  max-width: 40rem;
}

h3 {
  font-size: var(--text-md);
  font-weight: 600;
  margin: 0 0 var(--space-2);
}

p { margin: 0 0 var(--space-4); }
p:last-child { margin-bottom: 0; }

ul.plain, ol.plain {
  margin: 0 0 var(--space-4);
  padding-left: 1.25rem;
}
ul.plain li, ol.plain li { margin-bottom: var(--space-2); }
ul.plain li:last-child, ol.plain li:last-child { margin-bottom: 0; }

.note {
  font-size: var(--text-sm);
  color: var(--text-dim);
  background: var(--bg-raised);
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent-soft);
  border-radius: var(--radius-md);
  padding: var(--space-4) var(--space-5);
  margin: var(--space-6) 0 0;
}
.note:first-child { margin-top: 0; }
.note p:last-child { margin-bottom: 0; }
.note strong { color: var(--text); }

/* ---------- gesture table ---------- */

.table-scroll {
  overflow-x: auto;
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  background: var(--bg-card);
}

table {
  border-collapse: collapse;
  width: 100%;
  min-width: 40rem;
  font-size: var(--text-sm);
}

thead th {
  text-align: left;
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--text-faint);
  font-weight: 600;
  padding: var(--space-4);
  border-bottom: 1px solid var(--border);
  white-space: nowrap;
}

tbody td {
  padding: var(--space-4);
  border-bottom: 1px solid var(--border);
  vertical-align: top;
  color: var(--text-dim);
}

tbody tr:last-child td { border-bottom: none; }

tbody tr {
  transition: background var(--duration-fast) var(--ease-standard);
}
tbody tr:hover td { background: var(--bg-raised); }

td.gesture { color: var(--text); white-space: nowrap; }

.mode-pill {
  display: inline-block;
  font-size: var(--text-xs);
  padding: 0.15em 0.55em;
  border-radius: var(--radius-sm);
  white-space: nowrap;
  border: 1px solid var(--border);
  color: var(--text-dim);
}
/* Idle and Pointer get the two identity hues, so the two real modes
   read as distinct colors at a glance; Transition stays neutral
   on purpose, it's a momentary trigger, not a sustained mode. */
.mode-idle {
  background: var(--accent-soft);
  border-color: color-mix(in srgb, var(--accent) 40%, var(--border));
  color: var(--accent-strong);
}
.mode-pointer {
  background: var(--accent2-soft);
  border-color: color-mix(in srgb, var(--accent2) 40%, var(--border));
  color: var(--accent2-strong);
}
.mode-transition { background: var(--bg-raised); }

/* ---------- architecture pipeline ---------- */

.pipeline {
  list-style: none;
  margin: 0;
  padding: 0;
  counter-reset: step;
  position: relative;
}

.pipeline li {
  counter-increment: step;
  position: relative;
  padding: 0 0 var(--space-6) 2.75rem;
}
.pipeline li:last-child { padding-bottom: 0; }

.pipeline li::before {
  content: counter(step);
  position: absolute;
  left: 0;
  top: 0;
  width: 1.9rem;
  height: 1.9rem;
  border-radius: 50%;
  background: var(--bg-raised);
  border: 1px solid var(--border);
  color: var(--text-dim);
  font-size: var(--text-sm);
  font-weight: 600;
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1;
  transition: border-color var(--duration-base) var(--ease-standard),
              color var(--duration-base) var(--ease-standard);
}

.pipeline li:hover::before {
  border-color: var(--accent);
  color: var(--accent-strong);
}

.pipeline li:not(:last-child)::after {
  content: "";
  position: absolute;
  left: 0.95rem;
  top: 1.9rem;
  bottom: -0.05rem;
  width: 1px;
  background: var(--border);
}

.pipeline .step-name {
  color: var(--text);
  font-weight: 600;
  font-size: var(--text-md);
}

.pipeline .step-desc {
  color: var(--text-dim);
  font-size: var(--text-sm);
  margin-top: 0.15rem;
}

/* ---------- calibration cards ---------- */

.card-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--space-5);
  margin-top: var(--space-6);
}

@media (max-width: 42rem) {
  .card-grid { grid-template-columns: 1fr; }
}

.card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-6);
  box-shadow: var(--shadow-sm);
  transition: transform var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}

.card:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  box-shadow: var(--shadow-md);
}

/* Pre-existing gap closed while touching this file: .card carries
   data-reveal in the markup, so .js [data-reveal]'s shorthand
   (0,2,0) was beating the plain .card rule above (0,1,0) and the
   hover transition was silently inheriting the slow 700ms reveal
   timing instead of --duration-base. Same fix shape as
   sites/oanda/public/style.css's .currency-badge/.gate/.cap. */
/* transform carries var(--reveal-delay) too (matching opacity) so the
   scroll-in slide and fade settle together instead of transform
   snapping into place while opacity is still mid-fade on staggered
   cards. Trade-off, accepted deliberately: since :hover also animates
   transform, a settled card's hover-lift now carries that same
   per-card delay (0-160ms) rather than firing instantly — a small cost
   against a first-load entrance that's actually staggered correctly. */
.js .card[data-reveal] {
  transition: opacity var(--duration-reveal) var(--ease-standard) var(--reveal-delay),
              transform var(--duration-reveal) var(--ease-standard) var(--reveal-delay),
              border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}

.card .tag-row {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin-bottom: var(--space-3);
}

.card .tag-row .badge { font-size: var(--text-xs); padding: 0.2em 0.6em; }

.card p:last-child { margin-bottom: 0; }
.card ul.plain:last-child { margin-bottom: 0; }

@media (prefers-reduced-motion: reduce) {
  .card:hover { transform: none; }
}

/* ---------- status grid ---------- */

.status-grid {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: var(--space-4);
  margin: var(--space-6) 0 var(--space-8);
}

@media (max-width: 42rem) {
  .status-grid { grid-template-columns: 1fr; }
}

.status-card {
  background: var(--bg-card);
  border: 1px solid var(--border);
  border-radius: var(--radius-md);
  padding: var(--space-5);
  transition: transform var(--duration-base) var(--ease-standard),
              border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}

.status-card:hover {
  transform: translateY(-4px);
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  box-shadow: var(--shadow-md);
}

/* Same pre-existing gap as .card above, same fix, same transform+delay
   trade-off (see the comment on .js .card[data-reveal] above). */
.js .status-card[data-reveal] {
  transition: opacity var(--duration-reveal) var(--ease-standard) var(--reveal-delay),
              transform var(--duration-reveal) var(--ease-standard) var(--reveal-delay),
              border-color var(--duration-base) var(--ease-standard),
              box-shadow var(--duration-base) var(--ease-standard);
}

.status-card .badge { margin-bottom: var(--space-3); }

.status-card h4 {
  margin: 0 0 var(--space-2);
  font-size: var(--text-md);
}

.status-card p {
  font-size: var(--text-sm);
  color: var(--text-dim);
  margin: 0;
}

@media (prefers-reduced-motion: reduce) {
  .status-card:hover { transform: none; }
}

/* ---------- footer notice ----------
   .site-footer__notice (design-system.css) sets color + font-size on the
   container only; these two bare <p> tags still need their own rhythm. */

.site-footer__notice p { margin: 0 0 var(--space-2); }
.site-footer__notice p:last-child { margin-bottom: 0; }

/* ---------- source-link badge ----------
   A .badge rendered as an <a>, for linking out to the project's real
   GitHub repo. Distinct from the plain informational .badge spans: it
   gets a hover/focus state so it reads as interactive. */
.badge--link {
  align-self: flex-start;
  color: var(--text);
  text-decoration: none;
  transition: border-color 0.15s ease, color 0.15s ease, background 0.15s ease;
}
.badge--link:hover,
.badge--link:focus-visible {
  border-color: var(--accent);
  color: var(--accent-strong);
  background: var(--accent-soft);
}
.badge--link svg { flex: none; }

/* ---------- cursor-flow trail: --cursor-core ----------
   Component-scoped alias, not a new identity hue: at rest this would
   just be var(--tint-highlight), the suite's bright off-white core
   color, but that token is dark-mode-effective only (see its own
   comment above) and reads as nearly invisible against light mode's
   near-white --bg. --accent-strong is what that comment names as the
   correct light-mode fallback for exactly this "bright core point of a
   glow" use, so this alias makes that swap once, here, rather than
   repeating the theme check everywhere .cursor-flow's gradient below
   needs it. Same four-block theme shape as every other token in this
   file. */
:root { --cursor-core: var(--tint-highlight); }

@media (prefers-color-scheme: light) {
  :root:where(:not([data-theme])) { --cursor-core: var(--accent-strong); }
}

:root[data-theme="dark"] { --cursor-core: var(--tint-highlight); }
:root[data-theme="light"] { --cursor-core: var(--accent-strong); }

/* ---------- cursor flow (pointer-follow trailing light) ----------
   A short comet tail of glow samples trailing the pointer, standing in
   for this project's own "an open, still hand becomes a pointer" idea.
   Used to be a single dot that eased toward the pointer and toggled
   opacity 0/1; now a fixed pool of POOL_SIZE small elements (sized in
   the script below, alongside the full DOM-pool-vs-canvas reasoning
   for why this stayed plain pooled divs), laid down along the
   pointer's recent path and individually faded out over a 700ms
   lifetime each. A moving pointer keeps laying fresh samples faster
   than old ones expire, so it reads as a continuous fading tail; a
   stopped pointer just lets its last few samples finish that same
   700ms fade on their own, no separate "idle" state needed anywhere in
   this file.

   Every sample shares this one gradient, and opacity/transform are
   written directly per frame from the script as a real numeric fade
   curve, not a two-state toggle — so unlike the old single dot there's
   no CSS transition on opacity here. A transition would be racing
   values already being interpolated every frame in JS instead of
   helping them.

   Colored core-to-edge (bright --cursor-core center, through magenta,
   into wine) rather than a flat tint, echoing the hero-flow wash's
   three-hue treatment elsewhere on this page. Same
   color-mix(...,55%,transparent) stop treatment and the same .55/.42
   dark/light element-opacity split as .hero-flow too, and for the same
   underlying reason: several of these can overlap near the pointer
   (most visibly when it moves slowly — see MIN_DISTANCE_PX in the
   script for the other half of that fix), so the same restraint that
   keeps hero-flow's wash from printing as a stain on a light
   background keeps a cluster of overlapping trail samples from
   composing into a solid ink blob there either.

   Fixed, inert (pointer-events: none on the container and every pooled
   dot), and purely decorative — never intercepts clicks, drags, or
   scrolling. Every gate from the single-dot version is preserved
   exactly in the script: prefers-reduced-motion checked before any
   requestAnimationFrame loop starts, fine-pointer/hover-capable input
   required before starting at all, and nothing is ever positioned or
   shown before a real first pointer position is known. The (hover:
   none), (pointer: coarse) rule below is a CSS-only backstop for the
   touch case, so nothing can get stuck rendered at a stale last-tap
   position; a window blur is handled explicitly in the script instead
   of relying on this CSS (an aged-out sample clears within 700ms
   regardless, but blur forces every pooled sample to read as
   already-expired immediately, so nothing lingers mid-fade at a stale
   position while the window is out of focus). */
.cursor-flow {
  position: fixed;
  inset: 0;
  pointer-events: none;
  z-index: 80;
  opacity: .55;
}

@media (prefers-color-scheme: light) {
  :root:where(:not([data-theme])) .cursor-flow { opacity: .42; }
}
:root[data-theme="light"] .cursor-flow { opacity: .42; }
:root[data-theme="dark"] .cursor-flow { opacity: .55; }

.cursor-flow__dot {
  position: absolute;
  top: 0;
  left: 0;
  width: 32px;
  height: 32px;
  margin: -16px 0 0 -16px;
  border-radius: 50%;
  background: radial-gradient(circle,
    color-mix(in srgb, var(--cursor-core) 55%, transparent) 0%,
    color-mix(in srgb, var(--accent2) 55%, transparent) 38%,
    color-mix(in srgb, var(--accent3) 55%, transparent) 68%,
    transparent 80%);
  opacity: 0;
  pointer-events: none;
}

@media (hover: none), (pointer: coarse) {
  .cursor-flow { display: none; }
}
