/* ==========================================================================
   Workflow Studio — showreel
   compare.css
   The before / after under the self-hosting claim.
   ========================================================================== */

/* The wipe position, as a fraction of the frame's width. Registered so it can
   be animated: to the animation engine an unregistered custom property is an
   opaque token, and the sweep below would step between its keyframes instead
   of interpolating across them. Inherited, so the clip and the seam are
   reading one value rather than being kept in step. */
@property --pos {
  syntax: '<percentage>';
  inherits: true;
  initial-value: 50%;
}

/* --- The frame -------------------------------------------------------------
   It sits under the claim it is the evidence for: the note says no plate
   leaves your network, and this is the plate. Centred on the same axis as the
   line above it, and measured against that line rather than against the
   viewport.

   3/2 is the plates' own ratio, which is what makes object-fit: cover show the
   whole frame instead of a band cut out of the middle of it. Clipped rather
   than sized: a narrowing box would scale the frame inside it and the two
   shots would stop lining up under the seam, which is the one thing a
   comparison has to get right.
   -------------------------------------------------------------------------- */

.compare {
  position: relative;
  width: min(560px, 100%);
  margin: 34px auto 0;
  aspect-ratio: 3 / 2;
  overflow: hidden;
  border-radius: var(--radius);
  border: 1px solid var(--border);
  background: var(--surface-raised);
}

/* The reveal's rise is dropped here and only here: the sweep runs a clipped
   edge and a seam across this box, and doing that inside an element that is
   itself being moved a sub-pixel at a time leaves the two disagreeing by a
   fraction of a pixel every frame, which is what shimmers at the seam. The
   fade is kept — it is the arrival, and it costs nothing geometric. */
.compare.reveal {
  transform: none;
}

/* The reveal hides until something observes it, and without a script nothing
   ever does. The frame is the evidence for the claim above it, so it is
   legible with JS off — where it simply sits at its 50% split. */
html:not(.js) .compare.reveal {
  opacity: 1;
}

.compare.is-draggable {
  cursor: ew-resize;
  /* The drag owns the horizontal gesture once it is armed. */
  touch-action: none;
}

.compare img {
  position: absolute;
  inset: 0;
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  user-select: none;
  -webkit-user-drag: none;
}

/* The after is the right-hand side, so --pos is the seam and everything past
   it is the treated frame — the labels below read the same way round. */
.compare-after {
  position: absolute;
  inset: 0;
  clip-path: inset(0 0 0 var(--pos));
}

/* Moved with a transform rather than with `left`, which is the whole reason
   the box is full width and the line is its left border: `left` is a layout
   property, so the wipe would relayout the seam on every frame and it ends up
   a sub-pixel out of step with the clip edge it is supposed to be sitting on.
   A percentage translateX resolves against the element's own width, so 100%
   here is exactly the frame's width and the seam lands on the clip.

   drop-shadow and not box-shadow for the same reason: a box-shadow would
   outline all 100% of the box, a filter follows the pixels actually painted. */
.compare-seam {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 0;
  width: 100%;
  border-left: 1px solid var(--accent);
  filter: drop-shadow(0 0 8px rgba(216, 181, 111, 0.55));
  transform: translateX(var(--pos));
  pointer-events: none;
}

/* Pinned to the border and not to the middle of the box — the box is the whole
   frame now, and only its left edge is the seam. Only drawn where the seam can
   actually be taken hold of: on touch it is a line the sweep left behind, and
   a handle that does nothing is a lie. */
.compare-seam::after {
  content: '';
  position: absolute;
  top: 50%;
  left: 0;
  width: 28px;
  height: 28px;
  transform: translate(-50%, -50%);
  border: 1px solid var(--accent);
  border-radius: 50%;
  background: rgba(15, 15, 15, 0.5);
  opacity: 0;
  transition: opacity 0.4s ease;
}

.compare.is-draggable .compare-seam::after {
  opacity: 1;
}

.compare-labels {
  position: absolute;
  inset: auto 0 0;
  display: flex;
  justify-content: space-between;
  margin: 0;
  padding: 0 14px 12px;
  font-size: 10px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--text);
  /* The crop is light on one side and dark on the other, so the labels carry
     their own contrast rather than relying on the frame. */
  text-shadow: 0 1px 8px rgba(0, 0, 0, 0.9);
  pointer-events: none;
}

/* --- Opening sweep ---------------------------------------------------------
   Armed by the scroll reveal rather than by the hero, since this is no longer
   in the hero: the frame arrives on the untreated plate, wipes across to the
   finished one, then falls back to the middle and waits to be dragged. The
   fall back to the middle is the invitation — the curtain's own wipe ends on
   the finished frame because nothing there can be taken hold of, and this one
   does not because something here can.

   Delayed past the reveal's 0.6 s fade, mirrored from reveal.css: a wipe spent
   behind an element that is still fading up is a wipe nobody sees, and the two
   have to be retimed together.
   -------------------------------------------------------------------------- */

.compare.visible {
  animation: compare-sweep 2.6s cubic-bezier(0.65, 0, 0.35, 1) 0.6s both;
}

/* An animation's forwards fill outranks an inline custom property, so the
   sweep has to be dropped before the drag can write the first one. Same
   specificity as the rule above, which means this one wins on source order
   alone and has to stay below it. */
.compare.is-held {
  animation: none;
}

@keyframes compare-sweep {
  from {
    --pos: 100%;
  }
  55% {
    --pos: 0%;
  }
  to {
    --pos: 50%;
  }
}

@media (prefers-reduced-motion: reduce) {
  /* The frame still arrives — it is a layout, not a motion — but it is already
     split down the middle when it does. */
  .compare.visible {
    animation: none;
  }
}
