/*
 * Signal Trace scenes (topic: linear): radar, relay, signal-table, decoder, numline.
 * Markup: js/visuals/scenes-linear.js. Reactions come from the state classes on
 * #play-visual (is-correct / is-wrong / is-timeout, set by js/ext/scenes.js) plus
 * .ln-filled, .ln-revealed, .is-answer and .is-chosen added by scenes-linear.js.
 * Only transform and opacity are animated. Animated elements never carry an SVG
 * transform attribute themselves, so CSS transforms are safe.
 */

.scene-ln {
  display: block;
  width: 100%;
  max-width: 340px;
  height: auto;
  margin: 0 auto;
  overflow: visible;
}
.scene-ln .ln-font {
  font-family: ui-monospace, "SF Mono", Menlo, Consolas, monospace;
}
.scene-ln .ln-halo {
  paint-order: stroke;
  stroke: #0d1426;
  stroke-width: 4px;
  stroke-linejoin: round;
}
.scene-ln .ln-unknown,
.scene-ln .ln-filled,
.scene-ln .ln-reveal-point,
.scene-ln .ln-tower-unknown,
.scene-ln .ln-beacon {
  transform-box: fill-box;
  transform-origin: center;
}

/* ---------- Idle: the screen is live ---------- */
.scene-ln .ln-sweep {
  transform-box: fill-box;
  transform-origin: 0% 100%;
  animation: ln-sweep 5s linear infinite;
}
.scene-ln .ln-unknown {
  animation: ln-blink 1.6s ease-in-out infinite;
}
.scene-ln .ln-beacon {
  animation: ln-beacon 1.8s ease-in-out infinite;
}
.scene-ln .ln-pulse {
  animation: ln-flow 1.4s ease-in-out infinite;
}
.scene-ln .ln-pulse-2 {
  animation-delay: 0.35s;
}

/* ---------- Correct: print the answer and draw the solution ---------- */
.is-answered .scene-ln .ln-unknown {
  animation: none;
}
.is-correct .scene-ln .ln-filled {
  animation: ln-pop 0.45s cubic-bezier(0.2, 0.9, 0.3, 1.3) both;
}
.is-correct .scene-ln .ln-revealed .ln-reveal-line {
  animation: ln-fade 0.5s 0.1s ease-out both;
}
.is-correct .scene-ln .ln-revealed .ln-reveal-point {
  animation: ln-pop 0.5s 0.35s cubic-bezier(0.2, 0.9, 0.3, 1.3) both;
}
.is-correct [data-scene="relay"] .ln-tower-unknown {
  animation: ln-rise 0.5s ease-out both;
}

/* Number line options: the right graph glows on every outcome; a wrong pick is flagged. */
.is-answered [data-scene="numline"] .ln-option.is-answer .ln-option-frame {
  stroke: #2ee66b;
}
.is-answered [data-scene="numline"] .ln-option.is-chosen:not(.is-answer) .ln-option-frame {
  stroke: #ff5470;
}
.is-answered [data-scene="numline"] .ln-option:not(.is-answer) {
  opacity: 0.55;
}

/* ---------- Wrong / timeout: signal lost ---------- */
.is-wrong .scene-ln {
  animation: ln-shake 0.4s ease-in-out both;
}
.is-timeout .scene-ln {
  opacity: 0.6;
}
.is-wrong .scene-ln .ln-sweep,
.is-timeout .scene-ln .ln-sweep {
  animation: none;
  opacity: 0;
}

@keyframes ln-sweep {
  from { transform: rotate(0deg); }
  to { transform: rotate(360deg); }
}
@keyframes ln-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.35; }
}
@keyframes ln-beacon {
  0%, 100% { opacity: 1; transform: scale(1); }
  50% { opacity: 0.4; transform: scale(0.7); }
}
@keyframes ln-flow {
  0%, 100% { opacity: 0.35; }
  50% { opacity: 1; }
}
@keyframes ln-pop {
  from { opacity: 0; transform: scale(0.3); }
  to { opacity: 1; transform: scale(1); }
}
@keyframes ln-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
@keyframes ln-rise {
  0% { transform: translateY(0); }
  45% { transform: translateY(-8px); }
  100% { transform: translateY(0); }
}
@keyframes ln-shake {
  0%, 100% { transform: translateX(0); }
  20% { transform: translateX(-6px); }
  40% { transform: translateX(5px); }
  60% { transform: translateX(-4px); }
  80% { transform: translateX(2px); }
}

/* ---------- Reduced motion: static final frame ---------- */
@media (prefers-reduced-motion: reduce) {
  .scene-ln,
  .scene-ln * {
    animation: none !important;
    transition: none !important;
  }
  .scene-ln .ln-sweep {
    opacity: 0;
  }
}
