/* beddha training -- design tokens verbatim from control.beddha.com */
:root {
  --bg: #EFEBF5;
  --surface: #FFFFFF;
  --text: #231536;
  --text-2: #6E637D;
  --purple: #7047D6;
  --purple-hover: #5F39C2;
  --lavender: #A68EC9;
  --teal: #157F83;
  --rose: #A95365;
  --hl-purple: #F2EDFA;
  --hl-mint: #E3F2EB;
  --hl-blush: #F6E9ED;
  --line: rgba(35,21,54,.10);
  --line-strong: rgba(35,21,54,.18);
  --radius: 14px;
  --radius-sm: 9px;
  --shadow: 0 1px 2px rgba(35,21,54,.04), 0 8px 24px rgba(35,21,54,.06);
}

* { box-sizing: border-box; }
html, body { margin: 0; padding: 0; }
/* [hidden] must always win, even over an ID-selector display:flex rule (e.g.
   #workArea/#reviewWorkArea below) -- the browser's default [hidden]{display:none}
   has LOWER specificity than any #id rule, so without this an element toggled
   via .hidden=true can stay visually flexed/visible while its own JS thinks it's
   hidden. Real bug found by Part L3's fresh reviewer (queue-empty screen left a
   stale player + buttons visible and clickable, throwing on click). */
[hidden] { display: none !important; }
body {
  font-family: 'Manrope', system-ui, sans-serif;
  background: var(--bg);
  color: var(--text);
  padding-bottom: 84px; /* room for sticky footer */
}
h1, h2, h3, .heading { font-family: 'Plus Jakarta Sans', system-ui, sans-serif; }
a { color: var(--purple); }
button { font-family: inherit; }

/* ---------- login page ---------- */
#loginPage {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px 16px;
}
.login-card {
  background: var(--surface);
  border-radius: var(--radius);
  box-shadow: var(--shadow);
  padding: 32px 28px;
  width: 100%;
  max-width: 380px;
}
.login-wordmark { font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 800; font-size: 1.6rem; margin: 0; }
.login-sub { color: var(--text-2); margin: 0 0 24px; font-size: 0.95rem; }
.login-card label { display: block; font-size: 0.85rem; color: var(--text-2); margin-bottom: 4px; margin-top: 14px; }
.login-card input {
  width: 100%; padding: 10px 12px; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  font-size: 1rem; background: var(--surface); color: var(--text);
}
.login-card input:focus { outline: 2px solid var(--purple); outline-offset: 1px; }
.btn-primary {
  background: var(--purple); color: #fff; border: none; border-radius: var(--radius-sm);
  padding: 11px 18px; font-size: 0.95rem; font-weight: 600; cursor: pointer; width: 100%; margin-top: 20px;
}
.btn-primary:hover { background: var(--purple-hover); }
.btn-primary:disabled { opacity: 0.6; cursor: default; }
.login-error { color: var(--rose); font-size: 0.85rem; margin-top: 12px; min-height: 1.2em; }

/* ---------- app shell ---------- */
#appHeader {
  height: 60px; display: flex; align-items: center; gap: 16px; padding: 0 16px;
  background: var(--surface); border-bottom: 1px solid var(--line); position: sticky; top: 0; z-index: 10;
}
.wordmark-block { display: flex; align-items: baseline; gap: 6px; flex-shrink: 0; }
.wordmark { font-family: 'Plus Jakarta Sans', sans-serif; font-weight: 800; font-size: 1.15rem; }
.wordmark-sub { color: var(--text-2); font-size: 0.8rem; }
#tenantSelect {
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm); padding: 6px 10px;
  font-size: 0.85rem; background: var(--surface); color: var(--text);
}
#modeToggle { display: flex; gap: 2px; background: var(--hl-purple); border-radius: var(--radius-sm); padding: 3px; }
#modeToggle .modeBtn {
  background: transparent; color: var(--text-2); border: none; padding: 6px 16px; border-radius: 7px;
  cursor: pointer; font-size: 0.85rem; font-weight: 600;
}
#modeToggle .modeBtn.active { background: var(--surface); color: var(--purple); box-shadow: var(--shadow); }
#modeToggle .modeBtn:disabled { opacity: 0.4; cursor: not-allowed; }
#headerRight { margin-left: auto; display: flex; align-items: center; gap: 12px; }
#progressTile { font-size: 0.78rem; color: var(--text-2); text-align: right; line-height: 1.3; }
.avatar {
  width: 32px; height: 32px; border-radius: 50%; background: var(--purple); color: #fff;
  display: flex; align-items: center; justify-content: center; font-size: 0.8rem; font-weight: 700;
  flex-shrink: 0; cursor: pointer; border: none;
}
#anleitungToggle {
  background: var(--hl-purple); color: var(--purple); border: none; border-radius: var(--radius-sm);
  padding: 6px 12px; font-size: 0.8rem; cursor: pointer; font-weight: 600;
}

/* ---------- cards / stat tiles / chips ---------- */
.card {
  background: var(--surface); border-radius: var(--radius); box-shadow: var(--shadow); padding: 16px;
}
.stat-row { display: flex; gap: 10px; flex-wrap: wrap; margin: 12px 0; }
.stat-tile {
  background: var(--hl-purple); border-radius: var(--radius-sm); padding: 10px 14px; flex: 1 1 90px; min-width: 80px;
}
.stat-tile .stat-label { font-size: 0.7rem; color: var(--text-2); text-transform: uppercase; letter-spacing: .04em; }
.stat-tile .stat-value { font-size: 1.1rem; font-weight: 700; font-family: 'Plus Jakarta Sans', sans-serif; }
.chip-row { display: flex; gap: 6px; flex-wrap: wrap; margin: 10px 0; }
.chip {
  background: var(--hl-mint); color: var(--teal); border-radius: 999px; padding: 4px 12px; font-size: 0.78rem; font-weight: 600;
}
.chip.chip-blush { background: var(--hl-blush); color: var(--rose); }
.chip.chip-purple { background: var(--hl-purple); color: var(--purple); }
/* Deliberately NOT mint/blush/purple (this repo's existing chip colours all
   carry their own meaning elsewhere) -- a plain grey so it reads as neutral
   bookkeeping, never a hint about what the engine guessed for this video. */
