/* opo8 — structure only. one typeface, and nothing that is not the row.

   There are no colours written down here beyond the two triples below, and
   those are overwritten the moment a theme's picture has been measured. The
   page's palette comes out of whichever picture is behind it. */

:root {
  /* Two triples, and everything else is built out of them. js/ground.js writes
     both when a theme loads: --paper-rgb is the average of that theme's
     picture, --ink-rgb is a fixed distance from it. The values here are only
     what shows in the moment before the first picture has been measured. */
  --paper-rgb: 51 51 52;
  --ink-rgb:   233 233 234;

  --paper: rgb(var(--paper-rgb));
  --ink:   rgb(var(--ink-rgb));
  --gray:  rgb(var(--ink-rgb) / .55);
  --hair:  rgb(var(--ink-rgb) / .26);
  /* the control box: the page's own ground colour, solid */
  --panel: rgb(var(--paper-rgb));
  /* the slider, in two greys: what has been travelled, and what has not */
  --slot-fill:  rgb(var(--ink-rgb) / .62);
  --slot-empty: rgb(var(--ink-rgb) / .16);
  --head-hi:    #fbfbfb;
  --head-lo:    #b9b9bc;

  /* The one place the typeface is named — nav.js measures back off the element,
     so this line is the whole of it. Google's search box, verbatim: Arial, and
     the browser's own sans if it is somehow missing. Nothing to download, no
     licence, and every visitor sees what he sees. */
  --type:   arial, sans-serif;
  --weight: 700;
  /* How far the letters are pulled along x. 1 is the face as drawn. nav.js
     reads this off each element and widens that row by the same factor, or the
     words would be stretched on top of each other. The branch row is pulled
     less than the row above it: at 4 it ran off the right edge, since it is
     centred on `work` rather than on the window. */
  --stretch:       4;
  --stretch-sub:   3;
  --stretch-y:     2;     /* pulled up as well as out, so four times as wide is
                             still a letter and not a smear */
  --stretch-panel: 1.4;   /* the control box. its words are small; 4 is absurd */
  --size:  16px;
  --lead:  1.45;

}

* { box-sizing: border-box; }

html, body {
  margin: 0; min-height: 100%;
  font: var(--size)/var(--lead) var(--type);
  color: var(--ink);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
}

/* The bar is drawn by the operating system, in its own colours, pinned to the
   far right with nothing near it. The document scrolls just the same without it. */
html { scrollbar-width: none; -ms-overflow-style: none; }
html::-webkit-scrollbar { width: 0; height: 0; display: none; }

/* painted before anything else, so the page is never white for a frame */
html { background: var(--paper); }

/* The ground, on a layer of its own behind everything, so the canvas that
   glitches it can sit at the same depth and cover it exactly.

   The flat colour is under the picture, so a missing file is a plain dark page
   rather than a white one. `cover` keeps the picture whole and crops it to the
   window; `repeat` would tile it as a texture instead. */
body::before {
  content: '';
  position: fixed; inset: 0; z-index: -1;
  background: var(--ground-flat, #333334) var(--ground-img, url('backgrounds/bg1q3op2.png'))
              center / cover no-repeat;
}

/* js/ground.js puts a canvas here and redraws that same picture into it, live,
   tearing it under the cursor. It sits over body::before at the same depth — a
   later sibling at the same z-index — so if the script never runs, or the file
   will not load, the still picture underneath is what shows. */
#ground {
  position: fixed; inset: 0; z-index: -1;
  display: block; pointer-events: none;
}

body.blocked > *:not(#file-only) { display: none; }

/* ---------- the row ---------- */
/* Space Agnostic's: fixed, floating with no bar behind it, every word placed
   absolutely by nav.js. The container ignores the pointer so the gaps between
   words never sit on top of the page. */

#top {
  position: absolute; top: 34px; left: 0; width: 100%; min-height: 24px;
  display: flex; align-items: flex-start;
  padding: 0 26px;
  z-index: 1001; pointer-events: none;
}
#words { display: contents; }

/* the catch area: invisible, larger than the text, and it never moves */
.word {
  position: absolute; top: 0;
  padding: 24px 0; margin: -24px 0;
  color: var(--ink);
  font: var(--weight) 36px/1.2 var(--type);
  text-transform: lowercase;
  white-space: pre;
  background: none; border: 0;
  cursor: pointer; pointer-events: auto;
  user-select: none; -webkit-user-select: none;
  opacity: .6;
  transition: opacity .25s ease-out;
}
.word:hover { opacity: 1; }
.word[aria-current="true"] { opacity: 1; }

