/* FieldSync live replay — the review panel inside the attempt popup.
 *
 * Dark chrome in both themes, deliberately. It stops the review surface reading
 * as part of the entry form, and it is how every officiating room in the world
 * is lit. Sizes are for gloves and bifocals: on an iPad Air 4 one CSS px is
 * about 0.192mm, so nothing tappable is under 52px in its short dimension.
 */

#fsReplay {
  background: #0c0f14;
  border: 1px solid #232b36;
  border-radius: 12px;
  color: #e9eef5;
  margin-bottom: 10px;
  overflow: hidden;
  /* The dialog scroller sets touch-action: pan-y in the app shell. Without this
     the first vertical wobble of a scrub scrolls the dialog under the official's
     finger, which is the single highest-probability bug in this feature. */
  overscroll-behavior: contain;
  -webkit-tap-highlight-color: transparent;
}

#fsReplay button {
  font: inherit;
  color: #e9eef5;
  background: #172030;
  border: 1px solid #2b3648;
  border-radius: 10px;
  touch-action: none;
  -webkit-user-select: none;
  user-select: none;
}
#fsReplay button:disabled,
#fsReplay button[aria-disabled="true"] { opacity: .4; }

/* ── Collapsed: one target, no chevron to aim at ─────────────────────────── */
.fsRvBar {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 12px;
  min-height: 68px;
  width: 100%;
  text-align: left;
  background: transparent;
  border: 0 !important;
  border-radius: 0 !important;
}
.fsRvThumb {
  width: 156px; height: 88px;
  flex: 0 0 auto;
  background: #000;
  border-radius: 8px;
  object-fit: cover;
  display: block;
}
.fsRvBarText { flex: 1 1 auto; min-width: 0; }
.fsRvCta {
  font-size: 21px; font-weight: 700; letter-spacing: .01em;
  display: block; margin-bottom: 3px;
}
.fsRvSub { font-size: 15px; color: #9fb0c6; display: block; }

@media (max-width: 900px) {
  .fsRvThumb { width: 120px; height: 68px; }
  .fsRvBar { min-height: 60px; }
}

/* ── Status pill ────────────────────────────────────────────────────────── */
.fsRvPill {
  display: inline-flex; align-items: center; gap: 7px;
  font-size: 15px; font-weight: 700; letter-spacing: .04em;
  padding: 4px 10px; border-radius: 999px;
  background: #131c28; color: #9fb0c6;
}
.fsRvDot { width: 9px; height: 9px; border-radius: 50%; background: #6b7a8f; }
.fsRvPill.live { color: #6ee7a8; } .fsRvPill.live .fsRvDot { background: #22c55e; }
.fsRvPill.warn { color: #fbbf24; } .fsRvPill.warn .fsRvDot { background: #f59e0b; }
.fsRvPill.bad  { color: #f87171; } .fsRvPill.bad  .fsRvDot { background: #ef4444; }

/* ── Expanded: the panel takes the whole scroller ───────────────────────── */
#modalBackdrop.fsReplayOpen .modalBd {
  overflow: hidden;
  display: flex;
  flex-direction: column;
  padding: 0;
}
/* Time-multiplex rather than stack: the entry surface is temporarily gone
   instead of being pushed below the fold, so the feature costs zero vertical
   space at the moment the official actually needs the picture. Nothing is
   reparented and nothing is destroyed, so #markInput keeps its value and the
   .fsCanSave / .fsSaved contract is untouched. */
#modalBackdrop.fsReplayOpen .modalBd > *:not(#fsReplay) { display: none !important; }
#modalBackdrop.fsReplayOpen #fsReplay {
  flex: 1 1 auto; min-height: 0;
  display: flex; flex-direction: column;
  margin: 0; border-radius: 0; border: 0;
}
#modalBackdrop.fsReplayOpen .fsRvBar { display: none; }

.fsRvOpen { display: none; flex-direction: column; flex: 1 1 auto; min-height: 0; }
#modalBackdrop.fsReplayOpen .fsRvOpen { display: flex; }

.fsRvHead {
  display: flex; align-items: center; justify-content: space-between;
  gap: 10px; padding: 8px 12px; min-height: 44px;
  border-bottom: 1px solid #1b2430;
}
.fsRvWho { font-size: 17px; font-weight: 700; }

.fsRvStage {
  position: relative;
  flex: 1 1 auto; min-height: 0;
  background: #000;
  display: flex; align-items: center; justify-content: center;
}
.fsRvCanvas {
  max-width: 100%; max-height: 100%;
  display: block;
  touch-action: none;          /* the picture IS the control */
}

/* A frame we do not have is a plate, not an overlay on a stale picture. Leaving
   the previous frame under a fresh timecode is the one lie this panel must never
   tell. */
.fsRvPlate {
  position: absolute; inset: 0;
  display: none;
  align-items: center; justify-content: center;
  text-align: center; padding: 24px;
  background: #05070a; color: #cbd6e6; font-size: 19px; line-height: 1.45;
}
.fsRvPlate.on { display: flex; }

.fsRvFlash {
  position: absolute; left: 50%; top: 14px; transform: translateX(-50%);
  background: rgba(8,12,18,.92); border: 1px solid #2b3648;
  padding: 7px 14px; border-radius: 999px;
  font-size: 16px; font-weight: 700; opacity: 0; transition: opacity .18s;
  pointer-events: none;
}
.fsRvFlash.on { opacity: 1; }

.fsRvReadout {
  position: absolute; left: 12px; bottom: 10px;
  background: rgba(8,12,18,.82);
  padding: 5px 11px; border-radius: 8px;
  font-size: 16px; font-variant-numeric: tabular-nums; color: #cbd6e6;
}

/* ── Coverage ribbon: an indicator that is also draggable, never advertised ─ */
.fsRvRibbonWrap { padding: 9px 12px 5px; touch-action: none; }
.fsRvRibbon {
  position: relative; height: 26px; border-radius: 6px;
  background: #131a24; overflow: hidden;
}
.fsRvHeld { position: absolute; top: 0; bottom: 0; background: #24405e; }
/* Three vocabularies, never blended: solid = held and decodable, flat grey =
   held but the picture was blank, hatched = never arrived. */
.fsRvBlank { position: absolute; top: 0; bottom: 0; background: #3a4250; }
.fsRvGap {
  position: absolute; top: 0; bottom: 0;
  background: repeating-linear-gradient(45deg, #5a2130 0 6px, #131a24 6px 12px);
}
/* The playhead is now advertised as draggable, reversing the note above. The
   hairline alone read as a position marker, so officials stepped frame by frame
   with the buttons and never discovered they could simply drag to the moment
   they wanted. The 3px line stays — it is what makes the position readable to
   the frame — and a grab handle sits on top of it. */
.fsRvPlay { position: absolute; top: -3px; bottom: -3px; width: 3px; background: #7dd3fc; }
.fsRvPlay::after {
  content: "";
  position: absolute;
  left: 50%; top: 50%;
  width: 26px; height: 26px;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  background: #7dd3fc;
  border: 3px solid #0b1220;
  box-shadow: 0 1px 5px rgba(0, 0, 0, .55);
  pointer-events: none;   /* the hit strip below owns the gesture */
}
.fsRvRibbonWrap:active .fsRvPlay::after { background: #bae6fd; }
/* The hit strip has to sit ABOVE the ribbon, and it did not.
 *
 * .fsRvRibbon is position:relative and .fsRvPlay is absolute inside it, so both
 * painted over this strip, which was not positioned at all. Hit-testing follows
 * paint order, so a tap anywhere on the ribbon — including the whole of the new
 * round handle — landed on .fsRvPlay, which has no listener. Only the part of
 * the strip hanging BELOW the ribbon ever received a gesture, which is why the
 * handle looked draggable and refused to move.
 *
 * z-index puts it back on top. The negative margin now clears the top of the
 * handle (-32px rather than -17px) and the height grows by the same amount, so
 * the strip covers the handle while contributing exactly the same 43px to the
 * layout as before — the controls below do not move. */
.fsRvRibbonHit {
  position: relative;
  z-index: 2;
  margin: -32px 0 0;
  height: 75px;
  cursor: grab;
}
.fsRvRibbonHit:active { cursor: grabbing; }

/* Play/pause. Tinted only when paused, so a glance at the row answers "is this
   running?" without reading the glyph. */
.fsRvPP { font-size: 26px !important; }
.fsRvPP.on { background: #1d4ed8 !important; color: #fff !important; }

/* ── Controls ──────────────────────────────────────────────────────────── */
.fsRvSteps { display: flex; gap: 16px; padding: 6px 12px; }
.fsRvSteps button {
  flex: 1 1 0; height: 88px;
  font-size: 21px; font-weight: 700;
}
.fsRvHint {
  text-align: center; font-size: 15px; color: #8b9bb1;
  padding: 2px 12px 6px;
}

.fsRvRow2 { display: flex; gap: 12px; padding: 4px 12px 8px; }
.fsRvSpeed { display: flex; flex: 1 1 auto; gap: 0; }
.fsRvSpeed button {
  flex: 1 1 0; height: 52px; font-size: 17px; font-weight: 700;
  border-radius: 0;
}
.fsRvSpeed button:first-child { border-radius: 10px 0 0 10px; }
.fsRvSpeed button:last-child { border-radius: 0 10px 10px 0; }
.fsRvSpeed button.on {
  background: #2f6fb5; border-color: #4f93de; color: #ffffff;
  box-shadow: inset 0 0 0 1px #4f93de;
}
.fsRvLive { flex: 0 0 auto; min-width: 165px; height: 52px; font-size: 17px; font-weight: 700; }

.fsRvExit { padding: 4px 12px 6px; }
.fsRvExit button { width: 100%; height: 76px; font-size: 21px; font-weight: 700; }
.fsRvStands {
  text-align: center; font-size: 15px; color: #8b9bb1;
  padding: 0 12px 10px;
}

.fsRvNote {
  margin: 0 12px 8px; padding: 9px 12px;
  border-radius: 8px; background: #3a2410; border: 1px solid #6b4415;
  color: #fcd9a4; font-size: 16px; line-height: 1.4;
}
.fsRvNote[hidden] { display: none; }

/* ── Alignment mode ──────────────────────────────────────────────────────
   A vertical reference for seating the camera's entrance pupil on the
   take-off plane. Parallax is the only error here that cannot be fixed in
   software, so it must be fixed at setup; this makes it visible. Deliberately
   hidden unless asked for - during a real call it is clutter. */
.fsRvAlign { position: absolute; inset: 0; display: none; pointer-events: none; }
.fsRvAlign.on { display: block; }
/* Centre line = the principal point. Align the board edge parallel to it. */
.fsRvAlign::before {
  content: ""; position: absolute; left: 50%; top: 0; bottom: 0;
  width: 1px; margin-left: -0.5px;
  background: repeating-linear-gradient(#7dd3fc 0 10px, transparent 10px 18px);
}
/* Flanking rules at ±7px-equivalent: if the board edge leans outside these
   across half the frame, the pupil is roughly 2mm off the plane. */
.fsRvAlign::after {
  content: ""; position: absolute; left: 50%; top: 0; bottom: 0;
  width: 60px; margin-left: -30px;
  border-left: 1px solid rgba(125,211,252,.28);
  border-right: 1px solid rgba(125,211,252,.28);
}
#fsReplay .fsRvAim { flex: 0 0 auto; min-width: 96px; height: 52px; font-size: 17px; font-weight: 700; }
#fsReplay .fsRvAim.on { background: #2f6fb5; border-color: #4f93de; color: #fff; }

/* ── The saved still ─────────────────────────────────────────────────────
 *
 * One frame, kept by the official, final until deleted. The styling has one
 * job beyond looking right: KEEP must never be mistaken for a transport
 * button. It is the only control in this panel that writes anything, so it is
 * alone on its row, full width, and the only green thing on the surface.
 */
.fsRvKeepRow { padding: 2px 12px 8px; }
.fsRvKeep {
  width: 100%; height: 72px;
  font-size: 21px; font-weight: 800; letter-spacing: .02em;
  background: #14532d !important;
  border-color: #1d7a43 !important;
  color: #d6ffe4 !important;
}
/* Tinted only once there is a real picture to keep, so the row answers
   "can I press this yet?" without the official having to try. */
.fsRvKeep.ready {
  background: #16a34a !important;
  border-color: #4ade80 !important;
  color: #ffffff !important;
  box-shadow: inset 0 0 0 1px #4ade80;
}
.fsRvKeep:active { filter: brightness(1.15); }

.fsRvPill.saved { color: #6ee7a8; }
.fsRvPill.saved .fsRvDot { background: #22c55e; }

.fsRvSavedBar {
  display: none;
  padding: 10px 12px 4px;
  text-align: center;
}
.fsRvSavedText {
  font-size: 20px; font-weight: 800; letter-spacing: .04em;
  color: #6ee7a8;
}
.fsRvSavedSub {
  font-size: 15px; color: #8b9bb1; margin-top: 3px;
  font-variant-numeric: tabular-nums;
}

.fsRvDelRow { display: none; padding: 4px 12px 8px; }
.fsRvDel {
  width: 100%; height: 60px;
  font-size: 17px; font-weight: 700;
  background: #2a1417 !important;
  border-color: #5b2a30 !important;
  color: #fca5a5 !important;
}
.fsRvDel:active { filter: brightness(1.2); }

/* Saved mode swaps the panel over wholesale. The transport is REMOVED rather
   than disabled: a greyed-out scrubber invites tapping at something that
   cannot move, and there is genuinely nothing to scrub — one picture is all
   that exists for this attempt now. */
.fsRvSavedMode .fsRvRibbonWrap,
.fsRvSavedMode .fsRvSteps,
.fsRvSavedMode .fsRvHint,
.fsRvSavedMode .fsRvKeepRow,
.fsRvSavedMode .fsRvRow2 { display: none !important; }
.fsRvSavedMode .fsRvSavedBar,
.fsRvSavedMode .fsRvDelRow { display: block; }

/* The saved frame renders here, NOT on the decode canvas.
 *
 * In production a still is served from R2 through a 302 to a presigned URL, so
 * it is cross-origin; drawing it onto the canvas would taint it and make
 * canvas.toBlob() - the whole of KEEP THIS FRAME - throw SecurityError for
 * every later attempt in the session. An <img> cannot taint anything.
 *
 * It sits in the same flex centre as the canvas and matches its fit rules, so
 * the picture lands in exactly the same place the live frame did. */
.fsRvStill {
  display: none;
  max-width: 100%; max-height: 100%;
  object-fit: contain;
}
.fsRvSavedMode .fsRvStill { display: block; }
.fsRvSavedMode .fsRvCanvas { display: none; }