.chip.priority-chip { background: var(--bg); color: var(--text-2); border: 1px solid var(--line-strong); }

/* ---------- sticky footer ---------- */
#stickyFooter {
  position: fixed; bottom: 0; left: 0; right: 0; background: var(--surface); border-top: 1px solid var(--line);
  padding: 12px 16px; display: flex; flex-direction: column; align-items: center; gap: 6px; z-index: 10;
}
#footerSubmitBtn {
  background: var(--purple); color: #fff; border: none; border-radius: var(--radius-sm);
  padding: 13px; font-size: 1rem; font-weight: 700; cursor: pointer; width: 100%; max-width: 680px;
}
#footerSubmitBtn:hover { background: var(--purple-hover); }

/* Part L10: moved out of the sticky footer entirely -- a real, prominent
   button (Submit's own height, an outlined/secondary colour so it never
   reads as the primary action) directly below the card list in both modes.
   The keyboard shortcut (S in label, N in review -- see bindKeyboard) is
   still the real "works from any state, no scrolling required" escape path;
   this is the visible, discoverable counterpart to it. */
#nextVideoBlock {
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--line);
  display: flex; flex-direction: column; align-items: center; gap: 6px;
}
#footerSkipBtn {
  background: var(--surface); color: var(--purple); border: 2px solid var(--purple); border-radius: var(--radius-sm);
  padding: 11px; font-size: 1rem; font-weight: 700; cursor: pointer; width: 100%;
}
#footerSkipBtn:hover { background: var(--hl-purple); }

/* ---------- layout ---------- */
#main, #reviewMain, #questionsMain { padding: 16px; max-width: 1100px; margin: 0 auto; }
#workArea, #reviewWorkArea, #questionsWorkArea { display: flex; gap: 16px; align-items: flex-start; flex-wrap: wrap; }
#playerCol, #questionsPlayerCol { flex: 0 0 380px; max-width: 100%; }
#player, #questionsPlayer { width: 100%; max-width: 380px; background: #000; border-radius: var(--radius-sm); }

/* Part L6-UI change #1: "everything to review sits to the RIGHT of the video,
   the video never scrolls away" -- >=1100px only (below that, stack as before:
   the base rules above already do this). #playerCol sticks just under the
   sticky header; #rowsCol/#claimsCol become their OWN independent scroll
   container sized to the remaining viewport height, so scrolling through
   review cards never carries the video off-screen. 76px = 60px header +
   16px #main/#reviewMain padding; 84px matches this file's own existing
   body{padding-bottom} footer-clearance constant, reused here so both
   columns stop exactly above the fixed sticky footer instead of running
   underneath it. */
@media (min-width: 1100px) {
  #main, #reviewMain, #questionsMain { max-width: 1400px; }
  #workArea, #reviewWorkArea, #questionsWorkArea { flex-wrap: nowrap; }
  #playerCol, #questionsPlayerCol {
    position: sticky; top: 76px; align-self: flex-start;
    max-height: calc(100vh - 76px - 16px); overflow-y: auto;
  }
  /* A portrait (9:16) video at the column's full 380px width would otherwise be
     ~675px tall on its own -- taller than the whole left column can spare
     alongside the X-Ray strip/timeline/toggles/hint change #1 also asks to keep
     visible there. Capping height (not width) lets the browser's own replaced-
     element sizing shrink WIDTH to match, preserving the real aspect ratio --
     same box-overlay geometry math (computeVideoContentRect in app.js) already
     handles a video whose rendered box doesn't span the full column, since it
     measures the video element's OWN clientWidth/clientHeight, not the column's. */
  #player, #reviewPlayer { max-height: 46vh; }
  #rowsCol, #claimsCol {
    align-self: flex-start;
    max-height: calc(100vh - 76px - 84px);
    overflow-y: auto;
    padding-right: 6px;
  }
  /* Change #2: "'2 of 14 reviewed' stays at the top of the column" -- pinned to
     the top of claimsCol's OWN scroll container (not the page), so it survives
     scrolling through the card list underneath it. */
  #reviewProgressLine {
    position: sticky; top: 0; z-index: 5; background: var(--bg);
    margin: 0 0 8px; padding: 6px 0;
  }
}

/* Part L5b-UI: reviewPlayer lives inside a position:relative wrapper sized to
   exactly the SAME box as the video itself, so the box overlay (an absolutely
   positioned sibling) can be measured/positioned against it in JS -- see
   updateBoxOverlayGeometry() in app.js. */
#reviewPlayerWrap { position: relative; width: 100%; max-width: 380px; }
#reviewPlayer { display: block; width: 100%; background: #000; border-radius: var(--radius-sm); }
#boxOverlay { position: absolute; pointer-events: none; }
.box-group { pointer-events: auto; cursor: pointer; }
.box-group.box-generic { pointer-events: none; cursor: default; }
/* pointer-events:fill is required, NOT cosmetic: with fill:none (the whole point
   of drawing an outline-only box), SVG's default hit-testing (visiblePainted)
   only responds to clicks ON THE STROKE LINE ITSELF, not the transparent
   interior -- so a real click anywhere inside the box (where a user naturally
   clicks) would silently miss and fall through to the video underneath. Found
   live in this feature's own Playwright testing (a real click at the box's own
   center never registered). */
.box-rect { fill: none; stroke-width: 3; paint-order: stroke; pointer-events: fill; }
.box-group.box-hover-highlighted .box-rect { stroke-width: 6; }
.box-label-bg { opacity: 0.88; }
/* font-size deliberately NOT set here -- app.js sets it per-label (an SVG
   presentation attribute) scaled to the frame height, and a CSS rule for the
   same property would always win over that attribute and force one fixed size
   regardless of frame size. */