/* the text: the only thing that moves. their slide, put on hover.
   Pulled along x from its left edge — the same edge nav.js placed it at. */
.word .label {
  display: inline-block;
  position: relative; top: 0;
  /* --stretch is what this row is asked to be pulled to; --stretch-fit is what
     js/nav.js found room for. The fallback keeps the row right before, or
     without, any of that running. */
  transform: scaleX(var(--stretch-fit, var(--stretch))) scaleY(var(--stretch-y));
  transform-origin: left center;
  transition: top .5s ease-out;
}
.word:hover .label { top: 12px; }


/* ---------- the second row ---------- */
/* The same thing one size down, under the first. It is never removed from the
   page — only faded and made untouchable — so that it has somewhere to fade in
   from, and so its words are already measured and placed the instant their
   parent is clicked. */

/* It does not fade in, it grows out of its parent word. The origin is put on
   that word by nav.js, so scaling from almost nothing to full size reads as the
   row coming out of it — a window coming back out of the dock. Going away, it
   shrinks back into the same word. */
#subwords {
  position: absolute; top: 114px; left: 0; width: 100%; min-height: 22px;
  z-index: 1001; pointer-events: none;
  opacity: 0;
  transform: scale(.12);
  transform-origin: 50% -42px;          /* until nav.js measures the real word */
  /* One curve, both directions, because this one is its own mirror: reversing
     cubic-bezier(x1,y1,x2,y2) gives (1-x2,1-y2,1-x1,1-y1), and for .45,0,.55,1
     that is itself. So opening and closing are the same motion and there is no
     pair of curves to keep balanced against each other.

     The two asymmetric attempts each gave one direction the bad half: ease-out
     both ways dropped it to small at once and then crept, leaving the words
     readable at the tail; the mirrored ease-in held it at full size before
     going, which read as hesitation.

     The fade is shorter than the scale on purpose. It carries both ends, so
     neither the last of the shrink nor the first of the growth is spent
     watching very small words. */
  transition: opacity .2s ease,
              transform .34s cubic-bezier(.45, 0, .55, 1);
  will-change: transform;
}
#subwords.on { opacity: 1; transform: scale(1); }

.subword {
  --stretch: var(--stretch-sub);
  position: absolute; top: 0;
  padding: 12px 0; margin: -12px 0;
  color: var(--ink);
  font: var(--weight) 18px/1.2 var(--type);
  text-transform: lowercase;
  white-space: pre;
  background: none; border: 0;
  cursor: pointer;
  user-select: none; -webkit-user-select: none;
  opacity: .6;
  transition: opacity .25s ease-out;
}
.subword[hidden] { display: none; }
#subwords.on .subword { pointer-events: auto; }
.subword:hover { opacity: 1; }
.subword[aria-current="true"] { opacity: 1; }

.subword .label {
  display: inline-block;
  position: relative; top: 0;
  /* --stretch is what this row is asked to be pulled to; --stretch-fit is what
     js/nav.js found room for. The fallback keeps the row right before, or
     without, any of that running. */
  transform: scaleX(var(--stretch-fit, var(--stretch))) scaleY(var(--stretch-y));
  transform-origin: left center;
  transition: top .5s ease-out;
}
.subword:hover .label { top: 6px; }

/* ---------- the pages ---------- */
/* Three of them, empty. */

main { position: relative; }
/* a page with nothing on it still fills the screen; one with more grows */
.page { position: relative; min-height: 100vh; }
.page[hidden] { display: none; }

/* ---------- file:// notice ---------- */

#file-only { position: absolute; inset: 0; padding: 40px; max-width: 34rem; z-index: 2; }
#file-only pre { border: 1px solid var(--hair); padding: 8px; }

/* ---------- the ground's control panel ---------- */
/* Reached from `control panel` under home. It is furniture, not ground — it
   takes the pointer, and ground.js ignores any movement over it so the picture
   does not tear underneath it.

   Everything written in it is stretched the way the rows at the top are, so it
   reads as part of the same page rather than as a dialog that wandered in. */

