/*
 * Shimti Multimedia - the sliding doors
 *
 * THE IDEA
 *
 * The homepage is the machinery inside the site. The doors are the airlock in front of it.
 * Passing between the machinery and a page is a level change, and it looks like one: the
 * doors shut, hold for a beat, and open again on the other side.
 *
 * Moving between two ordinary pages is not a level change and gets no ceremony at all.
 *
 *   home -> section     shut, pause, open      crossing out of the machinery
 *   section -> home     shut, pause, open      crossing back into it
 *   section -> section  nothing                an ordinary link on an ordinary page
 *
 * THE RESTING STATE IS OPEN, EVERYWHERE
 *
 * That is the single most important property in this file. The doors are scenery that can
 * cover the entire viewport, so a mistake here does not make the site look wrong - it
 * makes it invisible. Every page rests with them off screen, and every animation both
 * starts and finishes there, so any failure to animate leaves the site visible rather than
 * sealed. There is no state in which "nothing happened" means "nothing can be seen".
 *
 * NO VIEW TRANSITIONS
 *
 * An earlier version drove the doors from cross-document view transitions. That was right
 * when the doors were the surface a page was printed on and each navigation was a single
 * A-to-B slide. An airlock is not A to B - it is out, hold, back - which a transition
 * between two captured states cannot express without fighting the geometry the browser
 * computes for each snapshot.
 *
 * So the doors are plain CSS animations on real elements, which is both simpler and
 * something that can be reasoned about with certainty. Ordinary page-to-page moves still
 * use a view transition for their cross-fade; the airlock skips it, because the screen is
 * behind two opaque panels at the moment of navigation and there is nothing to hide.
 *
 * @requires markup: .doors > .door.door-left + .door.door-right, data-page on <html>
 */

@view-transition {
  navigation: auto;
}

:root {
  --door-close: 460ms;
  /* The beat between shutting and opening. Entirely artificial, and the reason the
     transition reads as arriving somewhere rather than as a wipe. It also conceals the
     navigation itself, which happens while the doors are shut. */
  --door-pause: 260ms;
  --door-open: 560ms;
  /* Weighted so the doors leave slowly and arrive fast - machinery under load, rather
     than a panel easing politely into place. */
  --door-ease: cubic-bezier(0.72, 0, 0.24, 1);
  --door-seam: rgba(180, 220, 255, 0.55);
}

.doors {
  position: fixed;
  inset: 0;
  /* Above everything. While these are moving they are the only thing anyone should see,
     and the homepage's own layers occupy 0-50, documented in styles.css. */
  z-index: 100;
  pointer-events: none;

  /*
   * Clipped to the viewport, and it has to be.
   *
   * At rest each door is translated a full width outside the window. Being fixed does not
   * spare the document from them: they extended the page sideways, which raised a
   * horizontal scrollbar, and that scrollbar stole height from a layout built on the
   * window never scrolling at all. A stray 12px of vertical scroll on a page that is
   * supposed to be fixed, traced back to two panels parked off the right edge.
   */
  overflow: hidden;
}

.door {
  position: fixed;
  top: 0;
  bottom: 0;
  /* Slightly over half, so no hairline of the page shows between them when shut. Rounding
     at fractional device pixel ratios is otherwise visible as a flickering seam. */
  width: 50.4vw;
  background:
    linear-gradient(180deg, rgba(180, 220, 255, 0.05), rgba(0, 0, 0, 0) 38%),
    #05070b;
}

/*
 * Open. The resting state on every page, and the state every animation ends in.
 */
.door-left {
  left: 0;
  transform: translateX(-100%);
  border-right: 1px solid var(--door-seam);
}

.door-right {
  right: 0;
  transform: translateX(100%);
  border-left: 1px solid var(--door-seam);
}

/* ------------------------------------------------------------------- the airlock */

@keyframes door-shut-left {
  from { transform: translateX(-100%); }
  to   { transform: translateX(0); }
}

@keyframes door-shut-right {
  from { transform: translateX(100%); }
  to   { transform: translateX(0); }
}

@keyframes door-open-left {
  from { transform: translateX(0); }
  to   { transform: translateX(-100%); }
}

@keyframes door-open-right {
  from { transform: translateX(0); }
  to   { transform: translateX(100%); }
}

/*
 * Leaving. Held shut afterwards, because the navigation happens next and the shut doors
 * are what hides it.
 */
html[data-airlock="out"] .door-left {
  animation: door-shut-left var(--door-close) var(--door-ease) forwards;
}

html[data-airlock="out"] .door-right {
  animation: door-shut-right var(--door-close) var(--door-ease) forwards;
}

/*
 * Arriving. Shut through the pause, then open.
 *
 * backwards fill holds the first keyframe during the delay, which is what produces the
 * beat. No forwards fill on the other end, deliberately: the animation finishes exactly
 * where the element rests anyway, so once it is done the resting style takes over on its
 * own and there is no lingering animated value to go stale.
 */
html[data-airlock="in"] .door-left {
  animation: door-open-left var(--door-open) var(--door-ease) var(--door-pause) backwards;
}

html[data-airlock="in"] .door-right {
  animation: door-open-right var(--door-open) var(--door-ease) var(--door-pause) backwards;
}

/*
 * The safety net, set by doors.js when an arrival demonstrably never animated.
 *
 * An animation can be applied and "running" and still never advance - a frozen document
 * timeline reports exactly that, playState running with currentTime pinned at 0 - and the
 * first keyframe of an arrival is shut. That state hides the entire site.
 *
 * Deliberately over-specified. A running animation outranks any normal declaration, so
 * cancelling it is the only thing that works, and the rule being cancelled carries html
 * plus an attribute plus a class. A plainer selector loses the cascade silently and the
 * net does nothing while appearing to be in place - which is exactly what it did when
 * first written.
 */
html[data-doors-stuck][data-airlock] .door.door-left {
  animation: none;
  transform: translateX(-100%);
}

html[data-doors-stuck][data-airlock] .door.door-right {
  animation: none;
  transform: translateX(100%);
}

/* -------------------------------------------------------------- ordinary page moves */

/*
 * Section to section. No doors, so this is only a content change and should feel like one.
 */
::view-transition-old(root),
::view-transition-new(root) {
  animation-duration: 220ms;
}

/* ----------------------------------------------------------------- reduced motion */

/*
 * Two full-height panels sweeping the viewport is precisely the large-area motion this
 * setting exists to prevent, so the movement goes and nothing else does. The doors rest
 * open, every page reads correctly, and navigation still works. The animation is turned
 * off; the design is not.
 */
@media (prefers-reduced-motion: reduce) {
  @view-transition {
    navigation: none;
  }

  html[data-airlock] .door-left,
  html[data-airlock] .door-right {
    animation: none;
  }
}
