/**
 * Page transitions - staggered horizontal slide + custom carousel.
 *
 * Content layers (menu, title, then the main content blocks) each slide
 * sideways with a small stagger, so the page reads like layered cards moving
 * through a carousel rather than one flat block.
 *
 * Carousel arrows are injected by transitions.js. Pair with transitions.js.
 * Only active once JS adds html.sl-active; otherwise pages render normally.
 */

html.sl-active {
  overflow-x: hidden;
}

/* ---- outgoing ------------------------------------------------------------ */
html.sl-out.sl-left .sl-item {
  animation: slOutLeft var(--sl-dur, 300ms) cubic-bezier(0.55, 0.06, 0.68, 0.19)
    calc(var(--i, 0) * var(--sl-gap, 35ms)) both;
}
html.sl-out.sl-right .sl-item {
  animation: slOutRight var(--sl-dur, 300ms) cubic-bezier(0.55, 0.06, 0.68, 0.19)
    calc(var(--i, 0) * var(--sl-gap, 35ms)) both;
}

/* ---- incoming ------------------------------------------------------------
   fill-mode is "backwards" (not "both"): the layers start off-screen, then
   release back to their natural styles once the animation ends. Using "both"
   would pin transform/opacity on every layer afterwards and could stomp on
   elements that manage their own transform/opacity. */
html.sl-in.sl-left .sl-item {
  animation: slInLeft var(--sl-dur, 300ms) cubic-bezier(0.16, 1, 0.3, 1)
    calc(var(--i, 0) * var(--sl-gap, 35ms)) backwards;
}
html.sl-in.sl-right .sl-item {
  animation: slInRight var(--sl-dur, 300ms) cubic-bezier(0.16, 1, 0.3, 1)
    calc(var(--i, 0) * var(--sl-gap, 35ms)) backwards;
}

@keyframes slOutLeft {
  from { transform: translate3d(0, 0, 0); opacity: 1; }
  to   { transform: translate3d(-100vw, 0, 0); opacity: 0; }
}
@keyframes slOutRight {
  from { transform: translate3d(0, 0, 0); opacity: 1; }
  to   { transform: translate3d(100vw, 0, 0); opacity: 0; }
}
@keyframes slInLeft {
  from { transform: translate3d(100vw, 0, 0); opacity: 0; }
  to   { transform: translate3d(0, 0, 0); opacity: 1; }
}
@keyframes slInRight {
  from { transform: translate3d(-100vw, 0, 0); opacity: 0; }
  to   { transform: translate3d(0, 0, 0); opacity: 1; }
}

/* ---- carousel arrows ------------------------------------------------------ */
.sl-arrow {
  position: fixed;
  top: 50%;
  transform: translateY(-50%);
  z-index: 9000;
  padding: 10px 18px;
  background: transparent;
  border: none;
  outline: none;
  color: rgb(27, 142, 61);
  font-family: "MinecraftSlim", monospace;
  font-size: 46px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.5;
  user-select: none;
  transition: opacity 0.15s ease, color 0.15s ease;
}
.sl-arrow:hover {
  opacity: 1;
  color: rgb(34, 177, 76);
}
.sl-arrow:focus-visible {
  opacity: 1;
  color: rgb(34, 177, 76);
}
.sl-prev { left: 6px; }
.sl-next { right: 6px; }

@media (max-width: 700px) {
  /* On phones the swipe gesture is the control; arrows would just crowd the
     screen (and sit over the content on narrow viewports). */
  .sl-arrow {
    display: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  .sl-arrow {
    display: none;
  }
  html.sl-out .sl-item,
  html.sl-in .sl-item {
    animation: none !important;
    transform: none !important;
    opacity: 1 !important;
  }
}

/**
 * Pre-paint guard.
 * The inline <head> snippet adds html.sl-pending only when a transition is
 * actually incoming. These rules keep the slideable layers invisible until
 * transitions.js swaps in the real animation, so there is no one-frame flash
 * of the final layout. The portal canvas and the mobile menu chrome are
 * excluded: they stay put and are never part of the slide.
 */
html.sl-pending body > *:not(canvas):not(#hamburger_box):not(#hamburger_overlay):not(script):not(style) {
  visibility: hidden;
}

/**
 * Hamburger menu parallax.
 * The panel itself slides in via `left` (hamburger.js toggles .hamburger_open).
 * These rules layer a depth effect on top: each link flies in from further
 * right and slightly behind, staggered top-to-bottom, so the menu reads as a
 * stack of planes rather than one flat sheet.
 */
#hamburger_box .hamburger_link {
  transform: translate3d(0, 0, 0);
}

#hamburger_box.hamburger_open .hamburger_link {
  animation: hbParallaxIn 420ms cubic-bezier(0.16, 1, 0.3, 1) both;
}

@keyframes hbParallaxIn {
  from {
    opacity: 0;
    transform: translate3d(46px, 0, 0) scale(0.94);
  }
  to {
    opacity: 1;
    transform: translate3d(0, 0, 0) scale(1);
  }
}

/* Depth stagger: each row starts a little later than the one above it. */
#hamburger_box.hamburger_open .hamburger_link:nth-child(1) { animation-delay: 0.04s; }
#hamburger_box.hamburger_open .hamburger_link:nth-child(2) { animation-delay: 0.08s; }
#hamburger_box.hamburger_open .hamburger_link:nth-child(3) { animation-delay: 0.12s; }
#hamburger_box.hamburger_open .hamburger_link:nth-child(4) { animation-delay: 0.16s; }
#hamburger_box.hamburger_open .hamburger_link:nth-child(5) { animation-delay: 0.20s; }
#hamburger_box.hamburger_open .hamburger_link:nth-child(6) { animation-delay: 0.24s; }
#hamburger_box.hamburger_open .hamburger_link:nth-child(n + 7) { animation-delay: 0.28s; }

/* The overlay fades with a touch of scale so it feels like a plane behind the panel. */
#hamburger_overlay.overlay_open {
  animation: hbOverlayIn 300ms ease-out both;
}
@keyframes hbOverlayIn {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Closing: rows fade back out in the same staggered order. */
#hamburger_box .hamburger_link {
  will-change: transform, opacity;
}

@media (prefers-reduced-motion: reduce) {
  #hamburger_box.hamburger_open .hamburger_link,
  #hamburger_overlay.overlay_open {
    animation: none !important;
    opacity: 1;
    transform: none;
  }
}

/* ---------------------------------------------------------------- BACKGROUND
   #portal-canvas is a WebGL shader. If WebGL is unavailable or the shader
   fails to compile, particles.js bails out and the canvas keeps its own black
   background. That is the fallback: plain #000, no image to download and no
   stale frame left underneath the working effect. */
html {
  background-color: #000;
}
#portal-canvas {
  background: transparent;
}
