/* ============================================================
   crit-overlay.css — class critical-hit effect layers

   Shared by the app (index.html) and the dice lab
   (util/dice_test.html). Pairs with /js/crit-overlay.js, which owns
   the six class crits: wizard/priest spellcasting crits and melee
   attack crits.

   The generic D20 crit CSS (.crit-overlay shell, __text, flame /
   starburst particles, critShake) lives in components.css for the
   app and inline in the lab — this file adds only what the class
   beats need on top.
   ============================================================ */

/* ============================================================
   CLASS CRIT THEMES

   data-crit-theme on .crit-overlay swaps this palette; the
   shared effect layers below read it, so one builder + one
   theme block is a whole new crit look. Same pattern as
   data-texture on the dice grid.
   ============================================================ */

.crit-overlay {
  --crit-ink:   rgba(220,190,110,0.95);   /* primary vector stroke */
  --crit-glow:  rgba(255,240,190,0.90);   /* secondary/highlight stroke */
  --crit-flash: rgba(60,50,20,0.35);      /* full-screen flash tint */
}

.crit-overlay[data-crit-theme="arcane"] {
  --crit-ink:   rgba(168,85,247,0.95);
  --crit-glow:  rgba(120,220,255,0.88);
  --crit-flash: rgba(58,20,96,0.42);
}
.crit-overlay[data-crit-theme="misfire"] {
  --crit-ink:   rgba(110,240,122,0.95);
  --crit-glow:  rgba(168,85,247,0.85);
  --crit-flash: rgba(12,48,20,0.45);
}
.crit-overlay[data-crit-theme="divine"] {
  --crit-ink:   rgba(240,192,37,0.95);
  --crit-glow:  rgba(255,246,214,0.92);
  --crit-flash: rgba(80,62,12,0.38);
}
.crit-overlay[data-crit-theme="ash"] {
  --crit-ink:   rgba(150,138,116,0.92);
  --crit-glow:  rgba(90,80,64,0.85);
  --crit-flash: rgba(10,8,6,0.55);
}
.crit-overlay[data-crit-theme="steel"] {
  --crit-ink:   rgba(184,208,248,0.95);
  --crit-glow:  rgba(255,255,255,0.95);
  --crit-flash: rgba(150,180,235,0.30);
}
.crit-overlay[data-crit-theme="blood"] {
  --crit-ink:   rgba(204,16,32,0.95);
  --crit-glow:  rgba(255,140,90,0.85);
  --crit-flash: rgba(80,0,0,0.45);
}

/* Themed flash — generic form of .crit-overlay__flash-failure, which
   stays as-is because the main app's copy of the overlay still uses it. */
.crit-overlay__flash {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: var(--crit-flash);
  animation: themeFlash 2s ease-out forwards;
}

@keyframes themeFlash {
  0%   { opacity: 0; }
  10%  { opacity: 1; }
  55%  { opacity: 0.45; }
  100% { opacity: 0; }
}

/* Vector canvas used by the class crits (the older shatter effect keeps
   its own #shatter-canvas id). */
.crit-overlay__canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  z-index: 1;
}

/* ---- CRT scanlines: the 80's arcade tell, wizard crits only ---- */
.crit-overlay__scanlines {
  position: absolute;
  inset: -4px 0;
  z-index: 2;
  pointer-events: none;
  mix-blend-mode: overlay;
  opacity: 0.55;
  background: repeating-linear-gradient(
    to bottom,
    rgba(255,255,255,0.10) 0px,
    rgba(255,255,255,0.10) 1px,
    transparent 1px,
    transparent 3px
  );
  animation: crtRoll 2s linear forwards;
}

@keyframes crtRoll {
  0%   { transform: translateY(0);   opacity: 0; }
  12%  { opacity: 0.55; }
  85%  { opacity: 0.45; }
  100% { transform: translateY(12px); opacity: 0; }
}

/* ---- Chromatic split on the crit text (vector-arcade fringing) ---- */
.crit-overlay__text--chroma {
  text-shadow:
    -3px 0 0 rgba(255,40,160,0.55),
    3px 0 0 rgba(60,220,255,0.55),
    0 0 22px var(--crit-glow),
    2px 2px 4px rgba(0,0,20,0.95);
}

/* ---- Drifting motes (rise or fall) ---- */
.crit-mote {
  position: absolute;
  width: var(--sb-size);
  height: var(--sb-size);
  border-radius: 50%;
  background: var(--sb-color);
  animation: moteDrift var(--sb-dur) ease-out var(--sb-delay) forwards;
  z-index: 1;
}

