/* Shared in-browser video recorder — used by BOTH the public apply funnel and the brand's own
   intro/acceptance videos. Paired with video-recorder.js, following the data-export-modal.js/.css
   precedent.

   ONE implementation on purpose. The alternative was a second copy inside app.js, and this session
   already paid for a rule implemented twice (the needs-review predicate, where SQL and Python
   disagreed and the payload could not express which attempt was reviewable). A recorder duplicated
   across two surfaces would drift the same way, and the half that drifts is the one a stranger uses.

   Tokens are read from the HOST page with fallbacks: the apply page ports portal.css's palette and the
   admin app has styles.css, so var(--lb-navy, #16384a) lands correctly in both without this file
   deciding what the product looks like. */

.lbvr { display: block; }

/* TWO stages, because the two sides are doing different things.
   * The APPLICANT is holding a phone. Their window IS the phone: 9/16, filled edge to edge, no
     curtains. A 3/4 box was squarer than any phone shoots, so even a correct portrait stream sat in
     a wide-looking frame -- and if the camera handed back LANDSCAPE (iOS often gives the sensor's
     native orientation), `contain` letterboxed it into a short wide strip inside that box, with
     curtains narrowing it further. Owner, testing on a real phone: "the video record windows are
     very wide, they are not like the usual vertical phone aspect ratio."
   * The BRAND is at a laptop. There the letterbox and the curtains are the POINT -- they show which
     part of a landscape webcam a phone will actually keep -- so that stage is unchanged. */
.lbvr-stage {
  position: relative; border-radius: var(--radius-row, 14px); overflow: hidden;
  background: #0b0d10;
  /* Set from the REAL stream by sizeStage(), clamped between 9:16 and square. A fixed 9/16 here plus
     object-fit: cover was what magnified a landscape stream into a sliver of somebody's face. 9/16 is
     only the fallback, for before metadata lands. */
  aspect-ratio: var(--lbvr-aspect, 0.5625);
  /* Never taller than the screen it is held up to: the shutter has to stay reachable under it. */
  max-height: 68vh; margin-inline: auto;
}
/* The brand's laptop stage keeps the squarer box the curtain geometry was built around. */
.lbvr-stage.lbvr-guided { aspect-ratio: 3 / 4; max-height: none; }
/* FILL, and safe to fill: sizeStage() has already matched the box to the frame, so `cover` has
   nothing to crop. It stays `cover` rather than `contain` so a sub-pixel rounding difference shows as
   a hairline crop instead of a hairline black bar. */
.lbvr-stage video { width: 100%; height: 100%; object-fit: cover; display: block; }
/* ...except when the clamp bit -- a stream wider than square. Then the frame is shown COMPLETE inside
   the box, because seeing all of yourself small beats seeing a third of yourself large. */
.lbvr-stage.lbvr-letterbox video { object-fit: contain; }
/* CONTAIN only on the guided (brand) stage. sizeCurtains() computes the curtain width from the
   LETTERBOX geometry -- the fraction of a landscape stream a portrait window can show -- which is
   only true if the stage letterboxes. Under `cover` the browser has already cropped the sides away,
   so the curtains would mask that much again: ~58% of a laptop preview went black, leaving a slit. */
.lbvr-stage.lbvr-guided video { object-fit: contain; }

.lbvr-curtain {
  position: absolute; top: 0; bottom: 0; width: 0; background: rgba(11, 13, 16, .82);
  transition: width .45s cubic-bezier(.4, 0, .2, 1); pointer-events: none;
}
.lbvr-curtain.l { left: 0; border-right: 1px solid rgba(255, 255, 255, .28); }
.lbvr-curtain.r { right: 0; border-left: 1px solid rgba(255, 255, 255, .28); }
.lbvr-stage.framed .lbvr-curtain { width: var(--lbvr-crop, 0px); }

.lbvr-guide {
  position: absolute; left: 50%; bottom: 10px; transform: translateX(-50%);
  font-size: 11px; font-weight: 650; letter-spacing: .04em; text-transform: uppercase;
  color: #fff; background: rgba(11, 13, 16, .62); padding: 5px 10px;
  border-radius: var(--radius-pill, 999px); opacity: 0; transition: opacity .25s;
}
.lbvr-stage.framed .lbvr-guide { opacity: 1; }

