/* =============================================================
   Juoni Games — ship.css
   Background ship that chases the mouse. NO JavaScript.

   How it works
   ------------
   1. A fixed, invisible 32×18 hover grid (.hit) covers the viewport.
      Hovering a cell also hovers its column, so `body:has(... :hover)`
      rules read the column index into --tx and the row index into --ty.
      That is 32 + 18 = 50 small rules, not 576.
   2. The ship's position --sx/--sy are *registered* custom properties
      (@property) that transition toward --tx/--ty. Every mouse step
      retargets the transition from the ship's current position, so it
      flies a straight line and re-aims mid-flight.
   3. The heading is computed every frame with atan2() from the vector
      "current position -> aim point", so the nose leads. A slow loiter
      orbit (--t) is blended in so the ship circles the cursor nose-first
      when the mouse stops, instead of parking with an undefined heading.
   4. The exhaust flame length scales with the remaining distance.

   Requires: @property, custom-property transitions, :has(), and the CSS
   trig functions (Chrome 111+, Firefox 128+, Safari 16.4+). Older
   browsers show a static ship at the home position.

   Changing the grid size: edit GRID_COLS / GRID_ROWS below AND the
   .hit markup in every page (README.md has the block).
   ============================================================= */

/* ---- Tunables ---- */
.ship {
  --ship-w: 30px;           /* sprite width  */
  --ship-h: 36px;           /* sprite height */
  --flight: 2.6s;           /* time of one flight toward the cursor */
  --ease: cubic-bezier(.1, .7, .2, 1); /* fast burst, long brake */
  --aim-lag: .35s;          /* smoothing of the aim point between cells */
  --orbit-r: 44px;          /* loiter radius around the cursor */
  --orbit-k: 60px;          /* how strongly the loiter tangent steers the nose */
  --orbit-period: 7s;       /* one lap of the loiter orbit */
  --thrust-min: 10px;       /* idle flame */
  --thrust-max: 46px;       /* full-burn flame */
}

/* ---- Registered properties (so they can transition / animate) ---- */
@property --tx { syntax: '<length>'; inherits: true;  initial-value: 0px; }
@property --ty { syntax: '<length>'; inherits: true;  initial-value: 0px; }
@property --ax { syntax: '<length>'; inherits: false; initial-value: 0px; }
@property --ay { syntax: '<length>'; inherits: false; initial-value: 0px; }
@property --sx { syntax: '<length>'; inherits: false; initial-value: 0px; }
@property --sy { syntax: '<length>'; inherits: false; initial-value: 0px; }
@property --t  { syntax: '<angle>';  inherits: false; initial-value: 0deg; }

/* Home position: where the ship goes when the mouse leaves the window
   or rests on a link. */
body {
  --tx: 50vw;
  --ty: 58vh;
}

/* ---- 1. Hover grid ---- */
.hit {
  position: fixed;
  inset: 0;
  z-index: 0;
  display: grid;
  grid-template-columns: repeat(32, 1fr);   /* GRID_COLS */
}
.hit > div {
  display: grid;
  grid-template-rows: repeat(18, 1fr);      /* GRID_ROWS */
}
.hit i { display: block; }

