/* ============================================================
   fx-cursor — the target hologram
   ------------------------------------------------------------
   One fixed, compositor-only layer (the same cost class as
   .vignette), drawn in the holograms' idiom: thin ink lines,
   a dashed ring, threat red on threats. Centre outwards:
     .cur-dot   4px ink dot — the exact pointer position
     .cur-echo  ghost ring on a slower chase (--ex/--ey), ~.2,
                breathing
     .cur-ring  host for the lag + state scale. ::before is the
                dashed ring and spins on its own layer, so the
                rotation never touches the host's lag transform;
                ::after is the cinema sweep
     .cur-tick  N/E/S/W crosshair ticks — four gradients on one
                element, the .cur-bkt idiom
     .cur-bkt   corner brackets — the drag state
   No mix-blend-mode — see the .grain comment in index.html:
   blending a fixed overlay against the live WebGL canvas would
   re-blend the whole frame every frame.

   The element is only created by fx-cursor.js on fine-pointer,
   hover-capable, non-reduced-motion devices, so none of these
   rules match on touch / reduced-motion / keyboard-only paths.

   z-index 70: above .grain (60), below the loader (100) and the
   top layer (dialogs), where a fixed element cannot follow —
   the JS hands back the native cursor there.

   --ease-out-expo is minted by fx-grammar.css, which loads
   before this sheet; the fallback to :root's identical
   --ease-out keeps the sheet self-sufficient either way.
   ============================================================ */

.cur{
  position:fixed;left:0;top:0;z-index:70;pointer-events:none;
  opacity:0;transition:opacity .3s;will-change:transform;
  --tk:var(--ink);                /* tick colour: gradients cannot transition, so threat snaps here */
  --ky:rgba(16,16,16,.55);        /* dark keylines — legibility over light grounds */
  --ky2:rgba(16,16,16,.32);
}
html.cur-on .cur{opacity:1}
/* Overrides .holo__canvas{cursor:grab}, .dgm__hot{cursor:help}, .burger, buttons… */
html.cur-on *{cursor:none!important}

/* ---- dot: rides the pointer exactly ---- */
.cur-dot{
  position:absolute;left:-2px;top:-2px;width:4px;height:4px;border-radius:50%;
  background:var(--ink);
  transition:transform .2s var(--ease-out-expo, var(--ease-out)),background-color .25s,box-shadow .25s;
}

/* ---- lagging parts: --lx/--ly (ring, ticks, brackets) and --ex/--ey (echo), written by JS ---- */
.cur-ring,.cur-echo,.cur-tick,.cur-bkt{position:absolute;will-change:transform}
.cur-ring,.cur-echo{left:-20px;top:-20px;width:40px;height:40px;border-radius:50%}
.cur-tick{left:-32px;top:-32px;width:64px;height:64px}
.cur-bkt{left:-22px;top:-22px;width:44px;height:44px}

/* The lag offset and the state scale live in ONE transform, so the per-frame
   --lx/--ly write and the .35s scale transition compose instead of fighting. */
.cur-ring{
  border:1px solid transparent;     /* the solid ring — link / hot fade it in over the dashes */
  transform:translate3d(var(--lx,0px),var(--ly,0px),0) scale(1);
  transition:
    border-color .25s,
    border-radius .25s,
    background-color .25s,
    box-shadow .25s,
    opacity .2s,
    transform .35s var(--ease-out-expo, var(--ease-out));
}
/* Dashed ring: a solid 1px ring cut into 16 dashes by a conic mask — even dashes that stay
   crisp at 1x and 2x, where border-style:dashed refits its dash length per corner. Three
   inset spreads (keyline, ink, keyline) rather than a border, so the outer keyline stays
   inside the box the mask clips to. */
