/* pulse.css - the .swarm layer where caught-bot GLITCHES flicker in the wheat.
   Shared by index.html (live) and pulse-preview.html (offline mock). Purely
   presentational; entrance/exit motion is driven by pulse.js via the Web
   Animations API. The per-glitch tearing below is CSS, gated to nothing under
   prefers-reduced-motion so a still, split fragment remains. */

.swarm {
  position: absolute;
  inset: 0;
  z-index: 3;               /* glitches sit in the field, behind Todd (z:4) */
  pointer-events: none;     /* never intercept clicks meant for the page */
  overflow: hidden;
}

/* --activity (0..1, set on :root by scene.js) is how busy the trap is now. */

/* Honesty marker: shown ONLY while the glitches are simulated, so demo traffic
   can never be mistaken for real caught bots. Removed automatically the moment
   /pulse.json answers. */
.demo-note {
  position: fixed;
  left: 50%;
  bottom: 10px;
  transform: translateX(-50%);
  z-index: 8;
  padding: 4px 10px;
  border: 1px solid rgba(232, 219, 190, 0.22);
  border-radius: 999px;
  background: rgba(5, 3, 10, 0.55);
  color: #e8dbbe;
  opacity: 0.62;
  font: 500 10px/1.4 ui-monospace, "SF Mono", Consolas, monospace;
  letter-spacing: 0.06em;
  text-transform: lowercase;
  pointer-events: none;
  white-space: nowrap;
}

/* accumulating wear where catches land - a canvas heat-map, low in the wheat */
.scars {
  position: absolute;
  inset: 0;
  z-index: 1;               /* over the field, under everything else */
  width: 100%;
  height: 100%;
  pointer-events: none;
}

/* a charged haze that thickens with activity: magenta above, cyan fog below */
.weather {
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  mix-blend-mode: screen;
  opacity: 0.06;            /* base; scene.js drives it from live activity */
  background:
    radial-gradient(120% 68% at 50% 6%, rgba(255, 43, 209, 0.26), transparent 60%),
    radial-gradient(120% 55% at 50% 100%, rgba(58, 217, 240, 0.2), transparent 62%);
}

/* one caught bot. left + bottom are set inline by pulse.js so glitches scatter
   across the lower field; --accent + --amp are set by glitch.js from the catch. */
.glitch-mote {
  position: absolute;
  bottom: 6vh;
  transform: translateX(-50%);
  --accent: #3ad9f0;
  --amp: 1;
  color: var(--accent);
  font: 700 clamp(11px, 1.5vw, 17px)/1 ui-monospace, "SF Mono", Consolas, monospace;
  letter-spacing: 0.08em;
  white-space: nowrap;
  will-change: transform, opacity;
  text-transform: uppercase;
}

/* the identity fragment: base text glows in the accent; the two pseudo-layers
   are the red/cyan channel split, each torn by an offset scanline clip. */
.g-tag {
  position: relative;
  display: inline-block;
  text-shadow: 0 0 6px var(--accent), 0 0 14px color-mix(in srgb, var(--accent) 55%, transparent);
  animation: g-jit calc(0.6s / var(--amp)) steps(2, end) infinite;
}
.g-tag::before,
.g-tag::after {
  content: attr(data-text);
  position: absolute;
  top: 0;
  left: 0;
  pointer-events: none;
  opacity: 0.85;
}
.g-tag::before {
  color: #ff2b6b;                                    /* red channel, torn left */
  transform: translateX(calc(-1px * var(--amp)));
  animation: g-tear-a calc(0.9s / var(--amp)) steps(3, end) infinite;
}
.g-tag::after {
  color: #2be0ff;                                    /* cyan channel, torn right */
  transform: translateX(calc(1px * var(--amp)));
  animation: g-tear-b calc(1.1s / var(--amp)) steps(4, end) infinite;
}

/* a scan bar under the fragment, sweeping in the accent */
.g-bar {
  display: block;
  height: 2px;
  margin-top: 3px;
  background: var(--accent);
  box-shadow: 0 0 8px var(--accent);
  opacity: 0.8;
  transform-origin: left center;
  animation: g-bar 1.3s steps(6, end) infinite;
}

/* a forged bot tears harder and faster; a misbehaving one strobes its bar */
.glitch-mote.forged .g-tag { animation-duration: 0.3s; }
.glitch-mote.bad .g-bar { animation-duration: 0.55s; }

@keyframes g-jit {
  0%, 100% { transform: translate(0, 0); }
  50% { transform: translate(calc(1px * var(--amp)), calc(-1px * var(--amp))); }
}
@keyframes g-tear-a {
  0%   { clip-path: inset(0 0 72% 0); }
  33%  { clip-path: inset(42% 0 20% 0); }
  66%  { clip-path: inset(82% 0 0 0); }
  100% { clip-path: inset(12% 0 58% 0); }
}
@keyframes g-tear-b {
  0%   { clip-path: inset(60% 0 0 0); }
  50%  { clip-path: inset(0 0 54% 0); }
  100% { clip-path: inset(30% 0 28% 0); }
}
@keyframes g-bar {
  0%, 100% { transform: scaleX(0.2); opacity: 0.5; }
  50% { transform: scaleX(1); opacity: 0.9; }
}

/* reduced motion: no tearing, no strobe. pulse.js adds .shown to cross-fade the
   mote in; the channel split stays as a static frozen artifact. */
@media (prefers-reduced-motion: reduce) {
  .glitch-mote { opacity: 0; transition: opacity 0.4s ease; }
  .glitch-mote.shown { opacity: 1; }
  .g-tag, .g-tag::before, .g-tag::after, .g-bar { animation: none; }
  .g-tag::before { transform: translateX(-1.5px); clip-path: inset(30% 0 40% 0); }
  .g-tag::after { transform: translateX(1.5px); clip-path: inset(45% 0 20% 0); }
}