.box-label-text { font-weight: 700; fill: #fff; font-family: 'Manrope', system-ui, sans-serif; }
#playerHint { font-size: 0.75rem; color: var(--text-2); margin-top: 4px; }
#meta, #reviewMeta { font-size: 0.85rem; margin-top: 10px; }
#meta div, #reviewMeta div { margin-bottom: 4px; }
/* Follow-up to L6-L8: the caption is visible by default now (was behind
   "details") -- capped scroll height on the caption text itself (not the
   chips below it) so an unusually long caption can't push the toggles/hint
   further down the sticky left column than the layout has room for. */
#metaCaptionBox, #reviewMetaCaptionBox { font-size: 0.85rem; margin-top: 8px; }
.caption-line { max-height: 6.4em; overflow-y: auto; margin-bottom: 6px; line-height: 1.4; overflow-wrap: anywhere; }

/* Part L10 change #4: a human research aid under the caption -- never
   styled to look like an AI-generated hint (plain text buttons, same
   family as the caption's own chips). */
.translate-row { font-size: 0.78rem; color: var(--text-2); margin-bottom: 6px; }
.translate-btn {
  background: none; border: 1px solid var(--line-strong); border-radius: 999px;
  padding: 2px 9px; font-size: 0.74rem; font-weight: 600; color: var(--purple); cursor: pointer; margin-left: 2px;
}
.translate-btn:hover { background: var(--hl-purple); }
.translate-btn:disabled { opacity: 0.5; cursor: default; }
.translation-result {
  font-size: 0.85rem; margin-bottom: 6px; padding: 6px 8px; background: var(--hl-purple);
  border-radius: 7px; color: var(--text); max-height: 6.4em; overflow-y: auto; overflow-wrap: anywhere;
}
.translation-result.translation-unavailable { background: var(--bg); color: var(--text-2); font-style: italic; }
#agreementBadge { background: var(--hl-blush); color: var(--rose); padding: 4px 10px; border-radius: 999px; font-weight: 600; display: inline-block; font-size: 0.78rem; }
#videoActions { margin-top: 10px; display: flex; flex-direction: column; gap: 6px; }
#videoActions button { padding: 8px; font-size: 0.9rem; cursor: pointer; border-radius: var(--radius-sm); border: 1px solid var(--line-strong); background: var(--surface); }
#t0Btn { background: var(--hl-blush); color: var(--rose); border: none; }
#addProductBtn, #addMissedBtn { background: var(--hl-purple); color: var(--purple); border: none; }
/* Part L9 Problem 3: Florian's own "too quiet" -- same visual weight as
   Submit (#footerSubmitBtn), overriding the shared light style above. */
#addMissedBtn {
  background: var(--purple); color: #fff; border: none; border-radius: var(--radius-sm);
  padding: 13px; font-size: 0.95rem; font-weight: 700; cursor: pointer; width: 100%; margin: 12px 0;
}
#addMissedBtn:hover { background: var(--purple-hover); }

#rowsCol, #claimsCol { flex: 1 1 480px; min-width: 280px; }
.row-card, .claim-card {
  background: var(--surface); border-radius: var(--radius); box-shadow: var(--shadow); padding: 14px;
  /* minmax(0, 1fr), not bare 1fr: a bare 1fr track's minimum is content-based
     (same "auto" default as a flex item), so a long product/bundle title
     forces the track wider than its fair share and overflows the card --
     the actual root cause of the mobile horizontal-scroll bug a fresh-eyes
     reviewer found (the .dd-trigger min-width:0 chain alone wasn't enough,
     because the constraint starts here, one level up). */
  margin-bottom: 12px; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px;
}
.row-head, .claim-head { grid-column: 1 / -1; display: flex; justify-content: space-between; font-weight: 700; font-family: 'Plus Jakarta Sans', sans-serif; font-size: 0.9rem; }
.row-card label, .claim-card label { display: flex; flex-direction: column; font-size: 0.78rem; color: var(--text-2); gap: 3px; }
.row-card input, .row-card select, .claim-card input, .claim-card select {
  padding: 6px 8px; font-size: 0.88rem; border: 1px solid var(--line-strong); border-radius: 7px; color: var(--text); background: var(--surface);
}
.row-images { grid-column: 1 / -1; }
.ref-thumb { max-height: 110px; margin-right: 6px; border: 1px solid var(--line); border-radius: 7px; }
.img-match-note { font-size: 0.72rem; color: var(--text-2); margin-bottom: 4px; }
fieldset.f-evidence { grid-column: 1 / -1; border: 1px solid var(--line); border-radius: 7px; display: flex; flex-wrap: wrap; gap: 8px; }
.chk { flex-direction: row !important; align-items: center; font-size: 0.8rem; }
.removeRow, .removeMissed { cursor: pointer; background: none; border: none; color: var(--rose); font-weight: 700; }

.claim-img {
  width: 150px; height: 150px; object-fit: contain; background: #fff;
  border: 1px solid var(--line); border-radius: var(--radius-sm); margin: 4px 0;
}
.claim-sources { font-weight: 400; color: var(--text-2); font-size: 0.75rem; }
.claim-fact { grid-column: 1 / -1; font-size: 0.85rem; }
.claim-shade { margin-top: -6px; margin-bottom: 6px; }
.shade-missing { color: var(--rose); font-weight: 600; }
.shade-unresolved { color: var(--text-2); font-style: italic; }
.claim-empty { background: var(--surface); box-shadow: var(--shadow); padding: 16px; border-radius: var(--radius); color: var(--text-2); }
.missed-card { border: 2px dashed var(--rose); }

/* True fixed side panel (not a flex item that flows with content) -- it used to
   sit inline after the player/rows columns, so once a few product rows existed
   it got pushed far below the fold with no visual cue that it had opened at all. */