#panel {
  /* where it starts. js/panel.js only writes left/top once it has been dragged,
     so this corner costs no measurement and cannot land on the row. */
  position: fixed; right: 26px; bottom: 26px; z-index: 1200;
  width: min(400px, calc(100vw - 52px));
  /* A solid box with no edge drawn on it. It was see-through with a hairline
     around it, which meant the ground kept moving underneath the controls. */
  background: var(--panel);
  color: var(--ink);
  font: var(--weight) 11px/1.4 var(--type);
  text-transform: lowercase;
  user-select: none; -webkit-user-select: none;
  cursor: grab;                  /* the box is picked up anywhere it is not a control */
}
#panel[hidden] { display: none; }
#panel.moving { cursor: grabbing; }

/* every word in the box, pulled along x from its own centre */
.pw {
  display: inline-block;
  transform: scaleX(var(--stretch-panel));
  transform-origin: center;
}

/* the grip. nothing written on it and no rule under it — the top edge is the
   handle, and that is all it is. */
.panel-bar {
  display: flex; justify-content: flex-end;
  padding: 6px 6px 0;
}

.panel-x {
  position: relative;
  width: 26px; height: 26px; padding: 0;
  background: none; border: 0; cursor: pointer;
  opacity: .55;
  transition: opacity .2s ease-out;
}
/* the two bars. drawn, not typed, so the stroke is a number and not whatever
   the typeface felt like giving the × character. */
.panel-x i {
  position: absolute; left: 50%; top: 50%;
  width: 15px; height: 2.5px; margin: -1.25px 0 0 -7.5px;
  background: var(--ink);
}
.panel-x i:first-child { transform: rotate(45deg); }
.panel-x i:last-child  { transform: rotate(-45deg); }
.panel-x:hover { opacity: 1; }
.panel-x:focus-visible { outline: 1px solid var(--ink); outline-offset: 1px; }

.panel-body {
  padding: 2px 16px 16px;
  /* The theme on the left, everything that was already here on the right. The
     box is anchored to the right of the window and grows leftward, so what is
     in the right-hand stack never moves when the box gets wider. */
  display: flex; align-items: flex-end; justify-content: space-between; gap: 18px;
}

/* ---------- the theme ---------- */
/* A preview of the picture the ground actually is, cropped `cover` the same way
   the ground crops it, so what is in the box is what is behind the page. */

.theme-slot { display: flex; flex-direction: column; align-items: center; gap: 9px; }

.theme-shot {
  width: 158px; height: 92px; padding: 0;
  border: 1px solid var(--hair);
  background: none;
  cursor: pointer;
  overflow: hidden;
  transition: border-color .18s ease-out, opacity .18s ease-out;
}
.shot-img {
  width: 100%; height: 100%;
  background-color: var(--ground-flat, #333334);
  background-position: center;
  background-size: cover;
  background-repeat: no-repeat;
}
.theme-shot:hover { border-color: var(--ink); }
.theme-shot:focus-visible { outline: 1px solid var(--ink); outline-offset: 3px; }
.theme-shot.turning { opacity: .45; }    /* while the next file is coming in */

.theme-name { opacity: .55; letter-spacing: .04em; }

/* the right-hand stack: the sliders and the two words, as they were */
.panel-stack { display: flex; flex-direction: column; align-items: flex-end; gap: 16px; }

/* ---------- a slider ---------- */
/* A track with a playhead on it, stood on end. The proportions are the ones in
   the parts sheet — a fat capsule and a round bevelled handle a little wider
   than it — with the blue swapped for the page's own grey.

   Two shades, and which is which is the whole point: the part already travelled
   is the light one, the part not reached yet is the dark one. */

/* packed to the left rather than spread across the box: two thin tracks with a
   whole panel's width between them read as two unrelated things. */
.rack {
  display: flex; justify-content: flex-end; align-items: flex-end;
  gap: 14px;
  padding: 2px 0 0;
}
/* A real width, because the stretch is a transform: it makes a word look wider
   without making it take any more room, so two of them side by side run
   straight through each other. The column is as wide as the widest label needs
   once it has been pulled out. */
.slot {
  width: 76px;
  display: flex; flex-direction: column; align-items: center; gap: 9px;
}
.slot-name { opacity: .55; letter-spacing: .04em; }
.slot-read { font-variant-numeric: tabular-nums; }

.track {
  position: relative;
  width: 7px; height: 94px;
  border-radius: 999px;
  background: var(--slot-empty);        /* not reached yet */
  /* the parts sheet's capsule is not flat — it sits very slightly in */
  box-shadow: inset 0 1px 2px rgba(0, 0, 0, .28);
  cursor: default;               /* no resize arrows; it is just a track */
  touch-action: none;
}
.track:focus-visible { outline: 1px solid var(--ink); outline-offset: 4px; }

/* the travelled part, growing up out of the bottom of the same capsule */
.fill {
  position: absolute; left: 0; right: 0; bottom: 0;
  border-radius: 999px;
  background: var(--slot-fill);
}

/* The handle. The bevel is the parts sheet's: lit from the top left, a hair
   darker at the bottom right, sitting just off the track on a soft shadow.
   Small while nothing is happening, and it comes up under the pointer — which
   is the one place a control this quiet can afford to answer back. */
.head {
  position: absolute; left: 50%;
  width: 17px; height: 17px; margin: 0 0 -8.5px -8.5px;
  border-radius: 50%;
  background: radial-gradient(circle at 32% 28%, var(--head-hi), var(--head-lo));
  box-shadow: 0 1px 3px rgba(0, 0, 0, .45);
  pointer-events: none;
  transform: scale(.6);
  transition: transform .18s ease-out, box-shadow .18s ease-out;
}
.track:hover .head, .track.held .head {
  transform: scale(1);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .55);
}