/* Column k of 32 -> --tx = center of that column (generated) */
body:has(.hit > :nth-child(1):hover) { --tx: calc(100vw * 0.5 / 32); }
body:has(.hit > :nth-child(2):hover) { --tx: calc(100vw * 1.5 / 32); }
body:has(.hit > :nth-child(3):hover) { --tx: calc(100vw * 2.5 / 32); }
body:has(.hit > :nth-child(4):hover) { --tx: calc(100vw * 3.5 / 32); }
body:has(.hit > :nth-child(5):hover) { --tx: calc(100vw * 4.5 / 32); }
body:has(.hit > :nth-child(6):hover) { --tx: calc(100vw * 5.5 / 32); }
body:has(.hit > :nth-child(7):hover) { --tx: calc(100vw * 6.5 / 32); }
body:has(.hit > :nth-child(8):hover) { --tx: calc(100vw * 7.5 / 32); }
body:has(.hit > :nth-child(9):hover) { --tx: calc(100vw * 8.5 / 32); }
body:has(.hit > :nth-child(10):hover) { --tx: calc(100vw * 9.5 / 32); }
body:has(.hit > :nth-child(11):hover) { --tx: calc(100vw * 10.5 / 32); }
body:has(.hit > :nth-child(12):hover) { --tx: calc(100vw * 11.5 / 32); }
body:has(.hit > :nth-child(13):hover) { --tx: calc(100vw * 12.5 / 32); }
body:has(.hit > :nth-child(14):hover) { --tx: calc(100vw * 13.5 / 32); }
body:has(.hit > :nth-child(15):hover) { --tx: calc(100vw * 14.5 / 32); }
body:has(.hit > :nth-child(16):hover) { --tx: calc(100vw * 15.5 / 32); }
body:has(.hit > :nth-child(17):hover) { --tx: calc(100vw * 16.5 / 32); }
body:has(.hit > :nth-child(18):hover) { --tx: calc(100vw * 17.5 / 32); }
body:has(.hit > :nth-child(19):hover) { --tx: calc(100vw * 18.5 / 32); }
body:has(.hit > :nth-child(20):hover) { --tx: calc(100vw * 19.5 / 32); }
body:has(.hit > :nth-child(21):hover) { --tx: calc(100vw * 20.5 / 32); }
body:has(.hit > :nth-child(22):hover) { --tx: calc(100vw * 21.5 / 32); }
body:has(.hit > :nth-child(23):hover) { --tx: calc(100vw * 22.5 / 32); }
body:has(.hit > :nth-child(24):hover) { --tx: calc(100vw * 23.5 / 32); }
body:has(.hit > :nth-child(25):hover) { --tx: calc(100vw * 24.5 / 32); }
body:has(.hit > :nth-child(26):hover) { --tx: calc(100vw * 25.5 / 32); }
body:has(.hit > :nth-child(27):hover) { --tx: calc(100vw * 26.5 / 32); }
body:has(.hit > :nth-child(28):hover) { --tx: calc(100vw * 27.5 / 32); }
body:has(.hit > :nth-child(29):hover) { --tx: calc(100vw * 28.5 / 32); }
body:has(.hit > :nth-child(30):hover) { --tx: calc(100vw * 29.5 / 32); }
body:has(.hit > :nth-child(31):hover) { --tx: calc(100vw * 30.5 / 32); }
body:has(.hit > :nth-child(32):hover) { --tx: calc(100vw * 31.5 / 32); }

/* Row j of 18 -> --ty = center of that row (generated) */
body:has(.hit > * > :nth-child(1):hover) { --ty: calc(100vh * 0.5 / 18); }
body:has(.hit > * > :nth-child(2):hover) { --ty: calc(100vh * 1.5 / 18); }
body:has(.hit > * > :nth-child(3):hover) { --ty: calc(100vh * 2.5 / 18); }
body:has(.hit > * > :nth-child(4):hover) { --ty: calc(100vh * 3.5 / 18); }
body:has(.hit > * > :nth-child(5):hover) { --ty: calc(100vh * 4.5 / 18); }
body:has(.hit > * > :nth-child(6):hover) { --ty: calc(100vh * 5.5 / 18); }
body:has(.hit > * > :nth-child(7):hover) { --ty: calc(100vh * 6.5 / 18); }
body:has(.hit > * > :nth-child(8):hover) { --ty: calc(100vh * 7.5 / 18); }
body:has(.hit > * > :nth-child(9):hover) { --ty: calc(100vh * 8.5 / 18); }
body:has(.hit > * > :nth-child(10):hover) { --ty: calc(100vh * 9.5 / 18); }
body:has(.hit > * > :nth-child(11):hover) { --ty: calc(100vh * 10.5 / 18); }
body:has(.hit > * > :nth-child(12):hover) { --ty: calc(100vh * 11.5 / 18); }
body:has(.hit > * > :nth-child(13):hover) { --ty: calc(100vh * 12.5 / 18); }
body:has(.hit > * > :nth-child(14):hover) { --ty: calc(100vh * 13.5 / 18); }
body:has(.hit > * > :nth-child(15):hover) { --ty: calc(100vh * 14.5 / 18); }
body:has(.hit > * > :nth-child(16):hover) { --ty: calc(100vh * 15.5 / 18); }
body:has(.hit > * > :nth-child(17):hover) { --ty: calc(100vh * 16.5 / 18); }
body:has(.hit > * > :nth-child(18):hover) { --ty: calc(100vh * 17.5 / 18); }