#anleitungPanel {
  position: fixed; top: 60px; right: 0; bottom: 0;
  width: min(480px, 100vw); background: var(--surface); box-shadow: var(--shadow);
  padding: 16px; padding-bottom: 90px; overflow-y: auto; z-index: 20;
  border-left: 1px solid var(--line);
}
.anleitungHead { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
#anleitungClose { background: none; border: none; font-size: 1.2rem; cursor: pointer; color: var(--text-2); }
.anleitungCols { display: flex; flex-direction: column; gap: 24px; }
.anleitungCols pre { white-space: pre-wrap; font-family: inherit; font-size: 0.82rem; }

/* Part L10 addendum: same fixed-side-panel shell as #anleitungPanel above. */
#brokenVideosToggle {
  background: var(--hl-blush); color: var(--rose); border: none; border-radius: var(--radius-sm);
  padding: 6px 12px; font-size: 0.8rem; cursor: pointer; font-weight: 600;
}
#brokenVideosPanel {
  position: fixed; top: 60px; right: 0; bottom: 0;
  width: min(480px, 100vw); background: var(--surface); box-shadow: var(--shadow);
  padding: 16px; padding-bottom: 90px; overflow-y: auto; z-index: 20;
  border-left: 1px solid var(--line);
}
.broken-videos-hint { font-size: 0.82rem; color: var(--text-2); margin: 0 0 12px; }
.broken-video-row {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding: 10px 0; border-bottom: 1px solid var(--line);
}
.broken-video-row-text { font-size: 0.82rem; }
.broken-video-row-id { font-weight: 700; }
.broken-video-row-caption { color: var(--text-2); display: block; margin-top: 2px; }
.broken-video-clear-btn {
  background: var(--surface); color: var(--purple); border: 1px solid var(--purple); border-radius: var(--radius-sm);
  padding: 6px 10px; font-size: 0.76rem; font-weight: 600; cursor: pointer; flex-shrink: 0;
}
.broken-video-clear-btn:hover { background: var(--hl-purple); }
.broken-video-clear-btn:disabled { opacity: 0.5; cursor: default; }
.broken-videos-empty { font-size: 0.82rem; color: var(--text-2); }

/* ---------- progress bar (one only, per L3) ---------- */
#progressBarOuter { width: 120px; height: 6px; background: var(--hl-purple); border-radius: 999px; overflow: hidden; }
#progressBarInner { height: 100%; background: var(--purple); width: 0%; }
#progressBarLabel { font-size: 0.72rem; color: var(--text-2); margin-top: 2px; }

/* ---------- first-choice buttons (label mode) ---------- */
#firstChoiceBtns { display: flex; flex-direction: column; gap: 8px; margin-top: 12px; }
.firstChoiceBtn { padding: 12px; font-size: 0.92rem; font-weight: 700; border-radius: var(--radius-sm); border: 1px solid var(--line-strong); cursor: pointer; background: var(--surface); }
.firstChoiceBtn.fc-none { background: var(--hl-blush); color: var(--rose); border: none; }
.firstChoiceBtn.fc-unclear { background: var(--hl-purple); color: var(--purple); border: none; }
.firstChoiceBtn.fc-add { background: var(--purple); color: #fff; border: none; }

/* ---------- tap chips (state / evidence / yes-no) ---------- */
.tap-chips { display: flex; flex-wrap: wrap; gap: 6px; margin: 4px 0 10px; }
.tap-chip { padding: 8px 14px; border-radius: 999px; border: 1px solid var(--line-strong); background: var(--surface); color: var(--text); font-size: 0.82rem; cursor: pointer; font-weight: 600; }
.tap-chip.selected { background: var(--purple); color: #fff; border-color: var(--purple); }
.tap-chip.tap-chip-sm { padding: 6px 12px; font-size: 0.78rem; }

/* ---------- click-to-open dropdown pickers (Part L4 product/shade pickers) ----------
   Deliberately a <button> trigger, not a text input -- clicking it opens the
   full list immediately with no typing required (the L4 brief's headline fix
   for the old type-to-search autocomplete, which listed hashtags/aliases as
   selectable rows). The optional filter text inside the open panel narrows
   which canonical rows are visible; it can never surface a row of its own. */
/* min-width: 0 on every link in this chain: a flex item's default min-width
   is "auto" (its content's un-wrapped size), which overrides overflow/
   text-overflow and lets a long product/bundle title (common in this
   catalog) push the button wider than its card -- and, inside a row-card's
   column-flex <label>, wider than the viewport itself on mobile. Found live
   by a fresh-eyes reviewer (390px width, a 44+ char product title). */
.dd-wrap { position: relative; grid-column: 1 / -1; min-width: 0; }
.dd-trigger {
  display: flex; align-items: center; gap: 8px; width: 100%; min-width: 0; min-height: 44px;
  padding: 6px 10px; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  background: var(--surface); color: var(--text); font-size: 0.88rem; cursor: pointer; text-align: left;
}
.dd-trigger:disabled { opacity: 0.5; cursor: not-allowed; }
.dd-trigger-label { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.dd-trigger-label.dd-placeholder { color: var(--text-2); }
.dd-caret { color: var(--text-2); flex-shrink: 0; }
.dd-panel {
  position: absolute; top: 100%; left: 0; right: 0; margin-top: 4px; background: var(--surface);
  border: 1px solid var(--line-strong); border-radius: var(--radius-sm); box-shadow: var(--shadow);
  z-index: 30; padding: 8px;
}
.dd-filter {
  width: 100%; padding: 8px 10px; margin-bottom: 6px; border: 1px solid var(--line-strong);
  border-radius: 7px; font-size: 0.85rem; background: var(--surface); color: var(--text);
}
.dd-list { max-height: 300px; overflow-y: auto; }
.dd-group-label { font-size: 0.7rem; color: var(--text-2); text-transform: uppercase; letter-spacing: .04em; padding: 8px 6px 4px; }
.dd-item { display: flex; align-items: center; gap: 10px; padding: 8px 6px; border-radius: 7px; cursor: pointer; font-size: 0.88rem; min-height: 44px; }
.dd-item:hover { background: var(--hl-purple); }
.dd-empty { padding: 12px 6px; font-size: 0.85rem; color: var(--text-2); }
.dd-other-input {
  grid-column: 1 / -1; padding: 8px 10px; border: 1px solid var(--line-strong); border-radius: 7px;
  font-size: 0.88rem; margin-top: -2px;
}

/* Dropdown-row / trigger thumbnails: never squeezed, undersized images
   letterboxed on a white square (Part L4 image sizing rule). */
.dd-thumb, .dd-trigger-thumb {
  width: 56px; height: 56px; object-fit: contain; background: #fff;
  border: 1px solid var(--line); border-radius: 7px; flex-shrink: 0;
}
.dd-thumb-empty { background: var(--bg); border-style: dashed; }
.dd-swatch { border-radius: 50%; } /* a real averaged colour (or none) -- never a fabricated one, see catalogFamilies.mjs's swatch_hex_or_null */
.dd-selected-swatch { width: 18px; height: 18px; border-radius: 50%; border: 1px solid var(--line-strong); flex-shrink: 0; }
.dd-item-trailing { border-top: 1px dashed var(--line); margin-top: 4px; padding-top: 12px; color: var(--text-2); font-style: italic; }

/* ---------- more details collapsible ---------- */
.moreDetailsToggle { grid-column: 1/-1; background: none; border: none; color: var(--purple); font-weight: 600; font-size: 0.8rem; cursor: pointer; text-align: left; padding: 4px 0; }
.moreDetails { grid-column: 1/-1; display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 8px; border-top: 1px dashed var(--line); padding-top: 8px; margin-top: 4px; }
.moreDetails[hidden] { display: none; }
/* "Card" images -- the chosen product/shade preview and the review claim's
   detector image -- never squeezed: object-fit contain on a white square, at
   least Instagram-profile-picture size (150px) on desktop, 96px on mobile
   (Part L4). */
.selected-image {
  width: 150px; height: 150px; object-fit: contain; background: #fff;
  border-radius: var(--radius-sm); border: 1px solid var(--line); margin: 4px 0; grid-column: 1/-1;
}
.first-seen-line { grid-column: 1/-1; font-size: 0.78rem; color: var(--text-2); display: flex; align-items: center; gap: 8px; }
.first-seen-line a { cursor: pointer; }

/* ---------- toast ---------- */
#toast {
  position: fixed; bottom: 96px; left: 50%; transform: translateX(-50%); background: var(--text); color: #fff;
  padding: 10px 20px; border-radius: 999px; font-size: 0.85rem; z-index: 40; box-shadow: var(--shadow);
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap; justify-content: center; max-width: min(92vw, 480px);
}
/* Part L16: the required, one-tap skip-reason picker -- replaces #footerSkipBtn
   in place (same slot in #nextVideoBlock) rather than opening a modal, so it
   never blocks or covers the video. Any chip (including "Other") completes
   the skip in one tap; Cancel returns to the plain Skip button. */
#skipReasonPicker {
  display: flex; flex-direction: column; align-items: center; gap: 8px; width: 100%;
}
#skipReasonHint { font-size: 0.85rem; font-weight: 700; color: var(--text); }
#skipReasonChips { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.skip-reason-btn {
  background: var(--surface); color: var(--purple); border: 2px solid var(--purple);
  border-radius: 999px; padding: 6px 12px; font-size: 0.82rem; font-weight: 600; cursor: pointer;
}
.skip-reason-btn:hover { background: var(--hl-purple); }
#skipReasonNote {
  width: 100%; max-width: 320px; padding: 7px 10px; font-size: 0.82rem;
  border: 1px solid var(--line); border-radius: var(--radius-sm); font-family: inherit;
}
#skipReasonCancel {
  background: none; color: var(--text-2); border: none; font-size: 0.78rem;
  text-decoration: underline; cursor: pointer; padding: 2px;
}

