/* Design explorations: a wall of screens that moves on its own.

   The page is the wall. Once the script has laid it out it fills the viewport
   and nothing scrolls -- the columns carry the work past you instead, each at
   its own pace and in its own direction, so the grid never settles into a
   pattern you can read as a loop. Put the pointer anywhere on it and it waits.

   Every column holds its shots twice over and travels exactly half its own
   height, which lands the second copy where the first began: the seam falls on
   an identical frame and there is nothing to see.

   Before the script runs -- and if it never does -- the same shots are simply
   a grid you scroll, which is why they are in the markup rather than in a
   list of filenames somewhere. */

body.ex-doc{background:#fff;color:var(--ink)}
body.ex-doc.ex-live{overflow:hidden}   /* the wall moves; the page does not */

/* ---- the wall ---- */
.ex-wall{
  display:grid;
  grid-template-columns:repeat(var(--cols,5),1fr);
  gap:var(--gap,18px);
  padding:96px var(--gap,18px) var(--gap,18px);
  align-items:start;
}
.ex-wall.ex-ready{
  position:fixed;inset:0;
  padding:var(--gap,18px);
  overflow:hidden;
  align-items:stretch;
}
.ex-col{overflow:hidden}

/* The travelling strip: two identical runs stacked, shifted by half.

   The space between shots is a trailing margin rather than a flex gap. A gap
   falls only *between* children, so a strip of two copies is one gap short of
   twice a copy, and half of it lands half a gap above where the second copy
   starts -- a small jump on every lap. Carried as a margin the spacing belongs
   to each shot, the strip is exactly twice one copy, and half of it is exactly
   one copy. The last margin hangs off the bottom where it is clipped. */
.ex-run{
  display:flex;flex-direction:column;
  will-change:transform;
  animation:ex-up var(--dur,70s) linear infinite;
}
.ex-run .ex-shot{margin-bottom:var(--gap,18px)}
.ex-col:nth-child(even) .ex-run{animation-name:ex-down}
/* a different pace per column, so the columns never line up */
.ex-col:nth-child(1) .ex-run{--dur:74s}
.ex-col:nth-child(2) .ex-run{--dur:92s}
.ex-col:nth-child(3) .ex-run{--dur:64s}
.ex-col:nth-child(4) .ex-run{--dur:84s}
.ex-col:nth-child(5) .ex-run{--dur:70s}

@keyframes ex-up{  from{transform:translate3d(0,0,0)}   to{transform:translate3d(0,-50%,0)} }
@keyframes ex-down{from{transform:translate3d(0,-50%,0)} to{transform:translate3d(0,0,0)} }

/* The wall does not stop for the pointer. It covers the whole viewport, so
   anywhere you put the cursor is over it, and stopping on hover meant it was
   forever halting under a pointer that was only on its way somewhere -- which
   made the thing impossible to simply watch.

   Keyboard focus is different: tab to a shot and it holds, because a target
   that slides out from under the key you are about to press is no target.
   Deliberately :focus-visible rather than :focus-within -- focus also lands on
   a picture when one is closed again, and holding for that would stop the wall
   the first time anybody looked at anything. */
.ex-wall:has(:focus-visible) .ex-run{animation-play-state:paused}
@supports not (selector(:has(*))){
.ex-wall:focus-within .ex-run{animation-play-state:paused}
}

/* ---- the way it arrives ----
   The wall does not simply exist when you get here: the shots rise into place
   on a diagonal, nearest corner first, and the drift is held until they have
   all landed so nothing slides away while it is still arriving. Delays are
   worked out from where a shot actually sits, so what you can see comes in in
   the order you would read it and everything off screen is already home. */
.ex-wall.ex-enter .ex-shot{opacity:0;transform:translateY(22px) scale(.96)}
.ex-wall.ex-enter.ex-go .ex-shot{
  opacity:1;transform:none;
  transition:opacity .7s var(--ease) var(--d,0s),transform .9s var(--ease) var(--d,0s);
}
.ex-wall.ex-hold .ex-run{animation-play-state:paused}

/* A picture that has not arrived yet does not snap in at full strength. The
   rule is scoped to the laid-out wall, so if the script never runs the shots
   are simply visible. */
.ex-wall.ex-ready .ex-shot img{opacity:0;transition:opacity .55s var(--ease)}
.ex-wall.ex-ready .ex-shot img.ok{opacity:1}

/* ---- a single shot ---- */
.ex-shot{
  position:relative;margin:0;border-radius:12px;overflow:hidden;
  background:#f4f4f6;
  /* a hairline ring so a pale screenshot still has an edge on a white page */
  box-shadow:0 0 0 1px rgba(0,0,0,.05),0 10px 28px rgba(0,0,0,.08);
  transition:transform .5s var(--ease),box-shadow .5s var(--ease);
}
.ex-shot img{
  display:block;width:100%;height:auto;
  /* a full-page capture would otherwise run for thousands of pixels */
  max-height:var(--cap,760px);object-fit:cover;object-position:50% 0;
}
.ex-shot:hover{transform:translateY(-4px);box-shadow:0 0 0 1px rgba(0,0,0,.07),0 18px 44px rgba(0,0,0,.13)}
.ex-shot .ex-cap{
  position:absolute;left:0;right:0;bottom:0;
  padding:26px 14px 11px;
  font-family:'Poppins',sans-serif;font-size:11.5px;line-height:1.35;
  color:var(--ink2);
  background:linear-gradient(to top,rgba(255,255,255,.97),rgba(255,255,255,.82) 55%,rgba(255,255,255,0));
  opacity:0;transform:translateY(5px);
  transition:opacity .4s var(--ease),transform .4s var(--ease);
}
.ex-shot:hover .ex-cap{opacity:1;transform:none}

/* ---- the bit of chrome over the top ---- */
.ex-top{
  position:fixed;top:0;left:0;right:0;z-index:10;
  display:flex;align-items:center;justify-content:space-between;gap:18px;
  padding:18px clamp(16px,3vw,30px) 42px;
  pointer-events:none;                 /* the wall underneath stays reachable */
  background:linear-gradient(to bottom,rgba(255,255,255,.97) 10%,rgba(255,255,255,.78) 45%,rgba(255,255,255,0));
}
.ex-top > *{pointer-events:auto}
.ex-back{
  display:inline-flex;align-items:center;gap:8px;
  font-family:'Poppins',sans-serif;font-size:13px;color:var(--ink2);
  text-decoration:none;padding:8px 14px 8px 11px;border-radius:999px;
  /* solid rather than tinted: the wall moves behind it, and a translucent
     pill disappears the moment a pale screenshot drifts past */
  border:1px solid rgba(0,0,0,.10);background:#fff;
  box-shadow:0 6px 18px rgba(0,0,0,.10);
  transition:box-shadow .3s var(--ease),border-color .3s var(--ease),color .3s var(--ease);
}
.ex-back:hover{color:var(--purple);border-color:rgba(125,0,212,.34);box-shadow:0 8px 24px rgba(0,0,0,.14)}
.ex-back svg{flex:none}
.ex-title{font-family:'Poppins',sans-serif;text-align:right}
.ex-title b{display:block;font-size:14px;font-weight:600;letter-spacing:-.01em;color:var(--ink)}
.ex-title span{display:block;margin-top:2px;font-size:11.5px;color:var(--gray)}

/* ---- phones and tablets ---- */
@media(max-width:1200px){.ex-wall{--cols:4}}
@media(max-width:900px){.ex-wall{--cols:3;--gap:14px;--cap:620px}}
@media(max-width:640px){
.ex-wall{--cols:2;--gap:10px;--cap:460px}
.ex-shot{border-radius:9px}
/* a tap has no hover to reveal a caption, so leave them showing */
.ex-shot .ex-cap{font-size:10.5px;padding:20px 10px 9px;opacity:1;transform:none}
.ex-title span{display:none}
}

/* ---- less motion ---- */
@media(prefers-reduced-motion:reduce){
body.ex-doc.ex-live{overflow:auto}
.ex-wall.ex-ready{position:static;inset:auto;padding:96px var(--gap,18px) var(--gap,18px);align-items:start}
.ex-run{animation:none}
.ex-run .ex-dup{display:none}          /* one copy is enough once nothing moves */
.ex-wall.ex-enter .ex-shot,.ex-wall.ex-enter.ex-go .ex-shot{opacity:1;transform:none;transition:none}
.ex-wall.ex-ready .ex-shot img{opacity:1;transition:none}
.ex-shot,.ex-shot .ex-cap{transition:none}
.ex-shot .ex-cap{opacity:1;transform:none}
}
