/* ---- ambient field + click burst -----------------------------------------
   Carried across from amyleesterling/flywire-neuron-gallery: the page's whole
   ambient layer, a drifting corner-lit gradient, a tech-grid dot lattice, a
   faint hand-drawn circuit board, drifting neurotransmitter particles, and CRT
   scan lines, plus the click burst it fires on every click, a star sparkle
   spray and a sonar ring. ambient.js builds the layers, seeds the particles,
   and wires the click.

   Colours are the flywire page's, mapped onto this repo's tokens where they
   line up, its cyan is this page's --holo-cyan exactly; the magenta and warm
   accents stay as the source's literals. The whole ambient layer sits on
   negative z so the page's own content paints over it. Every moving part is
   off under reduced motion: the field stops drifting, the particles and the
   click burst are never built at all (see ambient.js). */

/* slow drifting gradient field, corners only, meant to be sensed not seen */
.holo-bg {
  position: fixed; inset: 0; z-index: -3; pointer-events: none;
  background:
    radial-gradient(ellipse 35% 25% at 0% 0%,
      rgb(var(--holo-cyan, 126 224 255) / .045) 0%, transparent 70%),
    radial-gradient(ellipse 35% 25% at 100% 100%,
      rgba(255, 126, 224, .028) 0%, transparent 70%);
  background-size: 200% 200%;
  animation: holo-bg-drift 32s ease-in-out infinite;
}
@keyframes holo-bg-drift {
  0%, 100% { background-position: 0% 0%, 100% 100%; filter: hue-rotate(0deg); }
  50%      { background-position: 100% 50%, 0% 50%; filter: hue-rotate(8deg); }
}

/* tech-grid dot lattice, masked to the middle band so it never strips the very
   top or the footer */
.holo-grid {
  position: fixed; inset: 0; z-index: -3; pointer-events: none;
  background-image: radial-gradient(circle,
    rgb(var(--holo-cyan, 126 224 255) / .10) 0.6px, transparent 1px);
  background-size: 28px 28px;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, transparent 12%,
    black 28%, black 72%, transparent 92%);
          mask-image: linear-gradient(to bottom, transparent 0%, transparent 12%,
    black 28%, black 72%, transparent 92%);
}

/* the hand-drawn circuit board, a synaptic chip-board undertone, faded top and
   bottom so it does not compete with the banner or the footer */
.holo-circuit {
  position: fixed; inset: 0; z-index: -3; width: 100%; height: 100%;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, transparent 0%, transparent 12%,
    black 32%, black 75%, transparent 95%);
          mask-image: linear-gradient(to bottom, transparent 0%, transparent 12%,
    black 32%, black 75%, transparent 95%);
}

/* drifting particles: rise the height of the viewport, drift sideways, fade in
   and out */
@keyframes holo-particle {
  0%   { transform: translate3d(0, 105vh, 0) scale(.5); opacity: 0; }
  10%  { opacity: .55; }
  90%  { opacity: .55; }
  100% { transform: translate3d(40px, -15vh, 0) scale(.3); opacity: 0; }
}
.holo-particles {
  position: fixed; inset: 0; z-index: -2; pointer-events: none; overflow: hidden;
}
.holo-particles i {
  position: absolute; width: 2px; height: 2px; border-radius: 50%;
  background: rgb(var(--holo-cyan, 126 224 255));
  box-shadow: 0 0 6px rgb(var(--holo-cyan, 126 224 255)),
              0 0 12px rgb(var(--holo-cyan, 126 224 255) / .4);
  animation: holo-particle linear infinite; will-change: transform, opacity;
}

/* faint CRT scan lines, almost subliminal */
.holo-scanlines {
  position: fixed; inset: 0; z-index: -2; pointer-events: none;
  background-image: repeating-linear-gradient(0deg,
    rgb(var(--holo-cyan, 126 224 255) / .012) 0px,
    rgb(var(--holo-cyan, 126 224 255) / .012) 1px,
    transparent 1px, transparent 3px);
  mix-blend-mode: screen;
}

/* ── click burst ──────────────────────────────────────────────────────────
   five-point stars that radiate out from the click point, each rotating and
   shrinking as it goes — a small confirm spray. The script places each spark at
   the click point and hands it its own --dx/--dy/--rot; the CSS carries the
   rest. */
