/* ==========================================================================
   Cartridge Tycoon — screen effects

   Two fixed overlays that sit above everything and never take pointer events:

     #crt         the scanline / bloom / flicker / vignette stack. Its strength
                  is entirely era-driven, so the "screen" the player is looking
                  at modernises along with the studio and has faded to nothing
                  by the modern era.

     #era-glitch  the celebratory transition played when the era levels up.

   THE ONE EXCEPTION TO THE NO-LITERALS RULE
   -----------------------------------------
   The gradients below use literal black and white stops. That is deliberate
   and is not a themeable decision: a scanline is the *absence* of phosphor and
   a vignette is the tube's corners falling off, so both are black regardless
   of palette, and the bloom is white light regardless of era. Every value that
   genuinely differs between eras — how dark, how thick, how fast, whether it
   appears at all — is still a variable from src/config/theme.js.
   ========================================================================== */

/* ------------------------------------------------------------------- CRT */

#crt {
  position: fixed;
  inset: 0;
  z-index: 50;
  pointer-events: none;
  /* Never let the overlay create a scrollbar or intercept a tap. */
  overflow: hidden;
}

#crt > * {
  position: absolute;
  inset: 0;
  /*
    Every layer cross-fades on an era change. This is the effect the brief
    calls for most specifically: the CRT visibly weakens each era, so the
    player perceives their *hardware* improving, not just their UI.
  */
  transition: opacity var(--t-slow) var(--ease);
}

/*
  Scanlines. One dark line every `--crt-scanline-size` pixels — the gap grows
  each era, which reads as a finer, higher-resolution display even before the
  opacity drop is accounted for.
*/
.crt__scanlines {
  opacity: var(--crt-scanline-opacity);
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 1) 0,
    rgba(0, 0, 0, 1) 1px,
    transparent 1px,
    transparent var(--crt-scanline-size)
  );
}

/* The soft glow of an over-driven phosphor tube. */
.crt__bloom {
  opacity: var(--crt-bloom);
  background-image: radial-gradient(
    ellipse at 50% 38%,
    rgba(255, 255, 255, 1) 0%,
    rgba(255, 255, 255, 0) 62%
  );
  mix-blend-mode: screen;
}

/* The corners of the tube falling away. */
.crt__vignette {
  opacity: var(--crt-vignette);
  background-image: radial-gradient(
    ellipse at 50% 50%,
    rgba(0, 0, 0, 0) 42%,
    rgba(0, 0, 0, 1) 125%
  );
}

/*
  Mains-hum flicker. Kept very subtle — `--crt-flicker` peaks at 0.035 — because
  anything stronger stops being atmosphere and starts being a headache.
  `steps(2)` gives a hard on/off rather than a smooth pulse, which is what a
  real tube does.
*/
.crt__flicker {
  opacity: var(--crt-flicker);
  background: rgba(255, 255, 255, 1);
  animation: crt-flicker 120ms steps(2, end) infinite;
}

@keyframes crt-flicker {
  0%   { opacity: var(--crt-flicker); }
  100% { opacity: calc(var(--crt-flicker) * 0.35); }
}

/* --- Switching it off ---------------------------------------------------
   Two independent kill switches, because they are two independent decisions:
   the player turning the effect off in settings, and the OS asking for less
   motion. Either alone must stop the flicker. See src/render/crt.js.          */

html[data-crt='off'] #crt {
  display: none;
}

html[data-crt-flicker='off'] .crt__flicker {
  animation: none;
  opacity: 0;
}

@media (prefers-reduced-motion: reduce) {
  .crt__flicker {
    animation: none;
    opacity: 0;
  }
}

/* -------------------------------------------------------- celebration flash */

/*
  The white flash thrown by `render/fx.js` when a game reviews well or the
  studio retires. It lives here rather than with the other effects in
  components.css for the same reason the scanlines do: it needs a literal
  white, and this is the one file where that exception is documented. Light is
  white in every era.
*/
.screen-flash {
  position: absolute;
  inset: 0;
  background: rgba(255, 255, 255, 1);
  animation: screen-flash 0.5s var(--ease) forwards;
}