/* ---------- Part L-broken-video: unplayable-video overlay ---------- */
/* Fixed, not scoped to either mode's player column -- shown/hidden purely by
   JS depending on which player (#player or #reviewPlayer) is currently
   failing, see app.js's watchPlayerHealth(). */
#playerBrokenOverlay {
  position: fixed; inset: 0; background: rgba(0,0,0,0.45); z-index: 50;
  display: flex; align-items: center; justify-content: center; padding: 16px;
}
#playerBrokenCard {
  max-width: 420px; text-align: center; padding: 24px;
}
#playerBrokenCard h3 { margin: 0 0 8px; color: var(--rose); }
#playerBrokenCard p { margin: 0 0 16px; color: var(--text-2); font-size: 0.9rem; }
#playerBrokenSkipBtn {
  background: var(--purple); color: #fff; border: none; border-radius: var(--radius-sm);
  padding: 10px 24px; font-size: 0.92rem; font-weight: 700; cursor: pointer;
}

/* ---------- Part L18: Mode B pending-question banner ---------- */
/* Prominent (solid purple, not just an accent border) but not alarming --
   this is a real customer waiting, not an error state, so it deliberately
   does NOT reuse the --rose "wrong/broken" colour used elsewhere on this
   page. [hidden] (see the global reset above) keeps this fully out of the
   way for every other video -- no layout reserved when absent. */
#pendingQuestionBanner {
  background: var(--purple); color: #fff; border-radius: var(--radius-sm);
  padding: 12px 14px; margin: 0 0 12px;
}
#pendingQuestionLabel { font-size: 0.72rem; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; opacity: .85; margin-bottom: 4px; }
.pendingQuestionLine { font-size: 0.95rem; font-weight: 600; margin-bottom: 4px; }
.pendingQuestionLine:last-of-type { margin-bottom: 0; }
#pendingQuestionHint { font-size: 0.78rem; opacity: .85; margin-top: 6px; }

/* ---------- Part L5a: review timeline / X-Ray strip / per-card review state ---------- */
#reviewProgressLine { font-size: 0.85rem; font-weight: 700; margin: 0 0 8px; color: var(--purple); }

