/* ==========================================================================
   ANIMATIONS — all @keyframes + the reduced-motion downgrade for each one.
   ========================================================================== */

@keyframes byte-idle-sway {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(-4px); }
}

@keyframes byte-wave {
  0%, 100% { transform: rotate(0deg); }
  50% { transform: rotate(-35deg); }
}

@keyframes byte-blink {
  0%, 88%, 100% { transform: scaleY(1); }
  92% { transform: scaleY(0.1); }
}
.byte-eye { transform-box: fill-box; transform-origin: 50% 50%; }

@keyframes byte-walk-bob {
  0% { transform: translateY(0); }
  50% { transform: translateY(-3px); }
  100% { transform: translateY(0); }
}

@keyframes byte-type-l {
  0%, 100% { transform: rotate(0deg); }
  50% { transform: rotate(12deg); }
}
@keyframes byte-type-r {
  0%, 100% { transform: rotate(0deg); }
  50% { transform: rotate(-12deg); }
}

@keyframes byte-zzz-float {
  0% { opacity: 0; transform: translate(0, 0); }
  20% { opacity: 1; }
  100% { opacity: 0; transform: translate(6px, -14px); }
}

@keyframes byte-bounce {
  0%, 100% { transform: translateY(0); }
  50% { transform: translateY(6px); }
}

@keyframes promo-dot-blink {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.3; }
}

@keyframes cursor-blink {
  0%, 50% { opacity: 1; }
  51%, 100% { opacity: 0; }
}
.tw-caret {
  display: inline-block;
  width: 0.55em;
  height: 0.9em;
  background: var(--accent);
  margin-left: 4px;
  vertical-align: -0.1em;
  animation: cursor-blink 0.8s steps(1) infinite;
}
.tw-caret.is-done { display: none; }

/* Header compress transition lives with the component, but the *trigger*
   class toggle happens in JS — no keyframe needed, plain transition. */

/* ---- Typewriter reveal -------------------------------------------------------
   Progressive enhancement: .tw-target holds real, always-present text with no
   default clipping. JS wraps each character in a .tw-char span (only when it
   decides to animate) and staggers its opacity fade-in — text occupies its
   final size/position from frame one (opacity doesn't affect layout), so
   there's no width/clip animation left to cause line-wrap flashing or
   overflow. JS-disabled or reduced-motion visitors just see plain text. */
.tw-line { display: block; }
/* A single long word (no spaces to wrap on) would otherwise overflow its
   column at any point — not tied to the reveal mechanism, just a backstop. */
.tw-target { overflow-wrap: break-word; }
/* Hidden by default so there's no flash of fully-visible plain text while
   the page waits on document.fonts.ready before wrapping it into .tw-char
   spans. Gated on .js (added synchronously at the top of <body>, see
   index.html) so a no-JS visitor — who never gets that class — always
   sees plain, visible text; initTypewriters() also restores opacity
   immediately for reduced-motion/narrow-viewport visitors, who get the
   class but skip the animation entirely. */
.js .tw-target { opacity: 0; }
@keyframes tw-char-fade {
  from { opacity: 0; }
  to { opacity: 1; }
}
.tw-char {
  opacity: 0;
  animation: tw-char-fade 0.12s ease forwards;
}
.byte-zzz {
  font-family: var(--font-display);
  fill: currentColor;
}

/* ---- Reduced motion downgrades ---------------------------------------------
   Every transform-driven loop above is neutralized; anything communicating
   state (blink, caret, success check) still fades so it isn't invisible. */
@media (prefers-reduced-motion: reduce) {
  .mascot,
  .byte-arm-l, .byte-arm-r, .byte-eye, .byte-head-group, .byte-zzz,
  .scroll-indicator__arrow, .tw-caret, .promo-badge__dot {
    animation: none !important;
  }
  .reveal {
    transform: none;
    transition: opacity var(--dur-med) ease;
  }
  .tw-char { animation: none; opacity: 1; }
  /* Keep the static -2deg sticker tilt (it's a fixed style, not motion) —
     just drop the entrance translate/scale down to a plain opacity fade. */
  .promo-badge,
  .js .promo-badge,
  .promo-badge.is-shown {
    transform: rotate(-2deg);
    transition: opacity var(--dur-med) ease;
  }
}
