game/ui/gamecursor.razor.scss
/* ══════════════════════════════════════════════════════════════════════════
   THE FUSE — Chain Reaction's cursor

   Drawn from glyphs rather than an image, so it needs no asset, scales with the
   UI and renders identically on every machine. The tip of the arrowhead is the
   hotspot; the .razor offsets the panel so it lands exactly under the real
   pointer, and HotspotX/Y there are measured against the sizes below — move one
   without the other and every click lands slightly off.

   Palette is the game's own: amber on the deep purple-black, teal for UI, and
   the surge orange when you're armed over the board.

   EVERY element here carries pointer-events: none, individually. Not just the
   parents -- every single one. These elements sit directly under the pointer by
   definition, so any one of them left hit-testable intercepts every click in the
   game before a button can see it. Worse, a hit-tested panel makes the UI ask
   for a cursor, which brings the native pointer back no matter what the
   component does. cursor: none is on them too, for the same reason.
   ══════════════════════════════════════════════════════════════════════════ */

.cur-root {
    position: absolute;
    top: 0; left: 0; right: 0; bottom: 0;
    pointer-events: none;
    cursor: none;
    overflow: hidden;

    /* Above every other panel in the game. The IdleHud root sits at 200, the
       ScreenPanels at 100 -- the cursor is the one thing that must never be
       covered by anything. */
    z-index: 100000;
}

.cur {
    position: absolute;
    width: 48px;
    height: 48px;
    pointer-events: none;
    cursor: none;
    align-items: center;
    justify-content: center;
    transition: transform 0.06s ease-out;
}

/* The arrowhead. Rotated -135° so it points up-and-left like a pointer. */
.cur-tip {
    position: absolute;
    left: 0px;
    top: 0px;
    pointer-events: none;
    cursor: none;
    font-size: 34px;
    line-height: 1;
    transform: rotate(-135deg);
    color: #fbbf24;
    text-shadow: 0px 2px 6px rgba(0, 0, 0, 1.0), 0px 0px 5px rgba(0, 0, 0, 0.9);
    transition: color 0.1s, font-size 0.08s;
}

/* A hard point on the exact hotspot, so precision clicking has something honest
   to aim with when the glyph behind it is a wide symbol. */
.cur-dot {
    position: absolute;
    left: 3px;
    top: 2px;
    width: 4px;
    height: 4px;
    pointer-events: none;
    cursor: none;
    background-color: rgba(255, 250, 235, 0.95);
    border-radius: 2px;
}

/* Rings that bloom when the cursor is over something that answers a click. */
.cur-ring {
    position: absolute;
    left: 13px;
    top: 11px;
    width: 28px;
    height: 28px;
    pointer-events: none;
    cursor: none;
    border: 2px solid rgba(94, 234, 212, 0.75);
    border-radius: 16px;
    animation: cur-ring-in 0.14s ease-out;
}

.ring-wide {
    left: 5px;
    top: 3px;
    width: 44px;
    height: 44px;
    border-radius: 24px;
    border-color: rgba(251, 146, 60, 0.45);
    animation: cur-ring-breathe 1.1s ease-in-out infinite alternate;
}

/* The click punch: a ring thrown outward from the hotspot on mouse down. */
.cur-burst {
    position: absolute;
    left: 3px;
    top: 2px;
    width: 6px;
    height: 6px;
    pointer-events: none;
    cursor: none;
    border: 2px solid rgba(251, 191, 36, 0.9);
    border-radius: 24px;
    animation: cur-burst 0.32s ease-out;
}

/* ── The fuse ──────────────────────────────────────────────────────────────
   Two embers chasing the pointer at different rates, positioned by the .razor.
   They stretch into a line when you sweep and collapse to a point when you
   stop -- the whole reason the cursor reads as a lit fuse rather than an arrow.
   ────────────────────────────────────────────────────────────────────────── */

.cur-spark {
    position: absolute;
    pointer-events: none;
    cursor: none;
    border-radius: 8px;
}

.s1 {
    width: 6px;
    height: 6px;
    background-color: rgba(251, 191, 36, 0.75);
    box-shadow: 0px 0px 8px 2px rgba(251, 146, 60, 0.45);
}

.s2 {
    width: 4px;
    height: 4px;
    background-color: rgba(94, 234, 212, 0.45);
    box-shadow: 0px 0px 7px 2px rgba(20, 184, 166, 0.30);
}

/* ── States ────────────────────────────────────────────────────────────────
   Amber to point, teal over anything clickable, surge orange when you're armed
   over the board, red when the board would refuse the click. The cursor tells
   you what a click does before you commit to it -- cheaper to read than a
   tooltip and it survives being seen out of the corner of your eye.
   ────────────────────────────────────────────────────────────────────────── */

.cur-ui {
    .cur-tip {
        color: #5eead4;
        text-shadow: 0px 0px 10px rgba(45, 212, 191, 0.65), 0px 2px 5px rgba(0, 0, 0, 1.0);
    }
}

.cur-bomb {
    /* Not rotated, unlike the arrowhead: the -135° that makes an arrow point
       up-left would lay the bomb glyphs on their side. Nudged down-right so the
       symbol sits beside the hotspot instead of covering it -- the dot is still
       what you aim with. */
    .cur-tip {
        transform: rotate(0deg);
        font-size: 26px;
        left: 7px;
        top: 5px;
        color: #fb923c;
        text-shadow: 0px 0px 12px rgba(251, 146, 60, 0.7), 0px 2px 5px rgba(0, 0, 0, 1.0);
    }

    .cur-dot { background-color: rgba(255, 236, 210, 0.95); }
    .cur-ring { border-color: rgba(251, 146, 60, 0.8); }
}

.cur-blocked {
    .cur-tip {
        transform: rotate(0deg);
        font-size: 24px;
        left: 7px;
        top: 5px;
        color: #ef4444;
        text-shadow: 0px 0px 10px rgba(239, 68, 68, 0.6), 0px 2px 5px rgba(0, 0, 0, 1.0);
    }

    .cur-dot { background-color: rgba(255, 210, 210, 0.95); }
}

/* Mouse down: the whole cursor dips, the way a physical button does. */
.pressed {
    transform: scale(0.86);
}

@keyframes cur-ring-in {
    0%   { opacity: 0; transform: scale(0.6); }
    100% { opacity: 1; transform: scale(1.0); }
}

@keyframes cur-ring-breathe {
    0%   { opacity: 0.35; transform: scale(0.94); }
    100% { opacity: 0.85; transform: scale(1.06); }
}

@keyframes cur-burst {
    0%   { opacity: 0.9; transform: scale(1.0);  }
    100% { opacity: 0.0; transform: scale(7.0); }
}

/* ══════════════════════════════════════════════════════════════════════════
   Hides the OS pointer over this panel and over the empty space behind it.

   GameCursor stamps .no-native-cursor on the topmost panel of its own tree.
   `cursor` inherits, so one rule covers the tree; the descendant rule is
   belt-and-braces in case inheritance of `cursor` isn't applied in this build.

   Nothing here affects hit-testing or event dispatch, and that distinction is
   the entire fix: Mouse.Visibility = Hidden changes the input model and kills UI
   clicks, this only changes what gets drawn.

   Every OTHER panel in the project declares cursor: none in its own stylesheet,
   because a hovered panel always outranks Mouse.CursorType -- a panel that
   declares nothing still claims the default pointer, which is exactly how the
   OS cursor comes back the moment you touch a button.
   ══════════════════════════════════════════════════════════════════════════ */

.no-native-cursor {
    cursor: none;
}

.no-native-cursor * {
    cursor: none;
}