#xrayStrip { display: flex; gap: 8px; overflow-x: auto; padding: 8px 0; }
.xray-chip {
  display: flex; align-items: center; gap: 6px; flex-shrink: 0; padding: 5px 10px 5px 5px;
  border-radius: 999px; background: var(--hl-purple); color: var(--purple); border: none;
  cursor: pointer; font-size: 0.78rem; font-weight: 600; white-space: nowrap;
}
.xray-chip img, .xray-chip .xray-chip-empty { width: 28px; height: 28px; object-fit: contain; background: #fff; border-radius: 50%; flex-shrink: 0; }
#xrayStripEmpty { font-size: 0.78rem; color: var(--text-2); padding: 8px 0; }

/* height is overridden inline per video (more lanes when several products share
   the same time window -- see renderTimeline()'s lane assignment); 16px is just
   the single-lane default/starting value before that JS runs. border-radius drops
   to a flatter value than the height-16px default once multi-lane, handled inline
   alongside height in renderTimeline() would be overkill -- a slightly-too-round
   corner on a tall multi-lane bar is a cosmetic non-issue. */
#reviewTimeline { position: relative; height: 16px; background: var(--hl-purple); border-radius: 8px; margin: 4px 0 8px; cursor: pointer; }
#reviewTimelineBar { position: relative; width: 100%; height: 100%; }
/* top/height are set per-segment by renderTimeline()'s lane assignment (JS) --
   omitted here since inline styles always win; box-sizing keeps a 1px inset
   border from pushing a lane's segment taller than its assigned lane slot. */
.timeline-seg { position: absolute; box-sizing: border-box; border: 1px solid var(--surface); border-radius: 3px; opacity: 0.85; min-width: 2px; }
.timeline-seg:hover { opacity: 1; }
#reviewTimelinePlayhead { position: absolute; top: -3px; bottom: -3px; width: 2px; background: var(--text); pointer-events: none; left: 0; }

#reviewTogglesRow { display: flex; flex-wrap: wrap; gap: 4px 16px; margin: 4px 0 8px; }
#xrayToggleWrap, #boxToggleWrap { display: flex; align-items: center; gap: 6px; font-size: 0.82rem; color: var(--text-2); cursor: pointer; margin: 0; }
#xrayToggleWrap input, #boxToggleWrap input { cursor: pointer; }

#xrayEmptyHint { background: var(--surface); box-shadow: var(--shadow); padding: 14px; border-radius: var(--radius); color: var(--text-2); font-size: 0.85rem; margin-bottom: 12px; }

/* ---------- Part L9: sticky editing + in-frame-only card list ---------- */
/* A pinned card renders exactly where an ordinary card would -- this is
   deliberately a marker, not a relocation/highlight-box treatment (that's
   .kbd-highlight's own job) -- just enough to read as "actively being
   edited, staying put on purpose" at a glance. */
.claim-card-pinned { border: 2px solid var(--purple); }
.pinned-badge {
  background: var(--hl-purple); color: var(--purple); border-radius: 999px;
  padding: 2px 8px; font-size: 0.68rem; font-weight: 600; margin-left: 6px; text-transform: none;
}
.headerNotInVideoBtn, .discardEditsBtn {
  background: none; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  padding: 4px 10px; font-size: 0.72rem; font-weight: 600; cursor: pointer; color: var(--text-2);
}
.headerNotInVideoBtn:hover { color: var(--rose); border-color: var(--rose); }
.discardEditsBtn:hover { color: var(--text); border-color: var(--text); }

/* Problem 2's collapsed overflow bar -- everything not pinned and not in
   frame right now (including what used to be the always-visible "Not
   timed" section) lives here instead, one line per claim. */
#notVisibleGroup { margin-top: 16px; border-top: 1px dashed var(--line-strong); padding-top: 12px; }
#notVisibleToggle { padding: 8px 4px; font-weight: 600; font-size: 0.85rem; }
#notVisibleCards { display: flex; flex-direction: column; gap: 6px; margin: 8px 0; }
.not-visible-row {
  display: grid; grid-template-columns: 32px 1fr auto auto auto auto; align-items: center; gap: 8px;
  background: var(--surface); border-radius: var(--radius-sm); padding: 6px 10px; font-size: 0.8rem;
}
.nv-thumb { width: 32px; height: 32px; object-fit: contain; border-radius: 5px; background: #fff; border: 1px solid var(--line); }
.nv-thumb-empty { display: block; background: var(--bg); }
.nv-name { font-weight: 600; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.nv-shade { color: var(--text-2); font-size: 0.75rem; }
.nv-conf { color: var(--text-2); font-size: 0.75rem; }
.nv-action {
  border: 1px solid var(--line-strong); border-radius: 999px; background: var(--surface);
  padding: 5px 10px; font-size: 0.74rem; font-weight: 600; cursor: pointer; white-space: nowrap;
}
.nv-not-in-video:hover { color: var(--rose); border-color: var(--rose); }
.nv-open:hover { color: var(--purple); border-color: var(--purple); }
#markAllNotInVideoBtn {
  background: none; border: 1px solid var(--line-strong); border-radius: var(--radius-sm);
  padding: 8px; font-size: 0.8rem; font-weight: 600; cursor: pointer; color: var(--text-2); width: 100%;
}
#markAllNotInVideoBtn:hover { color: var(--rose); border-color: var(--rose); }

.claim-card.card-highlight { outline: 3px solid var(--purple); outline-offset: 2px; }

/* Change #5: PERSISTENT (not a 1.5s timeout like .card-highlight above) outline
   marking which card 1/2/3/Enter currently act on -- must stay visible with no
   interaction, since arrow keys are the only way to see where it moved. */
.claim-card.kbd-highlight { outline: 3px solid var(--teal); outline-offset: 2px; }

/* Change #2: brief sink-away transition on first Submit, before the card
   actually relocates into the Reviewed group via the next renderClaimCards(). */
.claim-card.card-sinking { transition: opacity .3s ease, transform .3s ease; opacity: 0; transform: translateY(18px) scale(0.97); }

#reviewedGroup { margin-top: 4px; }
#reviewedGroupToggle { padding: 8px 4px; font-weight: 600; }
#reviewedCards > .claim-card { opacity: 0.9; }

/* Change #4: grey, plainly-worded flag on an X-Ray chip for a product whose
   ONLY active timing right now comes from on-screen-text/transcript (t1/t2) --
   never a vision hit (v2/boxes) -- so a reviewer doesn't mistake a text mention
   for a confirmed sighting. */
.xray-mentioned-tag {
  background: var(--bg); color: var(--text-2); border-radius: 999px; padding: 1px 8px;
  font-size: 0.68rem; font-weight: 600; margin-left: 2px;
}

.reviewed-row { grid-column: 1/-1; display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.reviewed-chip { border-radius: 999px; padding: 6px 14px; font-size: 0.82rem; font-weight: 700; }
.reviewed-chip.rv-correct { background: var(--hl-mint); color: var(--teal); }
.reviewed-chip.rv-wrong { background: var(--hl-blush); color: var(--rose); }
.reviewed-chip.rv-unsure { background: var(--hl-purple); color: var(--purple); }
.editReviewedBtn { background: none; border: 1px solid var(--line-strong); border-radius: var(--radius-sm); padding: 6px 12px; font-size: 0.8rem; cursor: pointer; color: var(--text); }

.cardSubmitBtn {
  grid-column: 1/-1; background: var(--purple); color: #fff; border: none; border-radius: var(--radius-sm);
  padding: 10px; font-size: 0.88rem; font-weight: 700; cursor: pointer; margin-top: 4px;
}
.cardSubmitBtn:disabled { opacity: 0.5; cursor: not-allowed; }

#footerReviewHint { font-size: 0.75rem; color: var(--text-2); text-align: center; max-width: 480px; }

/* ---------- review: verdict buttons + wrong-reveal chips + details toggle ---------- */
.verdict-btns { grid-column: 1/-1; display: flex; gap: 8px; }
.verdict-btn { flex: 1; padding: 12px; border-radius: var(--radius-sm); border: none; font-weight: 700; cursor: pointer; font-size: 0.88rem; }
.verdict-btn.vb-correct { background: var(--hl-mint); color: var(--teal); }
.verdict-btn.vb-wrong { background: var(--hl-blush); color: var(--rose); }
.verdict-btn.vb-unsure { background: var(--hl-purple); color: var(--purple); }
.verdict-btn.selected.vb-correct { background: var(--teal); color: #fff; }
.verdict-btn.selected.vb-wrong { background: var(--rose); color: #fff; }
.verdict-btn.selected.vb-unsure { background: var(--purple); color: #fff; }
.wrong-reveal { grid-column: 1/-1; display: flex; gap: 6px; flex-wrap: wrap; }
.also-detected-label { grid-column: 1/-1; font-size: 0.72rem; color: var(--text-2); text-transform: uppercase; letter-spacing: .04em; margin-top: 4px; }
.detailsToggle { grid-column: 1/-1; background: none; border: none; color: var(--text-2); font-size: 0.75rem; cursor: pointer; text-align: left; }
.detailsBox { grid-column: 1/-1; font-size: 0.75rem; color: var(--text-2); background: var(--bg); border-radius: 7px; padding: 8px; }
.detailsBox[hidden] { display: none; }

/* ---------- confidence bar (Part L4, label mode only) ----------
   Lives in the normal page flow (playerCol), NOT the fixed sticky footer --
   only Submit/Skip need to stay pinned; a fixed-position confidence bar would
   grow the footer's own height (it wraps to 2 rows on narrow phones) and
   could cover other controls underneath it on short mobile viewports. */
#confidenceRow {
  width: 100%; margin-top: 12px; padding: 12px; background: var(--surface);
  border-radius: var(--radius); box-shadow: var(--shadow);
}
#confidenceLabel { font-size: 0.8rem; font-weight: 700; margin-bottom: 8px; }
#confidenceHint { font-weight: 400; color: var(--text-2); }
#confidenceSegs { display: flex; flex-wrap: wrap; gap: 6px; justify-content: center; }
.confidence-seg {
  flex: 1 1 auto; min-width: 34px; min-height: 44px; border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong); background: var(--surface); color: var(--text);
  font-size: 0.9rem; font-weight: 700; cursor: pointer;
}
.confidence-seg.selected { background: var(--purple); color: #fff; border-color: var(--purple); }

#footerLabelHint { font-size: 0.75rem; color: var(--rose); text-align: center; max-width: 480px; }

/* ---------- Part L7: visible-span control (Part L8: fixed) ---------- */
.span-control { grid-column: 1/-1; }
.span-row { margin-bottom: 8px; }
/* 36px tall so a >=24px thumb (26px used here) has real room above/below it,
   not just a bare minimum -- easier to grab with a mouse, and the real
   requirement for touch (a finger is a much fatter "pointer" than a mouse). */
.span-slider { position: relative; height: 36px; margin: 2px 0; cursor: pointer; }
.span-track {
  position: absolute; top: 50%; left: 0; right: 0; height: 4px; margin-top: -2px;
  background: var(--hl-purple); border-radius: 2px;
}
.span-range-fill {
  position: absolute; top: 50%; height: 4px; margin-top: -2px; background: var(--purple); border-radius: 2px;
}
.span-range-fill.span-range-fill-incomplete { background: repeating-linear-gradient(135deg, var(--lavender), var(--lavender) 6px, transparent 6px, transparent 12px); }
/* Two overlapping native range inputs, one per handle -- pointer-events:none on
   the input itself (so clicks/taps land on whichever track is underneath
   instead of fighting the other handle), re-enabled only on the thumb
   pseudo-element, which is the only part a real click/drag/touch should ever
   grab -- this is also what makes native touch-dragging just work with no
   extra JS. touch-action:none stops the browser's own scroll-gesture
   recognizer from competing with a drag started on the thumb. */
.span-slider input[type=range] {
  position: absolute; top: 0; left: 0; width: 100%; height: 36px; margin: 0;
  background: transparent; -webkit-appearance: none; appearance: none; pointer-events: none; touch-action: none;
}
.span-slider input[type=range]::-webkit-slider-runnable-track { background: transparent; height: 36px; }
.span-slider input[type=range]::-moz-range-track { background: transparent; height: 36px; }
.span-slider input[type=range]::-webkit-slider-thumb {
  -webkit-appearance: none; pointer-events: auto; width: 26px; height: 26px; margin-top: 5px;
  border-radius: 50%; background: var(--purple); border: 2px solid #fff; box-shadow: var(--shadow); cursor: pointer;
}
.span-slider input[type=range]::-moz-range-thumb {
  pointer-events: auto; width: 26px; height: 26px; border-radius: 50%;
  background: var(--purple); border: 2px solid #fff; box-shadow: var(--shadow); cursor: pointer;
}
.span-slider input[type=range]:focus-visible::-webkit-slider-thumb { outline: 2px solid var(--teal); outline-offset: 2px; }
.span-slider input[type=range]:focus-visible::-moz-range-thumb { outline: 2px solid var(--teal); outline-offset: 2px; }
.span-slider input.span-handle-end { z-index: 2; } /* grabbable even when equal to the start handle */
.span-slider input.span-handle-incomplete::-webkit-slider-thumb { background: var(--surface); border-color: var(--rose); }
.span-slider input.span-handle-incomplete::-moz-range-thumb { background: var(--surface); border-color: var(--rose); }
.span-note-unavailable { grid-column: 1/-1; font-size: 0.8rem; color: var(--text-2); font-style: italic; padding: 6px 0; }
.span-info { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; font-size: 0.78rem; }
.span-time-label { font-weight: 600; min-width: 108px; }
.span-set-btn {
  background: var(--hl-purple); color: var(--purple); border: none; border-radius: 7px;
  padding: 5px 9px; font-size: 0.74rem; font-weight: 600; cursor: pointer;
}
.span-remove { background: none; border: none; color: var(--rose); font-weight: 700; cursor: pointer; margin-left: auto; }
.span-add-btn {
  background: none; border: 1px dashed var(--line-strong); border-radius: 7px; color: var(--purple);
  padding: 6px 10px; font-size: 0.78rem; font-weight: 600; cursor: pointer; width: 100%; margin-top: 2px;
}
.span-hint, .span-required-hint { font-size: 0.75rem; color: var(--rose); margin-top: 4px; }

/* ---------- Part L7: AI confidence line + legend + box colour toggle ---------- */
.ai-confidence-line { grid-column: 1/-1; }
.uncalibrated-tag {
  background: var(--bg); color: var(--text-2); border-radius: 999px; padding: 1px 8px;
  font-size: 0.68rem; font-weight: 600; margin-left: 2px;
}
#boxColorModeRow { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin: 6px 0 4px; font-size: 0.78rem; }
.boxColorModeLabel { color: var(--text-2); }
.colorModeBtn {
  background: var(--surface); color: var(--text-2); border: 1px solid var(--line-strong); border-radius: 999px;
  padding: 4px 11px; font-size: 0.76rem; font-weight: 600; cursor: pointer;
}
.colorModeBtn.active { background: var(--purple); color: #fff; border-color: var(--purple); }
.confidence-legend { margin: 4px 0 8px; }
.confidence-legend-label { font-size: 0.72rem; color: var(--text-2); margin-bottom: 3px; }
.confidence-legend-swatches { display: flex; gap: 2px; }
.cl-swatch { flex: 1 1 0; height: 10px; border-radius: 2px; }
.confidence-legend-scale { display: flex; justify-content: space-between; font-size: 0.68rem; color: var(--text-2); margin-top: 2px; }

/* SVG box-identity chip (confidence colour mode only) -- a thin white stroke
   keeps it visible against a label background that can itself be any hue in
   the confidence gradient. */
.box-identity-chip { stroke: #fff; stroke-width: 1; }

/* ---------- QUESTIONS mode (Part L23) -- deliberately minimal ---------- */
#questionsHint { margin-top: 8px; font-size: 0.85rem; color: var(--text-2); }
#questionsCommentsCol { flex: 1 1 480px; min-width: 280px; }
.question-comment-row {
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  padding: 12px; border: 1px solid var(--line); border-radius: var(--radius-sm);
  background: var(--surface); margin-bottom: 8px;
}
.question-comment-text { flex: 1 1 auto; font-size: 0.92rem; }
.question-comment-creator-tag { color: var(--text-2); font-weight: 600; font-size: 0.8rem; }
.question-comment-btns { display: flex; gap: 6px; flex: 0 0 auto; }
.question-verdict-btn {
  padding: 8px 12px; font-size: 0.82rem; font-weight: 700; border-radius: var(--radius-sm);
  border: 1px solid var(--line-strong); cursor: pointer; background: var(--surface);
}
.question-verdict-btn.active { background: var(--purple); color: #fff; border-color: var(--purple); }
#questionsSubmitBtn {
  width: 100%; padding: 14px; font-size: 0.95rem; font-weight: 700; border-radius: var(--radius-sm);
  border: none; background: var(--purple); color: #fff; cursor: pointer; margin-top: 8px;
}
#questionsSubmitBtn:disabled { opacity: 0.4; cursor: not-allowed; }

/* ---------- mobile ---------- */
@media (max-width: 640px) {
  #appHeader { flex-wrap: wrap; height: auto; padding: 10px 12px; gap: 8px; }
  #headerRight { margin-left: 0; width: 100%; justify-content: space-between; }
  #playerCol, #questionsPlayerCol { flex-basis: 100%; }
  #rowsCol, #claimsCol, #questionsCommentsCol { flex-basis: 100%; }
  .row-card, .claim-card, .moreDetails { grid-template-columns: minmax(0, 1fr); }
  #stickyFooter { padding: 10px 12px; }
  .stat-row { gap: 6px; }
  .stat-tile { flex-basis: 30%; }
  .selected-image, .claim-img { width: 96px; height: 96px; }
  #confidenceSegs { gap: 4px; }
  .confidence-seg { flex-basis: 18%; min-width: 30px; }
  .question-comment-row { flex-direction: column; align-items: stretch; }
}
