/* ============================================================
   CURSOR

   A follower that replaces the pointer over the demo grid and
   labels what a click will do.

   Strictly a desktop enhancement. It is gated on a fine pointer in
   BOTH the CSS and the JS: on touch there is no cursor to replace,
   and hiding the native one on a device that has none would leave
   a stray dot parked wherever the last tap landed.
   ============================================================ */

/* PHYSICAL top/left and margin-left, NOT the logical properties
   used everywhere else in this project.

   The document is RTL, so `inset-inline-start` resolves to `right`
   — but this element is positioned from `clientX`, which is always
   measured from the physical left edge whatever the direction. The
   two disagreed by the width of the viewport: with the pointer at
   x=200 in a 434px window the disc rendered at left=612, i.e. 178px
   off the right-hand side. The native cursor was hidden and its
   replacement was outside the screen, so the pointer simply
   vanished over the grid.

   Anything driven by viewport coordinates has to be positioned in
   viewport coordinates. */
.cursor {
  position: fixed;
  top: 0;
  left: 0;
  z-index: var(--z-cursor);
  pointer-events: none;

  display: grid;
  place-items: center;

  width: var(--cursor-size, 14px);
  height: var(--cursor-size, 14px);
  margin-left: calc(var(--cursor-size, 14px) / -2);
  margin-top:  calc(var(--cursor-size, 14px) / -2);

  border-radius: 50%;
  background-color: var(--c-text);

  opacity: 0;
  /* translate is written every frame by JS; the rest eases. */
  transition:
    width      var(--dur-base) var(--ease-out),
    height     var(--dur-base) var(--ease-out),
    margin     var(--dur-base) var(--ease-out),
    opacity    var(--dur-fast) var(--ease),
    background-color var(--dur-base) var(--ease);
}

.cursor.is-visible {
  opacity: 1;
}

/* Expanded state, over a card. 84px was large enough to cover a
   meaningful part of the screenshot it was supposed to be pointing
   at; 56px still reads as a deliberate swell without becoming the
   thing you look at. */
.cursor.is-over {
  --cursor-size: 56px;
  background-color: rgb(var(--rgb-ink) / 0.14);
  -webkit-backdrop-filter: blur(6px);
  backdrop-filter: blur(6px);
  border: 1px solid rgb(var(--rgb-ink) / 0.35);
}

.cursor__label {
  font-size: var(--fs-xs);
  font-weight: var(--fw-medium);
  color: var(--c-text);
  white-space: nowrap;

  opacity: 0;
  scale: 0.7;
  transition:
    opacity var(--dur-fast) var(--ease),
    scale   var(--dur-base) var(--ease-out);
}

.cursor.is-over .cursor__label {
  opacity: 1;
  scale: 1;
}

/* The arrow form. Same slot in the grid as the label, so whichever
   one is showing sits dead centre of the disc.

   It points LEFT because forward is left in an RTL document — the
   same direction the card's own chevron and hover sheen travel. */
.cursor__arrow {
  grid-area: 1 / 1;
  width: 18px;
  height: 18px;
  color: var(--c-text);

  opacity: 0;
  scale: 0.7;
  transition:
    opacity var(--dur-fast) var(--ease),
    scale   var(--dur-base) var(--ease-out);
}

.cursor.is-over.is-icon .cursor__arrow {
  opacity: 1;
  scale: 1;
}

/* Both children occupy the same cell, so the label needs the cell
   too — otherwise the grid gives them a row each and neither is
   centred. */
.cursor__label {
  grid-area: 1 / 1;
}

/* Hide the real pointer only where the follower stands in for it,
   and only when the follower is actually running. */
@media (hover: hover) and (pointer: fine) {
  .has-cursor .demos__grid,
  .has-cursor .demos__grid a {
    cursor: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .cursor { display: none; }
}