/* ---------- the two words ---------- */
/* Pressable words, like the row at the top of the page. No border, no box. */

.panel-row { display: flex; justify-content: flex-end; gap: 26px; padding-top: 2px; }
.panel-word {
  padding: 0; background: none; border: 0; cursor: pointer;
  color: var(--ink); font: inherit; text-transform: lowercase;
  opacity: .55;
  transition: opacity .2s ease-out;
}
.panel-word:hover, .panel-word.hit { opacity: 1; }
.panel-word[aria-pressed="false"] { opacity: .28; }
.panel-word:focus-visible { outline: 1px solid var(--ink); outline-offset: 3px; }

@media (prefers-reduced-motion: reduce) {
  .panel-word, .panel-x, .head { transition: none; }
}

/* ---------- about: the bio ---------- */
/* The one page with anything on it. It scrolls itself rather than the body, so
   the rows above stay put and every other page keeps its fixed frame. */



.bio {
  /* clear of the top row, which is twice as tall as its box says it is */
  padding: 150px 34px 16vh;
  max-width: 34rem;
  margin: 0 auto;          /* the block sits in the middle of the page */
}

/* the label over the picture, in the branch row's treatment so it belongs to
   the same page — pulled out and up, just smaller */
.bio-name {
  margin: 0 0 14px;
  font: var(--weight) 18px/1.2 var(--type);
  text-transform: lowercase;
  color: var(--ink);
}
.bio-name .s {
  display: inline-block;
  transform: scaleX(var(--stretch-sub)) scaleY(var(--stretch-y));
  transform-origin: left center;
}

.bio-shot { margin: 0; max-width: min(340px, 100%); }
.bio-shot img { display: block; width: 100%; height: auto; }

/* Not stretched. The rows can be four times as wide because they are three
   words; a paragraph pulled out like that is unreadable, and this is the one
   place on the site meant to actually be read. */
.bio-text {
  margin: 26px 0 0;
  font: var(--weight) 13px/1.75 var(--type);
  color: var(--ink);
  text-transform: lowercase;
  max-width: 34rem;
}

/* editing the statement, where serve.py can write: the words become a box in
   the same type, and one pressable word under it */
.bio-edit {
  display: block; width: 100%; margin: 26px 0 0; padding: 0;
  font: var(--weight) 13px/1.75 var(--type);
  color: var(--ink); background: none;
  border: 0; outline: 1px solid var(--hair); outline-offset: 6px;
  resize: none; overflow: hidden;
}
.bio-edit:focus { outline-color: var(--ink); }
.bio-edit-toggle {
  display: block; margin: 20px 0 0; padding: 0;
  background: none; border: 0; cursor: pointer;
  font: var(--weight) 13px/1.45 var(--type);
  color: var(--ink); text-transform: lowercase;
  opacity: .6; transition: opacity .25s ease-out;
}
.bio-edit-toggle:hover, .bio-edit-toggle[aria-pressed="true"] { opacity: 1; }

/* ---------- work: video, sculpture, image, class ---------- */
/* Iteration two's cover flow. The stage takes the screen below the rows, and
   the name of whatever is in front sits under it. */

