/**
 * view-transitions.css — cross-document page transitions
 *
 * The site is 90-odd separate HTML documents. Without this every link is a
 * white-flash reload; with it the browser snapshots the outgoing page, keeps
 * the chrome in place and animates the content swap, so navigating reads as
 * one continuous application instead of a series of loads.
 *
 * WHY ITS OWN FILE
 * Both the outgoing and the incoming document must carry the @view-transition
 * rule or nothing happens. Two thirds of the pages share style.css but the
 * standalone app landing pages do not, so the rules live here and every page
 * links this one file — one source of truth rather than the same block
 * duplicated into two stylesheets that would drift apart.
 *
 * WHY THE OPT-IN IS ALSO INLINE IN EVERY PAGE — DO NOT DELETE IT
 * Every page carries `<style>@view-transition{navigation:auto}</style>` as the
 * first thing in its <head>, and that inline copy is the one that actually
 * works. The incoming document's opt-in has to be known when the browser
 * decides whether to activate the transition, and an external stylesheet
 * resolves too late for that decision however early it is linked. Measured on
 * this site: opt-in via this file, 0 transitions out of 4; the identical rule
 * inline, 4 out of 4, whether placed first or last in the head.
 *
 * The copy below is kept deliberately. It costs nothing, it keeps the whole
 * mechanism readable in one place, and it covers any page that ever gets added
 * without the inline tag — but on its own it will not start a transition.
 *
 * DEGRADATION
 * Unsupported browsers ignore an unknown at-rule and unknown pseudo-elements
 * entirely, so they simply navigate as before. There is no scripted fallback
 * and none is wanted: this is decoration, and a JS shim for it would be a
 * liability on a site where the navigation already works.
 */

@view-transition {
  navigation: auto;
}

/* ── Persistent chrome ───────────────────────────────────────
   Anything named here is lifted out of the page snapshot and animated as its
   own element, so it holds position across the navigation instead of fading
   out with the content and back in on the other side.

   Every name must be unique within a document — a duplicate makes the browser
   abort the whole transition — hence `body >` on the header and footer. Both
   tags are reused inside cards and panels on some pages, and only the direct
   children of body are the site chrome. */

body > header  { view-transition-name: site-header; }
body > footer  { view-transition-name: site-footer; }
.animated-bg   { view-transition-name: site-backdrop; }
.floating-fb   { view-transition-name: site-social; }
#cyberfilesBot { view-transition-name: site-bot; }

/* ── Content ─────────────────────────────────────────────────
   The outgoing page leaves faster than the new one arrives, and they overlap:
   a straight swap of equal durations reads as a stall, whereas an early exit
   makes the navigation feel like it responded immediately. Distances are kept
   small deliberately — this fires on every single link, and anything that
   reads as a "slide" the first time reads as a delay by the tenth. */

::view-transition-old(root) {
  animation: jbPageOut 150ms cubic-bezier(0.4, 0, 1, 1) both;
}

::view-transition-new(root) {
  animation: jbPageIn 320ms cubic-bezier(0, 0.55, 0.25, 1) both;
}

@keyframes jbPageOut {
  to { opacity: 0; transform: translateY(-10px); }
}

@keyframes jbPageIn {
  from { opacity: 0; transform: translateY(14px); }
}

/* The backdrop is the same on both sides, so cross-fading it achieves nothing
   visible except a chance to flicker. Swap it outright. */
::view-transition-old(site-backdrop) { animation: none; opacity: 0; }
::view-transition-new(site-backdrop) { animation: none; opacity: 1; }

/* The chrome is near enough identical page to page; a short cross-fade covers
   the parts that do differ, such as which nav item is active or whether the
   header had picked up its scrolled state. */
::view-transition-old(site-header), ::view-transition-new(site-header),
::view-transition-old(site-footer), ::view-transition-new(site-footer),
::view-transition-old(site-social), ::view-transition-new(site-social),
::view-transition-old(site-bot),    ::view-transition-new(site-bot) {
  animation-duration: 180ms;
}

/* Stacking inside the transition overlay is independent of the page's own
   z-index, so the layering has to be restated here or the content snapshot
   would animate over the top of the header. */
::view-transition-group(site-backdrop) { z-index: 0; }
::view-transition-group(root)          { z-index: 1; }
::view-transition-group(site-footer)   { z-index: 2; }
::view-transition-group(site-header)   { z-index: 10; }
::view-transition-group(site-social),
::view-transition-group(site-bot)      { z-index: 20; }

/* Movement is the part that causes trouble, so it is the part that goes. The
   transition still runs and still prevents the white flash — it just cuts
   rather than animates. */
@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) {
    animation: none !important;
  }
}
