/* ==========================================================================
   motion — one authored moment, then a single scroll grammar.
   Sections arrive by phosphor decay: a scanline sweeps the block, the content
   resolves from a dim ghost to full brightness. Content is visible by default;
   the reveal only runs when JS marks the document as animatable.
   ========================================================================== */

.js [data-reveal] {
  opacity: 0.001;
  transform: translateY(14px);
  filter: blur(3px);
}

.js [data-reveal].is-in {
  opacity: 1;
  transform: none;
  filter: blur(0);
  transition:
    opacity 620ms var(--ease-out),
    transform 720ms var(--ease-out),
    filter 620ms var(--ease-out);
  transition-delay: var(--reveal-delay, 0ms);
}

/* The sweep: a thin phosphor line crossing the section as it enters. */
.js [data-sweep] { position: relative; }

.js [data-sweep]::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 0;
  height: 2px;
  background: linear-gradient(
    90deg,
    transparent 0%,
    var(--phos) 18%,
    #ffffff 50%,
    var(--phos) 82%,
    transparent 100%
  );
  opacity: 0;
  pointer-events: none;
  z-index: 3;
}

.js [data-sweep].is-in::after {
  animation: sweep 900ms var(--ease-crt) forwards;
}

@keyframes sweep {
  0%   { opacity: 0; transform: translateY(0) scaleX(0.2); }
  12%  { opacity: 1; }
  85%  { opacity: 0.7; }
  100% { opacity: 0; transform: translateY(var(--sweep-h, 320px)) scaleX(1); }
}

/* Type-on for the boot log. */
.type-cursor::after {
  content: "";
  display: inline-block;
  width: 7px;
  height: 12px;
  background: var(--phos);
  translate: 2px 1px;
  animation: caret 1.05s steps(1) infinite;
}

/* Scramble text: characters settle into place, used once on the wordmark. */
.scramble { color: var(--phos-mid); }

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { opacity: 1; transform: none; filter: none; }
  .js [data-sweep]::after { display: none; }
  .type-cursor::after { animation: none; }
}