@keyframes screen-flash {
  0% { opacity: 0; }
  12% { opacity: 0.5; }
  100% { opacity: 0; }
}

@media (prefers-reduced-motion: reduce) {
  .screen-flash {
    display: none;
  }
}

/* --------------------------------------------------------- era transition */

/*
  Idle state: present in the DOM, costing nothing, invisible. src/render/theme.js
  adds `.is-active` and sets `--glitch-duration` from the incoming era's
  `transitionMs`, so a later era's transition is longer and grander than an
  early one's.
*/
#era-glitch {
  position: fixed;
  inset: 0;
  z-index: 65;
  pointer-events: none;
  opacity: 0;
  overflow: hidden;
}

#era-glitch > * {
  position: absolute;
  inset: 0;
}

#era-glitch.is-active {
  opacity: 1;
}

/*
  Torn horizontal bands — the signature "signal is breaking up" look. They
  stretch as the animation peaks, which is the moment src/render/theme.js swaps
  the era, so the new look is *revealed* by the bands clearing rather than
  watched fading in.
*/
.glitch__bars {
  background-image: repeating-linear-gradient(
    to bottom,
    rgba(0, 0, 0, 0.85) 0,
    rgba(0, 0, 0, 0.85) 3px,
    rgba(255, 255, 255, 0.12) 3px,
    rgba(255, 255, 255, 0.12) 5px,
    transparent 5px,
    transparent 14px
  );
  opacity: 0;
}

/* The white peak. This is the frame the era changes on. */
.glitch__flash {
  background: rgba(255, 255, 255, 1);
  opacity: 0;
}

#era-glitch.is-active .glitch__bars {
  animation: era-glitch-bars var(--glitch-duration, 800ms) steps(8, end) both;
}

#era-glitch.is-active .glitch__flash {
  animation: era-glitch-flash var(--glitch-duration, 800ms) ease-out both;
}

/*
  The shell itself gets a short RGB-split shudder underneath the overlay.
  `:has()` is used so src/render/theme.js only has to toggle one class on one
  element; browsers without it simply skip the shudder and still get the
  glitch bars and the flash.
*/
html:has(#era-glitch.is-active) #shell {
  animation: era-shudder var(--glitch-duration, 800ms) ease-out both;
}

@keyframes era-glitch-bars {
  0%   { opacity: 0;    transform: translateY(0)     scaleY(1); }
  12%  { opacity: 0.9;  transform: translateY(-2%)   scaleY(1.1); }
  30%  { opacity: 0.55; transform: translateY(3%)    scaleY(0.9); }
  45%  { opacity: 1;    transform: translateY(-4%)   scaleY(1.35); }
  62%  { opacity: 0.4;  transform: translateY(2%)    scaleY(1); }
  100% { opacity: 0;    transform: translateY(0)     scaleY(1); }
}

@keyframes era-glitch-flash {
  0%   { opacity: 0; }
  38%  { opacity: 0.18; }
  46%  { opacity: 1; }
  58%  { opacity: 0.3; }
  100% { opacity: 0; }
}

@keyframes era-shudder {
  0%   { transform: translate3d(0, 0, 0); filter: none; }
  15%  { transform: translate3d(-4px, 1px, 0); filter: hue-rotate(25deg) saturate(1.6); }
  32%  { transform: translate3d(5px, -2px, 0); filter: hue-rotate(-30deg) saturate(1.4); }
  45%  { transform: translate3d(-6px, 2px, 0); filter: saturate(0.2) brightness(1.4); }
  60%  { transform: translate3d(3px, 0, 0);    filter: hue-rotate(12deg); }
  100% { transform: translate3d(0, 0, 0); filter: none; }
}

/*
  Reduced motion: src/render/theme.js already skips the animated path entirely
  and swaps the era instantly, but these keep the CSS honest if the class is
  ever applied by something else.
*/
@media (prefers-reduced-motion: reduce) {
  #era-glitch.is-active .glitch__bars,
  #era-glitch.is-active .glitch__flash,
  html:has(#era-glitch.is-active) #shell {
    animation: none;
  }
}