@keyframes click-spark {
  0%   { transform: translate(-50%,-50%) rotate(var(--rot)) scale(.4); opacity: 0; }
  12%  { opacity: 1; }
  100% { transform: translate(calc(-50% + var(--dx)), calc(-50% + var(--dy)))
                   rotate(calc(var(--rot) + 220deg)) scale(.1); opacity: 0; }
}
.click-spark {
  position: fixed; pointer-events: none; z-index: 9999; width: 10px; height: 10px;
  background: radial-gradient(circle, #fff 0%,
    rgb(var(--holo-cyan, 126 224 255)) 50%, transparent 80%);
  clip-path: polygon(50% 0%, 60% 35%, 100% 35%, 68% 57%, 79% 100%,
    50% 70%, 21% 100%, 32% 57%, 0% 35%, 40% 35%);
  animation: click-spark 750ms cubic-bezier(.16,1,.3,1) forwards;
  filter: drop-shadow(0 0 6px rgb(var(--holo-cyan, 126 224 255)));
}
.click-spark.magenta {
  background: radial-gradient(circle, #fff 0%, rgb(255 126 224) 50%, transparent 80%);
  filter: drop-shadow(0 0 6px rgb(255 126 224));
}
.click-spark.warm {
  background: radial-gradient(circle, #fff 0%,
    rgb(var(--holo-warm, 232 169 58)) 50%, transparent 80%);
  filter: drop-shadow(0 0 6px rgb(var(--holo-warm, 232 169 58)));
}
/* ── mobile tap: HUD reticle lock-on ───────────────────────────────────────
   On a touch device the sparkle spray fires out from under the finger and reads
   as nothing but the ring, so touch taps get their own thing instead: a
   targeting reticle that snaps onto the point. Four corner brackets converge
   inward, a crosshair draws through, a fast radar ping expands once, and a
   monospace readout flickers in beside it, all in the page's own HUD vocabulary
   (the corner ticks, the cyan glow, the SEC 04 / SYNC / 1.00 readouts). The
   whole thing is one anchor div sized 0 at the tap point; every part positions
   itself off that centre. ambient.js builds it and removes it after ~720ms, and
   only on touch, so a mouse still gets the sparkle burst. */
.tap-reticle {
  position: fixed; z-index: 9999; width: 0; height: 0; pointer-events: none;
  transform: translate(-50%, -50%);
}

/* the four corner brackets. Each is an L of two borders whose inner corner
   points at the tap centre; they start thrown outward and snap home. */
.tap-reticle b {
  position: absolute; width: 16px; height: 16px; box-sizing: border-box;
  opacity: 0; animation: tap-corner 620ms cubic-bezier(.16,1,.3,1) forwards;
  filter: drop-shadow(0 0 4px rgb(var(--holo-cyan, 126 224 255) / .7));
}
.tap-reticle b.tl { left: -26px; top: -26px; --dx: -1; --dy: -1;
  border-top: 2px solid rgb(var(--holo-cyan, 126 224 255));
  border-left: 2px solid rgb(var(--holo-cyan, 126 224 255)); }
.tap-reticle b.tr { left: 10px; top: -26px; --dx: 1; --dy: -1;
  border-top: 2px solid rgb(var(--holo-cyan, 126 224 255));
  border-right: 2px solid rgb(var(--holo-cyan, 126 224 255)); }
.tap-reticle b.bl { left: -26px; top: 10px; --dx: -1; --dy: 1;
  border-bottom: 2px solid rgb(var(--holo-cyan, 126 224 255));
  border-left: 2px solid rgb(var(--holo-cyan, 126 224 255)); }
.tap-reticle b.br { left: 10px; top: 10px; --dx: 1; --dy: 1;
  border-bottom: 2px solid rgb(var(--holo-cyan, 126 224 255));
  border-right: 2px solid rgb(var(--holo-cyan, 126 224 255)); }
@keyframes tap-corner {
  0%   { opacity: 0; transform: translate(calc(var(--dx) * 18px), calc(var(--dy) * 18px)) scale(1.15); }
  35%  { opacity: 1; }
  60%  { transform: translate(0, 0) scale(1); }
  78%  { opacity: 1; }
  100% { opacity: 0; transform: translate(0, 0) scale(1); }
}

/* crosshair through the centre — two hairlines that draw out along their axis */
.tap-reticle .cx, .tap-reticle .cy {
  position: absolute; background: rgb(var(--holo-cyan, 126 224 255) / .55);
  box-shadow: 0 0 4px rgb(var(--holo-cyan, 126 224 255) / .6); opacity: 0;
}
.tap-reticle .cx { left: -22px; top: -1px; width: 44px; height: 1.5px;
  animation: tap-cross-x 560ms cubic-bezier(.16,1,.3,1) 40ms forwards; }
.tap-reticle .cy { left: -1px; top: -22px; width: 1.5px; height: 44px;
  animation: tap-cross-y 560ms cubic-bezier(.16,1,.3,1) 40ms forwards; }
@keyframes tap-cross-x {
  0%   { opacity: 0; transform: scaleX(0); }
  45%  { opacity: .9; }
  100% { opacity: 0; transform: scaleX(1); }
}
@keyframes tap-cross-y {
  0%   { opacity: 0; transform: scaleY(0); }
  45%  { opacity: .9; }
  100% { opacity: 0; transform: scaleY(1); }
}

/* one fast radar ping — small and quick so it supports the lock, not the show */
.tap-reticle .ping {
  position: absolute; left: 0; top: 0; width: 64px; height: 64px;
  margin: -32px 0 0 -32px; border-radius: 50%;
  border: 1.5px solid rgb(var(--holo-cyan, 126 224 255) / .8);
  opacity: 0; animation: tap-ping 460ms ease-out forwards;
}
@keyframes tap-ping {
  0%   { opacity: .55; transform: scale(.25); }
  100% { opacity: 0; transform: scale(1); }
}

/* a small monospace readout beside the reticle, flickering like a lock code */
.tap-reticle .rd {
  position: absolute; left: 30px; top: -32px; white-space: nowrap;
  font: 600 8px/1 ui-monospace, "SFMono-Regular", Menlo, monospace;
  letter-spacing: .18em; text-transform: uppercase;
  color: rgb(var(--holo-cyan, 126 224 255) / .9);
  text-shadow: 0 0 6px rgb(var(--holo-cyan, 126 224 255) / .5);
  opacity: 0; animation: tap-readout 640ms steps(1, end) forwards;
}
@keyframes tap-readout {
  0%, 18% { opacity: 0; }
  22%     { opacity: .95; }
  34%     { opacity: .3; }
  44%     { opacity: .95; }
  72%     { opacity: .6; }
  100%    { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .holo-bg { animation: none; }
}
