/*
 * Topic scenes.
 * js/ext/scenes.js toggles state classes on #play-visual:
 *   is-answered + is-correct | is-partial | is-wrong | is-timeout
 * Scenes put data-scene="<name>" on their root and animate from those classes.
 * Shared base rules first, then Scenes-A (laser, tunnel, signs, safe).
 */

/* ---------- Shared base ---------- */
.visual .hr-scene,
.visual [data-scene] {
  display: block;
  width: 100%;
  max-width: 340px;
  height: auto;
  margin: 0 auto;
}
.visual .hr-scene text {
  font-family: inherit;
  paint-order: stroke;
}

/* ---------- Laser Grid ---------- */
[data-scene="laser"] .lz-lit {
  filter: drop-shadow(0 0 3px rgba(255, 209, 102, 0.35));
  animation: lz-hum 2.6s ease-in-out infinite;
}
[data-scene="laser"] .lz-reveal {
  opacity: 0;
}
[data-scene="laser"] .lz-reveal .lz-result {
  transform-box: fill-box;
  transform-origin: left center;
}
[data-scene="laser"] .lz-sweep {
  opacity: 0;
}
.is-correct [data-scene="laser"] .lz-reveal,
.is-partial [data-scene="laser"] .lz-reveal {
  opacity: 1;
}
.is-correct [data-scene="laser"] .lz-reveal .lz-result,
.is-partial [data-scene="laser"] .lz-reveal .lz-result {
  animation: lz-grow 0.55s 0.3s cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
.is-correct [data-scene="laser"] .lz-reveal line {
  animation: lz-fade-in 0.35s 0.3s both;
}
.is-correct [data-scene="laser"] .lz-sweep,
.is-partial [data-scene="laser"] .lz-sweep {
  animation: lz-sweep 0.6s ease-out both;
}
/* With a result beam, the source beams are "cut" (dimmed) as the result appears. */
.is-correct .lz-has-result .lz-beam .lz-lit,
.is-partial .lz-has-result .lz-beam .lz-lit {
  opacity: 0.4;
  animation: lz-cut 0.5s 0.1s both;
}
.is-correct [data-scene="laser"]:not(.lz-has-result) .lz-lit {
  animation: lz-flash 0.6s 0.2s both;
}
.is-correct [data-scene="laser"] .lz-overlap rect {
  fill: #2ee66b;
  transition: fill 0.3s 0.3s;
}
.is-wrong [data-scene="laser"] .lz-frame,
.is-timeout [data-scene="laser"] .lz-frame {
  animation: lz-alarm 0.45s ease-in-out 2;
}
.is-wrong [data-scene="laser"] .lz-lit rect,
.is-timeout [data-scene="laser"] .lz-lit rect {
  animation: lz-red 0.45s ease-in-out 2;
}

@keyframes lz-hum {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.82; }
}
@keyframes lz-grow {
  from { transform: scaleX(0); }
  to { transform: scaleX(1); }
}
@keyframes lz-fade-in {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes lz-sweep {
  0% { opacity: 0; transform: translateX(0); }
  20% { opacity: 0.9; }
  100% { opacity: 0; transform: translateX(286px); }
}
@keyframes lz-cut {
  from { opacity: 1; }
  to { opacity: 0.4; }
}
@keyframes lz-flash {
  0% { opacity: 1; }
  40% { opacity: 0.35; }
  100% { opacity: 1; }
}
@keyframes lz-alarm {
  0%, 100% { stroke: rgba(139, 124, 255, 0.35); fill: rgba(20, 26, 46, 0.6); }
  50% { stroke: #ff5470; fill: rgba(255, 84, 112, 0.22); }
}
@keyframes lz-red {
  0%, 100% { opacity: 1; }
  50% { fill: #ff5470; }
}

/* ---------- Number Line Tunnel + sign panel ---------- */
[data-scene="tunnel"] .tn-light {
  animation: tn-lights 2.4s linear infinite;
}
[data-scene="tunnel"] .tn-light:nth-child(odd) {
  animation-delay: -1.2s;
}
[data-scene="tunnel"] .tn-reveal,
[data-scene="signs"] .tn-reveal {
  opacity: 0;
}
.is-correct [data-scene="tunnel"] .tn-reveal,
.is-correct [data-scene="signs"] .tn-reveal {
  opacity: 1;
  transition: opacity 0.2s;
}
[data-scene="tunnel"] .tn-reveal-arc-path {
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
}
.is-correct [data-scene="tunnel"] .tn-reveal-arc-path {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.8s ease-in-out;
}
[data-scene="tunnel"] .tn-runner-wrap {
  opacity: 0;
}
.is-correct [data-scene="tunnel"] .tn-runner-wrap {
  animation: tn-hop 0.85s ease-in-out forwards;
}
.is-correct [data-scene="tunnel"] .tn-runner-v {
  animation-name: tn-hop-v;
}
.is-correct [data-scene="tunnel"] .tn-land {
  fill: #2ee66b;
  stroke: #2ee66b;
  stroke-dasharray: none;
  transition: fill 0.25s 0.8s, stroke 0.25s 0.8s;
}
.is-correct [data-scene="tunnel"] .tn-goal rect {
  fill-opacity: 0.6;
  transition: fill-opacity 0.3s 0.7s;
}
[data-scene="tunnel"] .tn-mercury rect,
[data-scene="tunnel"] .tn-sub {
  transform-box: fill-box;
  transform-origin: 50% 100%;
}
.is-correct [data-scene="tunnel"] .tn-mercury rect {
  transform: scaleY(var(--ms, 1));
  transition: transform 0.85s ease-in-out;
}
.is-correct [data-scene="tunnel"] .tn-sub {
  transform: translateY(var(--dy, 0));
  transition: transform 0.85s ease-in-out;
}
.is-correct [data-scene="signs"] .sp-result {
  stroke-dasharray: none;
}
.is-wrong [data-scene="tunnel"] .tn-shell,
.is-timeout [data-scene="tunnel"] .tn-shell,
.is-wrong [data-scene="signs"] .tn-shell,
.is-timeout [data-scene="signs"] .tn-shell {
  animation: tn-alarm 0.45s ease-in-out 2;
}
.is-wrong [data-scene="tunnel"] .tn-light,
.is-timeout [data-scene="tunnel"] .tn-light {
  fill: #ff5470;
  animation: tn-lights 0.3s linear 3;
}

@keyframes tn-lights {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.25; }
}
/* Parabolic hop: x = dx·t, y = −4h·t(1−t). Units are SVG user units. */
@keyframes tn-hop {
  0% { opacity: 1; transform: translate(0, 0); }
  25% { transform: translate(calc(var(--dx) * 0.25), calc(var(--h) * -0.75)); }
  50% { transform: translate(calc(var(--dx) * 0.5), calc(var(--h) * -1)); }
  75% { transform: translate(calc(var(--dx) * 0.75), calc(var(--h) * -0.75)); }
  100% { opacity: 1; transform: translate(var(--dx), 0); }
}
@keyframes tn-hop-v {
  0% { opacity: 1; transform: translate(0, 0); }
  25% { transform: translate(calc(var(--h) * 0.75), calc(var(--dy) * 0.25)); }
  50% { transform: translate(var(--h), calc(var(--dy) * 0.5)); }
  75% { transform: translate(calc(var(--h) * 0.75), calc(var(--dy) * 0.75)); }
  100% { opacity: 1; transform: translate(0, var(--dy)); }
}
@keyframes tn-alarm {
  0%, 100% { stroke: rgba(139, 124, 255, 0.45); }
  50% { stroke: #ff5470; fill: #2a1020; }
}

/* ---------- Power Safe ---------- */
[data-scene="safe"] .ps-dial,
[data-scene="safe"] .ps-expdial {
  transform-box: fill-box;
  transform-origin: center;
}
[data-scene="safe"] .ps-door {
  transform-box: fill-box;
  transform-origin: 100% 50%;
}
[data-scene="safe"] .ps-strikes,
[data-scene="safe"] .ps-reveal {
  opacity: 0;
}
[data-scene="safe"] .ps-strikes line {
  stroke-dasharray: 100;
  stroke-dashoffset: 100;
}
.is-correct [data-scene="safe"] .ps-dial {
  animation: ps-spin 0.55s ease-in-out both;
}
.is-correct [data-scene="safe"] .ps-expdial {
  animation: ps-spin 0.7s ease-in-out both;
}
.is-correct [data-scene="safe"] .ps-door {
  transform: scaleX(0.1);
  transition: transform 0.4s 0.5s cubic-bezier(0.5, 0, 0.3, 1);
}
.is-correct [data-scene="safe"] .ps-light {
  fill: #2ee66b;
}
.is-correct [data-scene="safe"] .ps-bank-b {
  transform: translateX(var(--slide, 0));
  transition: transform 0.45s cubic-bezier(0.3, 0.8, 0.3, 1);
}
.is-correct [data-scene="safe"] .ps-join,
.is-correct [data-scene="safe"] .ps-bracket {
  opacity: 0;
  transition: opacity 0.3s;
}
.is-correct [data-scene="safe"] .ps-token rect {
  stroke: #ffd166;
  transition: stroke 0.3s 0.25s;
}
.is-correct [data-scene="safe"] .ps-strikes,
.is-correct [data-scene="safe"] .ps-reveal {
  opacity: 1;
  transition: opacity 0.2s;
}
.is-correct [data-scene="safe"] .ps-strikes line {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 0.4s 0.1s ease-out;
}
.is-wrong [data-scene="safe"],
.is-timeout [data-scene="safe"] {
  animation: ps-shake 0.36s ease-in-out;
}
.is-wrong [data-scene="safe"] .ps-body,
.is-timeout [data-scene="safe"] .ps-body {
  animation: ps-alarm 0.45s ease-in-out 2;
}
.is-wrong [data-scene="safe"] .ps-light,
.is-timeout [data-scene="safe"] .ps-light {
  animation: ps-blink 0.3s linear 3;
}

@keyframes ps-spin {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
@keyframes ps-shake {
  0%, 100% { transform: translateX(0); }
  25% { transform: translateX(-5px); }
  75% { transform: translateX(5px); }
}
@keyframes ps-alarm {
  0%, 100% { stroke: #8b7cff; }
  50% { stroke: #ff5470; fill: #2a1020; }
}
@keyframes ps-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.15; }
}

/* ---------- Reduced motion: no animation, final frame on correct ---------- */
@media (prefers-reduced-motion: reduce) {
  .visual .hr-scene,
  .visual .hr-scene * {
    animation: none !important;
    transition: none !important;
  }
  .is-correct [data-scene="tunnel"] .tn-runner-wrap {
    opacity: 1;
    transform: translate(var(--dx), 0);
  }
  .is-correct [data-scene="tunnel"] .tn-runner-v {
    transform: translate(0, var(--dy));
  }
  /* Static alarm frame instead of the flashing one. */
  .is-wrong [data-scene] .lz-frame,
  .is-timeout [data-scene] .lz-frame,
  .is-wrong [data-scene] .tn-shell,
  .is-timeout [data-scene] .tn-shell,
  .is-wrong [data-scene] .ps-body,
  .is-timeout [data-scene] .ps-body {
    stroke: #ff5470;
  }
}
