/* ============================================================================
   REVIEW STAGE
   The fitted picture, its zoom/pan frame and the layer of marks over it.
   Written by components/reviewStage.js and by nothing else.

   THE ONLY STYLESHEET LINKED BY BOTH DOCUMENTS. §13 of 40-design.md says the
   room never links review.css and the public page never links main.css; a
   shared third file is the only shape that keeps both halves of that true, and
   it exists because the geometry below is a contract rather than a look. It is
   linked BEFORE videoPlayer.css in both, so the player's rules stay last.

   The four declarations that carry the fraction contract are marked in place.
   pin_x/pin_y are fractions of .review-stage-frame, and they only mean anything
   while that element is the fitted picture itself.
   ============================================================================ */

/* The box the picture is fitted into. padding is LOAD-BEARING, not spacing:
   STAGE_PAD in components/reviewStage.js is the same 16 and fitFrame subtracts
   it on both axes. The two are duplicated on purpose -- reading it back with
   getComputedStyle would be a layout read on every fit -- so they move
   together or the frame is fitted against a box it does not have. */
.review-stage {
  position: relative;
  overflow: hidden;
  flex: 1;
  min-height: 0;
  padding: 16px;
  /* STATED HERE rather than inherited from a global reset. Both documents do
     set `* { box-sizing: border-box }` today -- main.css and review.css each
     open with it -- so this changes nothing either place. It is here because
     the padding above is arithmetic: under content-box, clientWidth would
     include the padding, fitFrame would subtract it a second time, and the
     picture would be fitted to a box 32px larger than the one it is drawn in.
     A geometry contract must not rest on a rule from a stylesheet this one
     does not own. */
  box-sizing: border-box;
}

/* The one node that zooms. Everything inside it -- the media, the marks, and
   whatever the caller appended -- rides the same transform, which is the whole
   point of it being a node rather than a class on the media.
   CONTRACT: left/top/width/height are written in pixels from JS (layoutZoom),
   computed from the file's own dimensions. Nothing here may size it. */
.review-stage-frame {
  position: absolute;
  transform-origin: 0 0;
  will-change: transform;
}

.review-stage-frame.is-zoomed {
  cursor: grab;
}

.review-stage-frame.is-panning {
  cursor: grabbing;
}

/* Fills the frame exactly, because the FRAME is what carries the fitted size
   now -- computed from the file's own width and height, not from max-* letting
   the browser work it out. No object-fit on purpose: the frame already has the
   picture's proportions, so `contain` could only ever hide an arithmetic error
   by letterboxing it away instead of showing it.
   CONTRACT: this is what makes the frame's box and the picture's box the same
   box, and therefore what makes a fraction of one a fraction of the other. */
.review-stage img,
.review-stage video {
  width: 100%;
  height: 100%;
  display: block;
}

/* Belt to the preventDefault in pointerdown, not a replacement for it. A drag
   held over a picture otherwise starts the browser's own file drag and steals
   the pointer mid-pan; it also selects the media as if it were text. Neither
   line here is sufficient alone -- -webkit-user-drag is not a standard and
   Firefox ignores it, which is why the <img> also carries draggable="false". */
.review-stage-frame img,
.review-stage-frame video {
  -webkit-user-select: none;
  user-select: none;
  -webkit-user-drag: none;
}

/* WHERE THE MARKS LIVE. A sibling of the media inside .review-stage-frame, so
   it inherits the fitted size and the zoom transform and a mark at 40%/60%
   stays at 40%/60% of the picture at every scale. inset: 0 rather than
   width/height: 100%, because the frame's own size is set in JS and the layer
   must follow it without being told twice.
   CONTRACT: inset: 0 inside the frame. */
.review-stage-pins {
  position: absolute;
  inset: 0;
  /* NEVER A CLICK TARGET, and not only in the placing mode: the layer covers
     the whole picture, and a transparent box over the media would swallow every
     click meant for the frame -- including the one that places the next mark. */
  pointer-events: none;
}

/* .k-pin itself comes from krgxion.css and is NOT redrawn here -- the shape,
   the shadow and the two colours are the brandbook's. Only what the brandbook
   cannot know is set: where this instance sits, and that it does not take
   clicks. translateY(-100%) is what lands the drop's sharp bottom-left corner
   ON the point, instead of hanging the whole shape below it.
   CONTRACT: the translate is the difference between the mark pointing at the
   spot and the mark sitting under it. */
.review-stage-pins .k-pin {
  transform: translateY(-100%);
  pointer-events: none;
}

/* The one being placed, before it is sent. Dashed, so "not saved yet" is
   visible without a second colour -- the two colours are taken by authorship
   and a third would be a new thing to learn. */
.review-stage-pins .k-pin.is-pending {
  outline: 1px dashed rgba(255, 255, 255, 0.7);
  outline-offset: 2px;
}

/* The crosshair says the click will land on the picture rather than do nothing.
   On the stage and not on the frame: the cursor should change over the whole
   area being aimed at. */
.review-stage.is-pinning,
.review-stage.is-pinning .review-stage-frame {
  cursor: crosshair;
}