@keyframes moteDrift {
  0%   { transform: translate(0, 0) scale(1);          opacity: 0; }
  15%  { opacity: 1; }
  100% { transform: translate(var(--drift), var(--dist)) scale(0.4); opacity: 0; }
}

/* ---- Divine light shafts ---- */
.crit-overlay__rays {
  position: absolute;
  inset: -20% -20% 0 -20%;
  z-index: 1;
  transform-origin: 50% 0;
  background: repeating-conic-gradient(
    from 200deg at 50% 0%,
    transparent 0deg 3deg,
    var(--crit-glow) 3deg 5deg,
    transparent 5deg 9deg
  );
  opacity: 0;
  -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,0.95) 0%, transparent 78%);
  mask-image: linear-gradient(to bottom, rgba(0,0,0,0.95) 0%, transparent 78%);
  animation: raysSweep 2s ease-out forwards;
}

@keyframes raysSweep {
  0%   { opacity: 0;    transform: scaleY(0.6) rotate(-3deg); }
  18%  { opacity: 0.55; }
  70%  { opacity: 0.35; }
  100% { opacity: 0;    transform: scaleY(1.05) rotate(3deg); }
}

/* Failure runs the same shafts backwards — light withdrawing. */
.crit-overlay__rays--retract {
  animation: raysRetract 2s ease-in forwards;
}

@keyframes raysRetract {
  0%   { opacity: 0.40; transform: scaleY(1.05); }
  35%  { opacity: 0.22; }
  100% { opacity: 0;    transform: scaleY(0.35); }
}

/* ---- Forsaken: shadow curtain closing from the top ---- */
.crit-overlay__curtain {
  position: absolute;
  inset: 0;
  z-index: 0;
  background: linear-gradient(to bottom, rgba(0,0,0,0.85) 0%, transparent 70%);
  transform: translateY(-100%);
  animation: curtainFall 2s ease-out forwards;
}

@keyframes curtainFall {
  0%   { transform: translateY(-100%); }
  45%  { transform: translateY(0); }
  80%  { transform: translateY(0); }
  100% { transform: translateY(-40%); opacity: 0; }
}

/* ---- Fumble: CRT dropout bar rolling down the screen ---- */
.crit-overlay__dropout {
  position: absolute;
  left: 0;
  width: 100%;
  height: 14vh;
  z-index: 2;
  pointer-events: none;
  background: linear-gradient(
    to bottom,
    transparent 0%,
    rgba(255,255,255,0.05) 30%,
    rgba(255,120,120,0.14) 50%,
    rgba(255,255,255,0.05) 70%,
    transparent 100%
  );
  animation: dropoutRoll 2s linear forwards;
}

@keyframes dropoutRoll {
  0%   { top: -20vh; opacity: 0; }
  10%  { opacity: 1; }
  85%  { opacity: 0.7; }
  100% { top: 110vh; opacity: 0; }
}

/* ---- Body animations for the class crits ---- */
@keyframes critGlitchShake {
  0%   { transform: translate(0, 0); }
  33%  { transform: translate(-6px, 2px) skewX(1.2deg); }
  66%  { transform: translate(5px, -2px) skewX(-1deg); }
  100% { transform: translate(0, 0); }
}

@keyframes critPunch {
  0%   { transform: scale(1); }
  12%  { transform: scale(1.016); }
  100% { transform: scale(1); }
}

/* Reduced motion: keep the crit readable, drop the movement. The JS
   already skips particles and freezes the canvas on a single frame. */
@media (prefers-reduced-motion: reduce) {
  .crit-overlay__scanlines,
  .crit-overlay__rays,
  .crit-overlay__curtain,
  .crit-overlay__dropout { animation: none; }
  .crit-overlay__rays,
  .crit-overlay__curtain { opacity: 0.3; transform: none; }
  .crit-overlay__dropout { display: none; }
}


/* ============================================================
   PAGE SHAKE CONTAINMENT

   The shake/punch keyframes translate the page wrapper (.app-shell
   in the app, .lab-page in the lab). Its sideways travel would spawn
   a horizontal scrollbar, which shrinks the viewport — and with it
   the fixed crit overlay — for the length of the effect. The clip
   goes on <html>, the scroll container: an element's own overflow
   cannot clip its own displacement. clip (not hidden) leaves the Y
   axis alone, so scrolling and sticky children are unaffected. The
   class is added only while an animation runs.
   ============================================================ */

.crit-shaking {
  overflow-x: clip;
}