.cur-ring::before{
  content:"";position:absolute;inset:-2px;border-radius:50%;
  box-shadow:inset 0 0 0 1px var(--ky2),inset 0 0 0 2px var(--ink),inset 0 0 0 3px var(--ky2);
  -webkit-mask-image:repeating-conic-gradient(#000 0 14deg,transparent 14deg 22.5deg);
  mask-image:repeating-conic-gradient(#000 0 14deg,transparent 14deg 22.5deg);
  animation:cur-spin 18s linear infinite;
  transition:opacity .25s,box-shadow .25s;
}
@keyframes cur-spin{to{transform:rotate(360deg)}}

/* Echo: the hologram ghost — a solid ring on the slower chase. The breath lives on its
   own pseudo so the host's state opacity and the keyframe never fight. */
.cur-echo{
  transform:translate3d(var(--ex,0px),var(--ey,0px),0) scale(1);
  transition:opacity .25s,transform .35s var(--ease-out-expo, var(--ease-out));
}
.cur-echo::before{
  content:"";position:absolute;inset:0;border-radius:50%;
  border:1px solid var(--ink);opacity:.2;
  animation:cur-breath 3s ease-in-out infinite;
  transition:border-color .25s;
}
@keyframes cur-breath{0%,100%{opacity:.14}50%{opacity:.24}}

/* Ticks: 8×1px at N/E/S/W, 3px clear of the ring (r 23–31). Each is a 3px gradient — 1px
   ink core, dark edges — since a gradient cannot carry the keyline box-shadow. */
.cur-tick{
  transform:translate3d(var(--lx,0px),var(--ly,0px),0) scale(1);
  transition:opacity .2s,transform .3s var(--ease-out-expo, var(--ease-out));
  background-image:
    linear-gradient(90deg,var(--ky) 0 1px,var(--tk) 1px 2px,var(--ky) 2px 3px),
    linear-gradient(90deg,var(--ky) 0 1px,var(--tk) 1px 2px,var(--ky) 2px 3px),
    linear-gradient(180deg,var(--ky) 0 1px,var(--tk) 1px 2px,var(--ky) 2px 3px),
    linear-gradient(180deg,var(--ky) 0 1px,var(--tk) 1px 2px,var(--ky) 2px 3px);
  background-size:3px 8px,3px 8px,8px 3px,8px 3px;
  background-position:31px 1px,31px 55px,1px 31px,55px 31px;   /* N S W E */
  background-repeat:no-repeat;
}

/* Eight 7px×1px ticks — the .hud .corner idiom — hidden until the drag state. */
.cur-bkt{
  opacity:0;
  transform:translate3d(var(--lx,0px),var(--ly,0px),0);
  transition:opacity .2s,transform .35s var(--ease-out-expo, var(--ease-out));
  background-image:
    linear-gradient(var(--ink),var(--ink)),
    linear-gradient(var(--ink),var(--ink)),
    linear-gradient(var(--ink),var(--ink)),
    linear-gradient(var(--ink),var(--ink)),
    linear-gradient(var(--ink),var(--ink)),
    linear-gradient(var(--ink),var(--ink)),
    linear-gradient(var(--ink),var(--ink)),
    linear-gradient(var(--ink),var(--ink));
  background-position:
    0 0, 0 0,
    100% 0, 100% 0,
    0 100%, 0 100%,
    100% 100%, 100% 100%;
  background-size:
    7px 1px, 1px 7px,
    7px 1px, 1px 7px,
    7px 1px, 1px 7px,
    7px 1px, 1px 7px;
  background-repeat:no-repeat;
}

/* ---- states (data-s written by JS from one delegated pointerover) ---- */
/* link / hot: lock-on — the dashes give way to a solid ring, the ticks close on it */
.cur[data-s="link"] .cur-ring,.cur[data-s="hot"] .cur-ring{
  border-color:var(--ink);
  box-shadow:0 0 0 1px var(--ky2),inset 0 0 0 1px var(--ky2);
}
.cur[data-s="link"] .cur-ring::before,.cur[data-s="hot"] .cur-ring::before{opacity:0;animation-play-state:paused}
.cur[data-s="link"] .cur-ring{transform:translate3d(var(--lx,0px),var(--ly,0px),0) scale(.6)}
.cur[data-s="link"] .cur-echo{transform:translate3d(var(--ex,0px),var(--ey,0px),0) scale(.6)}
.cur[data-s="link"] .cur-tick{transform:translate3d(var(--lx,0px),var(--ly,0px),0) scale(.55)}
.cur[data-s="hot"] .cur-ring{transform:translate3d(var(--lx,0px),var(--ly,0px),0) scale(.5)}
.cur[data-s="hot"] .cur-echo{transform:translate3d(var(--ex,0px),var(--ey,0px),0) scale(.5)}
.cur[data-s="hot"] .cur-tick{transform:translate3d(var(--lx,0px),var(--ly,0px),0) scale(.46)}
.cur[data-s="hot"] .cur-dot{transform:scale(.66)}

/* I-beam: the ring host collapses to a 2×18px bar. A border alone would vanish at scale(.1)
   (the side borders become .1px), so the bar is filled in ink; the two 10px inset shadows
   scale to 1px dark edges — its keyline for light grounds. Ticks and echo go. */
.cur[data-s="text"] .cur-ring{
  transform:translate3d(var(--lx,0px),var(--ly,0px),0) scale(.1,.45);
  border-radius:1px;border-color:var(--ink);background-color:var(--ink);
  box-shadow:inset 10px 0 0 0 var(--ky),inset -10px 0 0 0 var(--ky);
}
.cur[data-s="text"] .cur-ring::before{opacity:0;animation-play-state:paused}
.cur[data-s="text"] .cur-tick,.cur[data-s="text"] .cur-echo{opacity:0}

/* drag (hologram canvas): the reticle gives way to the corner brackets */
.cur[data-s="drag"] .cur-ring,.cur[data-s="drag"] .cur-tick,.cur[data-s="drag"] .cur-echo{opacity:0}
.cur[data-s="drag"] .cur-bkt{opacity:1}
.cur.grab .cur-bkt{
  transform:translate3d(var(--lx,0px),var(--ly,0px),0) scale(.8);
}

/* cinema: the loader's radar sweep inside the dashed ring (keyframe radarspin lives in index.html) */
.cur[data-s="cinema"] .cur-ring::after{
  content:"";position:absolute;inset:-1px;border-radius:50%;
  background:conic-gradient(from 0deg,rgba(232,235,233,.35),transparent 70deg);
  animation:radarspin 2.4s linear infinite;
}

/* threat: the one accent — pins/nodes flagged as threat or compromised */
.cur[data-t="1"]{--tk:var(--threat)}
.cur[data-t="1"] .cur-dot{background:var(--threat)}
.cur[data-t="1"] .cur-ring{border-color:var(--threat)}
.cur[data-t="1"] .cur-ring::before{box-shadow:inset 0 0 0 1px var(--ky2),inset 0 0 0 2px var(--threat),inset 0 0 0 3px var(--ky2)}
.cur[data-t="1"] .cur-echo::before{border-color:var(--threat)}
.cur[data-t="1"][data-s="text"] .cur-ring{background-color:var(--threat)}

/* pressed: after the hot rule so it wins at equal specificity */
.cur[data-down] .cur-dot{transform:scale(1.6)}

/* Belt and braces: the module never instantiates under html.rm (GH.reduced), and the
   global html.rm rule already flattens every transition to .001s; pin the loops off too —
   flattened, cur-spin would strobe. */
/* reduced motion (html.rm) */html.rm .cur-ring::before,html.rm .cur-echo::before,html.rm .cur[data-s="cinema"] .cur-ring::after{animation:none}
@media print{
  .cur{display:none}
}

/* ---- legibility over light grounds (the white pill, the ink wipe) ----
   A dark keyline on every part: the reticle must read on #fff and #e8ebe9 as well as #101010.
   The dashes, the solid ring, the ticks and the I-beam carry theirs inline above (--ky/--ky2). */
.cur-dot{box-shadow:0 0 0 1px var(--ky)}
.cur[data-s="link"] .cur-dot{background:#101010;box-shadow:0 0 0 1px var(--ink)}
