/* ===============================================================
   fx.css — presentation for the four effect layers. All of the
   heavy lifting happens in js/fx/*; this file only positions the
   surfaces and states what they are allowed to cost.
   =============================================================== */

/* ---------------------------------------------------------------
   1. Dithered field — one low-resolution canvas stretched over the
   viewport with nearest-neighbour scaling, so a 190x120 buffer
   fills a 4K screen for the price of 23k pixels.

   Deliberately NOT mix-blend-mode: screen. Blending a full-viewport
   layer forces the whole page into a blending group and costs more
   frames than every other effect on this page put together (26 fps
   -> 51 fps measured in software rendering). The ramp carries its
   own alpha over a near-black ground, which lands in the same place
   for free.
   --------------------------------------------------------------- */
.fx-field {
  position: fixed;
  inset: 0;
  z-index: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  image-rendering: pixelated;
  opacity: .62;
  transition: opacity var(--t-slow);
}

/* Everything real sits above the field. */
.topbar, .board, .transport, .dropzone { position: relative; z-index: 1; }
.topbar { z-index: 20; }
.transport { z-index: 30; }

/* ---------------------------------------------------------------
   2. Holo grid + vignette — static, painted once by the compositor,
   zero per-frame cost.
   --------------------------------------------------------------- */
.fx-grid {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(var(--line-soft) 1px, transparent 1px),
    linear-gradient(90deg, var(--line-soft) 1px, transparent 1px);
  background-size: 46px 46px;
  mask-image: radial-gradient(ellipse 90% 70% at 50% 0%, #000 10%, transparent 78%);
  -webkit-mask-image: radial-gradient(ellipse 90% 70% at 50% 0%, #000 10%, transparent 78%);
  opacity: .7;
}

.fx-vignette {
  position: fixed;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background:
    radial-gradient(ellipse 120% 80% at 50% -10%, rgba(86, 224, 214, 0.07), transparent 60%),
    radial-gradient(ellipse 100% 60% at 50% 110%, rgba(255, 166, 43, 0.05), transparent 60%);
}

/* ---------------------------------------------------------------
   3. Flame wrap — a single canvas, parented into whichever card is
   live. Only ever one instance exists.
   --------------------------------------------------------------- */
.fx-flame {
  position: absolute;
  inset: -1px;
  width: calc(100% + 2px);
  height: calc(100% + 2px);
  pointer-events: none;
  image-rendering: pixelated;
  border-radius: var(--r);
  opacity: 0;
  /* Screen blending is right for fire and, unlike the full-viewport
     field, it is confined to one card — measured at no frame cost. */
  mix-blend-mode: screen;
  transition: opacity var(--t-slow) var(--ease);
  /* Behind the card's own content: the fire licks around and under
     the panel instead of washing over the name and the readouts. */
  z-index: 0;
}

.fx-flame.is-on { opacity: 1; }

/* ---------------------------------------------------------------
   4. Decrypting reveal — the glyph noise is plain text, so the only
   styling needed is a tint on characters still resolving.
   --------------------------------------------------------------- */
.is-decrypting {
  color: var(--holo);
  text-shadow: 0 0 6px var(--holo-glow);
}

/* The FX switch: the interface keeps its shape, every animated
   surface stops. Nothing is torn down, so flipping it back is
   instant. */
.fx-off .fx-grid { opacity: .3; }
.fx-off .fx-vignette { opacity: .5; }
.fx-off .clip.is-live .wave__scan { animation: none; opacity: 0; }
.fx-off .caret.is-on { animation: none; }
.fx-off .clip.is-new { animation: none; }

/* Reduced motion: keep the frame, drop everything that moves. */
@media (prefers-reduced-motion: reduce) {
  .fx-field { opacity: .28; }
  .clip.is-live .wave__scan { animation: none; opacity: .4; }
  .caret.is-on { animation: none; }
  .clip.is-new { animation: none; }
}
