/**
 * Motion.
 *
 * Every animated element starts in its "before" state HERE, in CSS, and is
 * released by a class the JS adds. That ordering matters: if the initial state
 * were set by JS, a slow script would leave content visible and then snap it
 * away. It also means the reduced-motion block below is the whole story — with
 * motion off, nothing ever enters a hidden state to begin with.
 */

/* ---------------------------------------------------------------
 * Scroll reveal
 *
 * `data-reveal` marks an element; `--sbs-reveal-delay` staggers siblings.
 * The JS only ever adds `.is-revealed`.
 * ------------------------------------------------------------- */
.sbs-motion [data-reveal] {
  opacity: 0;
  transform: translateY(28px);
  transition: opacity var(--sbs-dur-reveal) var(--sbs-ease-out),
    transform var(--sbs-dur-reveal) var(--sbs-ease-out);
  transition-delay: var(--sbs-reveal-delay, 0ms);
  will-change: opacity, transform;
}

.sbs-motion [data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/* A reveal that only fades — for large photography, where a rise reads as a
 * layout shift rather than as an entrance. */
.sbs-motion [data-reveal='fade'] {
  transform: none;
}

/* ---------------------------------------------------------------
 * Hero title — words rise in sequence.
 *
 * sbs_split_words() wraps each word in a span pair: the outer clips, the
 * inner moves. The clip is what makes the words appear from behind the line
 * rather than fading in place.
 * ------------------------------------------------------------- */
/*
 * The reveal mask.
 *
 * Each word sits in an `overflow: hidden` box and slides up into it from
 * below. The box is sized by the line box, and glyph ink drops BELOW the line
 * box — so left alone the mask shears the tail off every descender it
 * animates, and the damage outlasts the animation because the mask never goes
 * away. It reads as a font bug: "Empfangen." with a flat-bottomed g.
 *
 * The padding gives the mask room for that ink; the matching negative margin
 * takes the same amount back off the margin box, so line spacing, baselines
 * and `vertical-align: bottom` are all exactly as they were.
 */
.sbs-word {
  display: inline-block;
  overflow: hidden;
  vertical-align: bottom;
  padding-bottom: var(--sbs-descender-room);
  margin-bottom: calc(var(--sbs-descender-room) * -1);
}

/* No transform in the base rule: with the runtime absent the words must be
 * plainly visible, so only `.sbs-motion` hides them in the first place. */
.sbs-word__inner {
  display: inline-block;
  transition: transform 1000ms var(--sbs-ease-out);
  transition-delay: var(--sbs-reveal-delay, 0ms);
}

/*
 * Off the bottom of the mask, which is now its own height PLUS the descender
 * room — at a plain 100% the top of the word would show in that extra strip
 * before the animation starts.
 */
.sbs-motion .sbs-word__inner {
  transform: translateY(calc(100% + var(--sbs-descender-room)));
}

.sbs-motion .is-revealed .sbs-word__inner {
  transform: none;
}

/* ---------------------------------------------------------------
 * Parallax
 *
 * The image is oversized by the travel distance so it can move without
 * exposing an edge. The JS writes --sbs-parallax as a px offset.
 * ------------------------------------------------------------- */
.sbs-motion [data-parallax] {
  height: 116%;
  top: -8%;
  transform: translate3d(0, var(--sbs-parallax, 0px), 0);
  will-change: transform;
}

/* ---------------------------------------------------------------
 * Overlay entrance — panels slide, links stagger in.
 * ------------------------------------------------------------- */
.sbs-overlay__left,
.sbs-overlay__right {
  transform: translateY(-14px);
  opacity: 0;
  transition: transform var(--sbs-dur) var(--sbs-ease-out),
    opacity var(--sbs-dur) var(--sbs-ease-out);
}

.sbs-overlay.is-open .sbs-overlay__left,
.sbs-overlay.is-open .sbs-overlay__right {
  transform: none;
  opacity: 1;
}

.sbs-overlay.is-open .sbs-overlay__right {
  transition-delay: 60ms;
}

.sbs-overlay__sections li {
  opacity: 0;
  transform: translateY(16px);
  transition: opacity 320ms var(--sbs-ease-out), transform 320ms var(--sbs-ease-out);
}

.sbs-overlay.is-open .sbs-overlay__sections li {
  opacity: 1;
  transform: none;
  /* 40ms per item, set inline by nav.js via --sbs-reveal-delay. */
  transition-delay: var(--sbs-reveal-delay, 0ms);
}

/* ---------------------------------------------------------------
 * Carousels
 * ------------------------------------------------------------- */
.sbs-carousel {
  cursor: grab;
}

.sbs-carousel.is-dragging {
  cursor: grabbing;
  user-select: none;
}

.sbs-carousel.is-dragging a {
  pointer-events: none;
}

/* ---------------------------------------------------------------
 * Reduced motion
 *
 * Everything final and static. main.js checks the same query and never
 * initialises Lenis, the parallax loop or the word split, so this block and
 * the JS agree by construction rather than by coincidence.
 * ------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }

  .sbs-motion [data-reveal],
  .sbs-motion [data-reveal].is-revealed {
    opacity: 1;
    transform: none;
    transition: none;
  }

  .sbs-motion .sbs-word__inner,
  .sbs-word__inner {
    transform: none;
    transition: none;
  }

  .sbs-motion [data-parallax] {
    height: 100%;
    top: 0;
    transform: none;
  }

  .sbs-overlay__left,
  .sbs-overlay__right,
  .sbs-overlay__sections li {
    transform: none;
    opacity: 1;
    transition: none;
  }

  .sbs-photo-card img,
  .sbs-photo-card:hover img {
    transform: none;
    transition: none;
  }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}