/* ---- 2. Ship layer + flight ---- */
.ship {
  position: fixed;
  inset: 0;
  z-index: 1;
  overflow: hidden;
  pointer-events: none;
}

/* The craft itself: a 0×0 point that moves; the sprite hangs off it. */
.ship > i {
  position: absolute;
  left: 0;
  top: 0;
  width: 0;
  height: 0;

  /* aim point (smoothed between grid cells) and position (the flight) */
  --ax: var(--tx);
  --ay: var(--ty);
  --sx: var(--tx);
  --sy: var(--ty);
  transition:
    --ax var(--aim-lag) ease-out,
    --ay var(--aim-lag) ease-out,
    --sx var(--flight) var(--ease),
    --sy var(--flight) var(--ease);

  /* seek vector: current position -> aim point */
  --dx: calc(var(--ax) - var(--sx));
  --dy: calc(var(--ay) - var(--sy));
  --dist: hypot(var(--dx), var(--dy));

  /* heading vector = seek + tangent of the loiter orbit */
  --hx: calc(var(--dx) - var(--orbit-k) * sin(var(--t)));
  --hy: calc(var(--dy) + var(--orbit-k) * cos(var(--t)));
  /* sprite is drawn nose-up, so 0deg = up and rotation is clockwise */
  --heading: atan2(var(--hx), calc(-1 * var(--hy)));

  transform:
    translate(
      calc(var(--sx) + var(--orbit-r) * cos(var(--t))),
      calc(var(--sy) + var(--orbit-r) * sin(var(--t)))
    )
    rotate(var(--heading));

  animation: ship-loiter var(--orbit-period) linear infinite;
  filter: drop-shadow(0 0 6px color-mix(in srgb, var(--accent) 55%, transparent));
}

@keyframes ship-loiter {
  from { --t: 0deg; }
  to   { --t: 360deg; }
}

/* ---- 3. Sprite: a V / chevron, vertex (nose) at the top ---- */
.ship > i::before {
  content: "";
  position: absolute;
  z-index: 1;          /* body draws over the exhaust */
  left: calc(var(--ship-w) / -2);
  top: calc(var(--ship-h) / -2);
  width: var(--ship-w);
  height: var(--ship-h);
  background: linear-gradient(160deg,
    var(--accent-strong) 0%,
    var(--accent) 45%,
    color-mix(in srgb, var(--accent) 65%, black) 100%);
  clip-path: polygon(50% 0, 100% 100%, 50% 72%, 0 100%);
}

/* ---- 4. Exhaust: grows with distance to the cursor ---- */
.ship > i::after {
  content: "";
  position: absolute;
  z-index: 0;
  left: -5px;
  top: 8px; /*calc(var(--ship-h) * 0.72 - var(--ship-h) / 2 - 4px);*/
  width: 10px;
  height: clamp(var(--thrust-min), calc(var(--dist) * .06), var(--thrust-max));
  transform-origin: 50% 0;
  background: linear-gradient(to bottom,
    var(--flame-hot) 0%,
    var(--flame) 35%,
    transparent 100%);
  border-radius: 0 0 50% 50% / 0 0 100% 100%;
  animation: ship-flicker .16s ease-in-out infinite alternate;
}

@keyframes ship-flicker {
  from { transform: scaleX(1);   opacity: .95; }
  to   { transform: scaleX(.65); opacity: .7;  }
}

/* ---- Devices ----
   Motion is intentionally NOT gated on prefers-reduced-motion: the ship is
   the point of the page. To honor the setting, wrap the .ship > i
   transition/animation in @media (prefers-reduced-motion: reduce) { ... }. */

/* Touch devices have no hover: the grid is inert and the ship loiters at
   home. A tap on the page still nudges it there (sticky hover). */