.lbvr-timer {
  position: absolute; top: 10px; right: 10px; display: none; align-items: center; gap: 6px;
  background: rgba(11, 13, 16, .68); color: #fff; padding: 5px 10px;
  border-radius: var(--radius-pill, 999px); font-size: 12.5px; font-weight: 650;
  font-variant-numeric: tabular-nums;
}
.lbvr-timer.on { display: flex; }
.lbvr-timer i {
  width: 8px; height: 8px; border-radius: 50%; background: #d92d20;
  animation: lbvr-pulse 1.1s infinite;
}
@keyframes lbvr-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .25; } }

.lbvr-countdown {
  position: absolute; inset: 0; display: none; place-items: center;
  background: rgba(11, 13, 16, .45); color: #fff;
  font-size: 84px; font-weight: 700; font-variant-numeric: tabular-nums; line-height: 1;
}
.lbvr-countdown.on { display: grid; }

@media (prefers-reduced-motion: reduce) {
  .lbvr-timer i { animation: none; }
  .lbvr-curtain { transition: none; }
  .lbvr-guide { transition: none; }
}

/* A real shutter, not a text button: the red disc becomes a stop square while recording. */
.lbvr-shutter { display: grid; place-items: center; margin-top: 16px; }
.lbvr-shutter button {
  width: 74px; height: 74px; padding: 0; margin: 0; border-radius: 50%;
  background: var(--surface, #fff); border: 3px solid var(--line-strong, #e0e1e8);
  display: grid; place-items: center; cursor: pointer;
}
.lbvr-shutter button > span {
  width: 54px; height: 54px; border-radius: 50%; background: #d92d20;
  transition: border-radius .18s, width .18s, height .18s;
}
.lbvr-shutter button.recording > span { width: 30px; height: 30px; border-radius: 6px; }
.lbvr-shutter button:disabled { opacity: .5; cursor: default; }
.lbvr-shutter button:focus-visible { outline: 2px solid var(--lb-teal, #2f9aa8); outline-offset: 3px; }
.lbvr-hint { margin: 8px 0 0; font-size: 12.5px; color: var(--faint, #8a90a0); text-align: center; }

/* Thumbs: down re-records, up accepts. Sized for a thumb on a phone. */
.lbvr-verdict { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; margin-top: 16px; }
.lbvr-verdict button {
  margin: 0; height: 60px; display: grid; place-items: center; gap: 4px; cursor: pointer;
  border: 1px solid var(--line-strong, #e0e1e8); border-radius: var(--radius-sm, 8px);
  font: inherit; font-weight: 650;
}
.lbvr-verdict button svg { width: 24px; height: 24px; }
.lbvr-verdict button small { font-size: 11.5px; font-weight: 600; }
.lbvr-verdict .lbvr-no { background: var(--surface, #fff); color: var(--danger, #b42318); }
.lbvr-verdict .lbvr-yes { background: var(--ok, #137a47); color: #fff; border-color: transparent; }
.lbvr-verdict button:disabled { opacity: .5; cursor: default; }
/* A take we watched freeze. "Record again" becomes the loud one and "Use this one" goes quiet -- but it
   stays ENABLED and clickable on purpose: the detector can be wrong, and a wrong detector must never be
   the thing that stops somebody applying for a job. See checkForStall() in video-recorder.js. */
.lbvr-verdict.lbvr-stalled .lbvr-no {
  background: var(--danger, #b42318); color: #fff; border-color: transparent;
}
.lbvr-verdict.lbvr-stalled .lbvr-yes {
  background: var(--surface, #fff); color: var(--muted, #6b7080);
  border-color: var(--line, #ececf1);
}

.lbvr-msg { margin-top: 12px; font-size: 13.5px; }
.lbvr-msg.bad {
  color: var(--danger, #b42318); background: var(--danger-soft, #fff1f0);
  padding: 10px 12px; border-radius: var(--radius-sm, 8px);
}
.lbvr-hidden { display: none !important; }