.flow {
  position: absolute; inset: 150px 0 150px 0;
  perspective: 1100px;
  perspective-origin: 50% 46%;
  overflow: hidden;
  cursor: grab;
  /* the reel is dragged, so the gesture has to reach it rather than being
     taken by the browser for a scroll */
  touch-action: none;
}
/* the rail is a container and nothing else: a full-size box at z=0 with the
   covers pushed to negative z behind it, so it must not take the pointer */
.rail {
  position: absolute; inset: 0;
  transform-style: preserve-3d;
  pointer-events: none;
}

.cover {
  position: absolute; left: 50%; top: 46%;
  width: var(--cover-w, 340px); height: var(--cover-h, 340px);
  margin: 0; padding: 0;
  transform-style: preserve-3d;
  will-change: transform, opacity;
  cursor: pointer; pointer-events: auto;
}
.cover .face {
  position: relative;
  width: 100%; height: 100%;
  background: rgb(var(--paper-rgb));
  overflow: hidden;
}

/* The iPod tell. It was -webkit-box-reflect, which Firefox does not have in any
   form, so the reel had no reflection there at all. This is a canvas of the
   cover's own frame, caught once by js/reel.js, mirrored and faded — the same
   in every engine rather than one of them getting the idea and the rest not. */
.cover .glass {
  position: absolute; left: 0; top: 100%;
  width: 100%; height: 62%;
  overflow: hidden;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to bottom, rgb(0 0 0 / .5), transparent 82%);
          mask-image: linear-gradient(to bottom, rgb(0 0 0 / .5), transparent 82%);
}
.cover .glass canvas {
  display: block; width: 100%;
  transform: scaleY(-1);
  transform-origin: top;
  opacity: .34;
}
.cover video { width: 100%; height: 100%; object-fit: contain; display: block; }

/* the name of the one in front, and nothing else on the page */
.reel-name {
  position: absolute; inset: auto 0 62px 0;
  margin: 0; text-align: center;
  font: var(--weight) 14px/1.2 var(--type);
  color: var(--ink);
  text-transform: lowercase;
  pointer-events: none;
}
.reel-name .s {
  display: inline-block;
  transform: scaleX(var(--stretch-sub)) scaleY(var(--stretch-y));
  transform-origin: center;
}

/* ---------- image: the map ---------- */
/* A solid grid: rows that line up, and the joint between cells is the ground
   showing through. The hairline matters — several of these files are almost
   entirely black, and without an edge they read as holes rather than as pieces.

   Nothing here transitions. Opacity is written inline every frame by map.js,
   for the arrival and for the step back while one is open, and a transition on
   top of that would fight it. */

/* In the page's flow, and as tall as its rows: map.js sets the height, so the
   page scrolls exactly as far as there is work to see. */
.map {
  position: relative; margin-top: 150px;
  overflow: hidden;
  opacity: 0;
  transition: opacity 700ms ease-out;
}
.map.on { opacity: 1; }

.piece {
  position: absolute; top: 0; left: 0;
  margin: 0; padding: 0;
  border: 1px solid var(--hair);
  background: rgb(var(--paper-rgb));
  overflow: hidden;
  cursor: pointer;
  will-change: transform, opacity;   /* the only two things that ever animate */
  transform-origin: 50% 50%;
  transition: border-color 160ms ease-out;
}
.piece img { width: 100%; height: 100%; display: block; object-fit: contain; }
.map[data-fit="cover"] .piece img { object-fit: cover; }
.piece:hover { border-color: var(--gray); }

/* Opened, a piece is shown whole and nothing frames it. The element stays
   cell-shaped and is scaled to cover the size asked for, so it is larger than
   the picture on one axis — the border and the ground would show as a slab
   down one side of it.

   That surround is invisible but it is still the element, so it is taken out of
   the hit test and the picture is put back into it. Otherwise the empty air
   beside an open picture counts as the picture, and clicking what looks like
   the ground does not close it. */
.map .piece.open {
  border-color: transparent; background: none;
  display: flex; align-items: center; justify-content: center;
  pointer-events: none;
}
.map .piece.open img { object-fit: contain; pointer-events: auto; }

/* ---------- the playback window ---------- */
/* Pressing a cover opens the file itself over the page. It grows out of the
   cover it came from — js/reel.js puts the origin there — on the same curve the
   branch rows use, so the whole site opens things the one way. */

