/* ==========================================================================
   path-gallery.css — the curve gallery's shell.

   Almost nothing lives here: the whole scene is one matrix3d written by
   js/path-gallery.js every frame, and every plane's place in it is another.
   What CSS owns is the lens and the ground.
   ========================================================================== */

.pathg{
  position: fixed; inset: 0;
  overflow: hidden;
  /* THE SITE'S OWN WHITE, the ground this page has always had. The fog is an
     opacity fade rather than a black wash, so it resolves to whatever is
     behind — which means the same code fades photographs INTO the paper here
     and would fade them into black on a dark page, with nothing to change. */
  background: var(--c-white, #fff);
  perspective: var(--pg-persp, 900px);
  perspective-origin: 50% 50%;
}

/* THE SCENE. Its transform is the VIEW MATRIX — the inverse of where the camera
   is — so moving the camera forward moves this backward, which is what a camera
   is in a compositor that has none. preserve-3d on both this and the planes, or
   the depth is flattened one level down and everything lands on one sheet. */
.pathg__world{
  position: absolute; left: 50%; top: 50%;
  transform-style: preserve-3d;
  will-change: transform;
}

/* PINNED, BUT ONLY THE ONES ON SCREEN — and the order of those two clauses is
   the whole fix. See the long note below for what happened when it was pinned
   unconditionally, and js/path-gallery.js's park() for what changed.

   WHY PIN AT ALL. A plane flying past changes its screen SIZE every frame, and
   an unpinned layer is re-rastered every time that size moves — a photograph
   right next to the camera is a megapixel of raster redone sixty times a
   second. Miss the deadline and the compositor shows the tile it has, which is
   nothing: the picture blinks out for a frame. The ones that blink are always
   the big near ones, because those are the expensive rasters. `will-change`
   tells Chrome to raster once and transform that, which is exactly the trade a
   photograph rushing past wants — it may be a shade soft at the last moment,
   and it is there.

   WHY IT IS SAFE NOW AND WAS NOT BEFORE. Parked planes are `visibility:hidden`
   and hidden elements are not composited, so this reaches the hundred or so
   being drawn instead of all three hundred and twenty. Measured, at count 320:
   126 layers / 70.7MB unpinned, 122 / 73.5MB pinned. Pinning the PICTURE as
   well doubles it to 240 layers, which is what the note below is about, so
   that one stays off. */
.pathg.is-pinned .pathg__it{ will-change: transform; }

/* NO UNCONDITIONAL will-change HERE, AND THAT WAS HALF THE FLICKER.

   `will-change` is a promise to keep a compositor layer alive, and there are
   THREE HUNDRED AND TWENTY of these. With it on the plane and on the picture
   inside it, this page asked Chrome for 645 layers and about 156MB of layer
   memory; past its budget the compositor starts evicting and re-rasterising
   tiles, and what that looks like is the whole field vanishing on alternate
   frames — one frame drawn, one frame blank, at 30Hz, while the copy and the
   bar (which are not in this scene) stay perfectly still. Measured: 645 layers
   with it, 326 without, and 125 once the faded planes are hidden as well.

   Nothing is lost by dropping it. These planes carry a 3D transform inside a
   preserve-3d scene, so the compositor promotes the ones it is actually
   drawing anyway — `will-change` only pinned the other two hundred as well.
   It is the one property whose documentation says outright not to apply it to
   large numbers of elements, and this is what that warning is about. */
.pathg__it{
  position: absolute; left: 0; top: 0;
  transform-style: preserve-3d;
  backface-visibility: visible;
}
/* The filter lives on the ITEM now, not here: it is a function of how far away
   the photograph is, and js/path-gallery.js writes it per plane per frame.

   The CLIP lives on the item too, and that is what makes the parallax possible:
   the shape belongs to the window, so the photograph can move underneath it
   without dragging its own outline along. Put the shape on the picture and a
   circular frame becomes a circle wandering about inside a square. */
.pathg__it img{
  display: block; width: 100%; height: 100%;
  object-fit: cover;
  /* ROUNDED WHILE SCROLLING. js/path-gallery.js writes one percentage on the
     root for the whole field; the same variable name the work page uses for the
     same idea, on its own root. On the IMAGE rather than on the plane because
     the plane also carries the shape clip-path — the two then compose, a
     rounded photograph inside whatever silhouette the shape gives it, instead
     of one silently winning. */
  border-radius: var(--card-radius, 0%);
  /* Drawn LARGER than its window — js/path-gallery.js scales it — and the
     surplus is the room it has to travel. transform-origin stays central so the
     surplus is even on all four sides and the clamp is one number per axis. */
  /* And no will-change on this one either — see the note on .pathg__it. This
     was the second layer per plane and half of the 645. */
}

/* THE BLOB, the same one the home page's white block runs — js/work-cursor.js,
   unchanged, hosted here because a blend composites against its own stacking
   context's backdrop and .pathg is what paints this page's paper.

   z-index 0 puts it under the photographs: it is the ground the gallery floats
   over, which is the arrangement it has on the index page too. */
body[data-page="gallery"] .work-blobs{ z-index: 0; }
.pathg__world{ z-index: 1; }


/* ---------------- the track switch ----------------------------------------
   Figma 14680:26000: 184x16, black, at x10 y259, with `back` 4px in from the
   left edge and `next` 4px in from the right.

   ITS X AND Y ARE VARIABLES, not literals, because 259 is an offset inside the
   file's own frame and the one thing certain about it is that it will want
   moving once it is on a real screen next to real photographs. js/track-switch.js
   owns both, so they are one console row each rather than an edit and a deploy. */
.trk{
  position: absolute; z-index: 44;
  left: var(--trk-x, 10px);
  top: var(--trk-y, 259px);
  width: 184px; height: 16px;
  background: #000;
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 4px;
  /* The gallery's own layer is below and takes the wheel; this must not. */
  pointer-events: auto;
}
.trk__btn{
  appearance: none; -webkit-appearance: none;
  background: none; border: 0; margin: 0; padding: 0;
  font: inherit; color: #fff;
  font-size: var(--ui-10); letter-spacing: var(--ui-track, -0.4px); line-height: 1;
  text-transform: uppercase;
  cursor: pointer;
}
/* THE RED IS THE SITE'S, not a second red. Everything that answers to a press
   on this site goes to --ui-red on hover, and a switch that went to some other
   colour would read as belonging to something else. */
.trk__btn:hover{ color: var(--ui-red, #e8271c); }
.trk__btn:focus-visible{ outline: 1px solid var(--ui-red, #e8271c); outline-offset: 2px; }
/* WHILE IT IS SWITCHING the presses are ignored — see js/track-switch.js — so
   the bar says so rather than looking pressable and doing nothing. */
.trk.is-busy .trk__btn{ color: rgba(255,255,255,.35); cursor: default; }