#player {
  position: fixed; inset: 0; z-index: 1400;
  display: flex; align-items: center; justify-content: center;
  /* no ground behind it: the page shows through, because the thing you opened
     is the only thing worth adding */
  opacity: 0;
  transform: scale(.12);
  transition: opacity .2s ease,
              transform .34s cubic-bezier(.45, 0, .55, 1);
}
#player.on { opacity: 1; transform: scale(1); }
#player[hidden] { display: none; }

.player-box {
  display: flex; flex-direction: column; align-items: center; gap: 14px;
  width: 100%; padding: 0 34px;
}
#player video, #player img {
  display: block;
  max-width: min(1200px, calc(100vw - 192px));   /* clear of the arrows */
  max-height: 76vh;
  width: auto; height: auto;
}

.player-name {
  margin: 0;
  font: var(--weight) 14px/1.2 var(--type);
  color: var(--ink);
  text-transform: lowercase;
}
.player-name .s {
  display: inline-block;
  transform: scaleX(var(--stretch-sub)) scaleY(var(--stretch-y));
  transform-origin: center;
}

/* the same two drawn bars the control panel closes with */
.player-x {
  position: absolute; top: 26px; right: 26px;
  width: 30px; height: 30px; padding: 0;
  background: none; border: 0; cursor: pointer;
  opacity: .55;
  transition: opacity .2s ease-out;
}
.player-x i {
  position: absolute; left: 50%; top: 50%;
  width: 17px; height: 2.5px; margin: -1.25px 0 0 -8.5px;
  background: var(--ink);
}
.player-x i:first-child { transform: rotate(45deg); }
.player-x i:last-child  { transform: rotate(-45deg); }
.player-x:hover { opacity: 1; }

/* the arrows either side of an opened picture: the same drawn bars, meeting */
.player-prev, .player-next, .node-prev, .node-next {
  position: absolute; top: 50%;
  width: 48px; height: 48px; margin-top: -24px; padding: 0;
  background: none; border: 0; cursor: pointer;
  opacity: .55;
  transition: opacity .2s ease-out;
}
.player-prev { left: 26px; }
.player-next { right: 26px; transform: scaleX(-1); }
.player-prev i, .player-next i, .node-prev i, .node-next i {
  position: absolute; left: 16px; top: 50%;
  width: 19px; height: 2.5px; margin-top: -1.25px;
  background: var(--ink);
  transform-origin: 1.25px 50%;
}
.player-prev i:first-child, .player-next i:first-child,
.node-prev i:first-child, .node-next i:first-child { transform: rotate(-45deg); }
.player-prev i:last-child, .player-next i:last-child,
.node-prev i:last-child, .node-next i:last-child { transform: rotate(45deg); }
.player-prev:hover, .player-next:hover, .node-prev:hover, .node-next:hover { opacity: 1; }
.player-prev[hidden], .player-next[hidden] { display: none; }
@media (max-width: 600px) {
  .player-prev { left: 0; }
  .player-next { right: 0; }
  #player video { max-width: calc(100vw - 88px); }
}

/* Looking closer, for an enlarged picture anywhere on the site (js/zoom.js).
   The frame clips; the picture scales inside it around the pointer. */
.zoom-frame { display: block; overflow: hidden; line-height: 0; }
.zoom-frame > img { cursor: zoom-in; }
.zoom-frame.zoomed > img, .body.zoomed > img {
  transform: scale(2.5);           /* ZOOM in js/zoom.js */
  cursor: zoom-out;
}
.zoom-frame > img, .node .body > img { transition: transform .25s ease-out; }
.body.zoomed { overflow: hidden; }

/* a picture is sized by player.js to the window, not capped like a video */
#player img.fit { max-width: none; max-height: none; }

/* adding pictures to image: said in the middle of the window while files are
   held over the page, in the branch row's treatment, and nothing else drawn */
.image-drop {
  position: fixed; inset: 0; z-index: 1300;
  display: grid; place-items: center; margin: 0;
  pointer-events: none;
  font: var(--weight) 18px/1.2 var(--type);
  color: var(--ink);
  text-transform: lowercase;
}
.image-drop[hidden] { display: none; }
.image-drop .s {
  display: inline-block;
  transform: scaleX(var(--stretch-sub)) scaleY(var(--stretch-y));
}

@media (prefers-reduced-motion: reduce) {
  #player { transition: opacity .2s ease; transform: none; }
  #player.on { transform: none; }
  .player-x, .player-prev, .player-next { transition: none; }
}
