/* ───────────────────────────────────────────────────────────────────────────
   Kontraframe — dark review theme.

   Everything derives from the tokens below: surfaces are a four-step ramp
   (page → panel → raised → hover) rather than flat fills, so depth comes from
   the ramp plus a border, not from heavy shadows. Retheme by editing tokens.
   ─────────────────────────────────────────────────────────────────────────── */
:root {
  color-scheme: dark;

  /* surfaces, darkest to lightest */
  --bg: #0f1115;
  --surface: #16181e;
  --surface-2: #1c1f26;
  --surface-3: #23272f;

  /* text */
  --text: #edeff3;
  --text-dim: #8b929e;
  --text-faint: #626976;

  /* lines */
  --border: #262a32;
  --border-strong: #343a45;

  /* accents */
  --accent: #6b93ff;
  --accent-hover: #7fa2ff;
  --accent-soft: rgba(107, 147, 255, 0.14);
  --ok: #3ecf8e;
  --ok-soft: rgba(62, 207, 142, 0.14);
  --warn: #f5a623;
  --warn-soft: rgba(245, 166, 35, 0.14);
  --danger: #ff5a67;
  --danger-soft: rgba(255, 90, 103, 0.14);

  /* radii */
  --r-sm: 6px;
  --r: 10px;
  --r-lg: 14px;
  --r-pill: 999px;

  /* elevation */
  --shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.35);
  --shadow-md: 0 6px 20px -6px rgba(0, 0, 0, 0.5);
  --shadow-lg: 0 28px 70px -16px rgba(0, 0, 0, 0.7);

  /* motion */
  --ease: cubic-bezier(0.4, 0, 0.2, 1);
  --fast: 0.12s;
  --med: 0.2s;

  --ring: 0 0 0 3px rgba(107, 147, 255, 0.35);
  --font: "Inter", system-ui, -apple-system, sans-serif;

  /* Kept so any older rule or snippet referring to these still resolves. */
  --panel: var(--surface);
  --panel-2: var(--surface-2);
  --radius: var(--r);
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: 14px;
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* Lining figures keep timecodes and counts from jittering. */
  font-variant-numeric: tabular-nums;
}

#app { height: 100%; display: flex; flex-direction: column; }

::selection { background: rgba(107, 147, 255, 0.3); }

/* Thin, unobtrusive scrollbars — the default ones are heavy on dark panels. */
* { scrollbar-width: thin; scrollbar-color: var(--surface-3) transparent; }
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--surface-3); border-radius: var(--r-pill);
  border: 3px solid transparent; background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover { background: var(--border-strong); background-clip: content-box; }

/* One visible focus treatment for every control, keyboard only. */
:focus-visible { outline: none; box-shadow: var(--ring); border-radius: var(--r-sm); }

h1 { font-size: 21px; font-weight: 650; letter-spacing: -0.02em; margin: 0 0 16px; }
h2 { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; margin: 0; }
h3 { font-size: 15px; font-weight: 600; letter-spacing: -0.01em; margin: 0 0 12px; }
h4 {
  font-size: 11px; font-weight: 600; margin: 20px 0 8px; color: var(--text-faint);
  text-transform: uppercase; letter-spacing: 0.08em;
}
code {
  background: var(--surface-2); color: var(--text-dim);
  padding: 2px 6px; border-radius: var(--r-sm); font-size: 12px;
  border: 1px solid var(--border);
}

.dim { color: var(--text-dim); }
.spacer { flex: 1; }
.hidden { display: none !important; }
.hint { font-size: 12.5px; line-height: 1.5; margin: 4px 0 12px; }
.empty-note { padding: 32px 16px; text-align: center; color: var(--text-faint); }
.error-note {
  color: var(--danger); background: var(--danger-soft);
  border: 1px solid rgba(255, 90, 103, 0.25); border-radius: var(--r);
  padding: 14px 16px; margin: 16px;
}

/* ── header ── */
.app-header {
  display: flex; align-items: center; gap: 14px;
  padding: 0 20px; height: 54px; flex: none;
  background: color-mix(in srgb, var(--surface) 85%, transparent);
  backdrop-filter: blur(12px);
  border-bottom: 1px solid var(--border);
  position: relative; z-index: 10;
}
.brand {
  display: inline-flex; align-items: center; gap: 9px;
  font-weight: 650; font-size: 15px; letter-spacing: -0.01em;
  color: var(--text); text-decoration: none;
}
.brand::before {
  content: "▶";
  color: var(--accent); font-size: 9px; line-height: 1;
  background: var(--accent-soft); border-radius: var(--r-sm);
  width: 22px; height: 22px; display: inline-flex;
  align-items: center; justify-content: center;
  padding-left: 2px; /* optical centring of the triangle */
}

/* ── app shell: sidebar + routed content ── */
.app-body { flex: 1; min-height: 0; display: flex; }
main#main { flex: 1; min-width: 0; min-height: 0; display: flex; flex-direction: column; }

/* A persistent project list, so switching projects is a click in place
   rather than a trip back through the grid. */
.sidebar {
  flex: none; width: 218px; display: flex; flex-direction: column;
  padding: 12px 10px; gap: 2px;
  background: var(--surface); border-right: 1px solid var(--border);
  overflow-y: auto;
}
.sidebar-head {
  display: flex; align-items: center; justify-content: space-between;
  margin: 16px 8px 4px;
}
.sidebar-head h4 { margin: 0; }
.sidebar-add {
  font: inherit; font-size: 16px; line-height: 1; font-weight: 400;
  color: var(--text-faint); background: none; border: none; cursor: pointer;
  width: 20px; height: 20px; border-radius: var(--r-sm);
  display: inline-flex; align-items: center; justify-content: center;
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.sidebar-add:hover { background: var(--surface-2); color: var(--text); }

.sidebar-list { display: flex; flex-direction: column; gap: 1px; min-height: 0; }
.sidebar-item {
  display: flex; align-items: center; gap: 9px; width: 100%;
  font: inherit; font-size: 13px; text-align: left; cursor: pointer;
  color: var(--text-dim); background: none; border: none;
  padding: 7px 8px; border-radius: var(--r-sm);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.sidebar-item:hover { background: var(--surface-2); color: var(--text); }
.sidebar-item.active { background: var(--accent-soft); color: var(--accent); }
/* currentColor, so the icon follows the same hover/active colour as the label
   beside it without a second rule to keep in step. */
.sidebar-item .icon-16 { flex: none; width: 15px; height: 15px; opacity: 0.9; }
.sidebar-item-label {
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}

/* The disclosure triangle sits beside the project row rather than inside it,
   so clicking it (expand/collapse) and clicking the row itself (open the
   project) stay two separate gestures rather than one button trying to be
   both. */
.sidebar-row { display: flex; align-items: center; }
.sidebar-row .sidebar-item { flex: 1; min-width: 0; }
.sidebar-toggle {
  flex: none; width: 20px; height: 28px; margin-right: -2px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  color: var(--text-faint); background: none; border: none; cursor: pointer;
  border-radius: var(--r-sm);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.sidebar-toggle:hover { background: var(--surface-2); color: var(--text); }
.icon-chevron { width: 10px; height: 10px; transition: transform var(--fast) var(--ease); }
.sidebar-toggle.expanded .icon-chevron { transform: rotate(90deg); }

/* A project's folders, one level deep and indented under it — loaded the
   first time the triangle opens, kept afterward. */
.sidebar-subtree { display: flex; flex-direction: column; gap: 1px; margin-bottom: 2px; }
.sidebar-item-group { padding-left: 30px; font-size: 12.5px; }
.sidebar-empty, .sidebar-error {
  margin: 3px 8px 5px 30px; font-size: 12px; color: var(--text-faint);
}
.sidebar-error { color: var(--danger); }

/* The player wants the width back; it drops the sidebar the same way the
   comments panel already gives up its column on a narrow screen. */
.app-body.review-mode .sidebar { display: none; }

@media (max-width: 900px) {
  .sidebar { display: none; }
}

/* ── buttons ── */
.btn {
  font: inherit; font-weight: 500; color: var(--text);
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 7px 13px; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  white-space: nowrap;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease),
              transform var(--fast) var(--ease);
}
.btn:hover { background: var(--surface-3); border-color: var(--border-strong); }
.btn:active { transform: translateY(1px); }

.btn-primary {
  background: var(--accent); border-color: var(--accent); color: #0c1220;
  font-weight: 600; box-shadow: var(--shadow-sm);
}
.btn-primary:hover { background: var(--accent-hover); border-color: var(--accent-hover); }

.btn-danger {
  background: var(--danger); border-color: var(--danger); color: #fff;
  font-weight: 600; box-shadow: var(--shadow-sm);
}
.btn-danger:hover { background: #ff6d78; border-color: #ff6d78; }

.btn-sm { padding: 5px 10px; font-size: 12.5px; border-radius: var(--r-sm); }
.btn-lg { padding: 11px 22px; font-size: 15px; border-radius: var(--r); }
.btn:disabled { opacity: 0.45; cursor: default; transform: none; }
.btn:disabled:hover { background: var(--surface-2); border-color: var(--border); }
.btn-primary:disabled:hover { background: var(--accent); border-color: var(--accent); }
.btn-danger:disabled:hover { background: var(--danger); border-color: var(--danger); }

.btn-link {
  font: inherit; font-size: 13px; background: none; border: none; cursor: pointer;
  color: var(--text-dim); padding: 5px 8px; border-radius: var(--r-sm);
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.btn-link:hover { color: var(--text); background: var(--surface-2); }
.btn-link.danger:hover { color: var(--danger); background: var(--danger-soft); }

/* ── inputs ── */
.input, .select, textarea.composer-text {
  font: inherit; color: var(--text);
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 8px 11px; width: 100%;
  transition: border-color var(--fast) var(--ease), box-shadow var(--fast) var(--ease);
}
.input::placeholder, textarea::placeholder { color: var(--text-faint); }
.input:hover, .select:hover, textarea.composer-text:hover { border-color: var(--border-strong); }
.input:focus, .select:focus, textarea:focus {
  outline: none; border-color: var(--accent); box-shadow: var(--ring);
}
/* The native file button is a light grey chip that fights the dark theme. */
.input[type="file"] { padding: 6px 8px; cursor: pointer; }
.input[type="file"]::file-selector-button {
  font: inherit; font-weight: 500; font-size: 12.5px;
  color: var(--text); background: var(--surface-2);
  border: 1px solid var(--border); border-radius: var(--r-sm);
  padding: 5px 11px; margin-right: 10px; cursor: pointer;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
.input[type="file"]::file-selector-button:hover { background: var(--surface-3); border-color: var(--border-strong); }

.input-sm { width: 96px; }

/* Native selects reserve room for the system chevron and size themselves to
   their longest option, so a short value like "v1" ends up stranded beside a
   far-off arrow. Drawing the chevron ourselves puts it next to the value, and
   field-sizing shrinks the box to the chosen option where it is supported. */
.select {
  width: auto; cursor: pointer;
  appearance: none;
  field-sizing: content;
  min-width: 4.5em; max-width: 15em;
  padding: 7px 27px 7px 11px;
  background-repeat: no-repeat; background-position: right 9px center;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6' viewBox='0 0 9 6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' fill='none' stroke='%238b929e' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.select:hover { background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6' viewBox='0 0 9 6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' fill='none' stroke='%23edeff3' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); }
.select option { background: var(--surface); color: var(--text); }
.select-sm { padding: 4px 24px 4px 9px; font-size: 12px; background-position: right 8px center; }

/* Safari has no field-sizing yet, so those boxes stay as wide as their longest
   option. Centring the value keeps it from stranding against the left edge. */
@supports not (field-sizing: content) {
  .version-select, .status-select { text-align: center; text-align-last: center; }
}

.form-row { display: flex; align-items: center; gap: 12px; margin: 12px 0; }
.form-row label { flex: none; width: 90px; color: var(--text-dim); font-size: 13px; }
.check-label {
  display: inline-flex; align-items: center; gap: 7px;
  color: var(--text-dim); font-size: 12.5px; cursor: pointer;
  transition: color var(--fast) var(--ease);
}
.check-label:hover { color: var(--text); }
.check-label input { accent-color: var(--accent); cursor: pointer; }

/* ── auth gate ── */
.auth-gate { flex: 1; display: flex; align-items: center; justify-content: center; min-height: 100vh; padding: 24px; }
.auth-card {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 44px 46px; max-width: 430px; text-align: center;
  box-shadow: var(--shadow-lg);
}
.auth-card h1 { margin-bottom: 10px; }
.auth-card p { margin: 0 0 26px; line-height: 1.6; }

/* ── pages ── */
.page { padding: 32px; max-width: 1140px; width: 100%; margin: 0 auto; overflow-y: auto; }
.page-head { display: flex; align-items: center; gap: 12px; margin-bottom: 24px; }
.page-head h1 { margin: 0; }

/* ── drag & drop upload ── */
.drop-target { position: relative; min-height: 60vh; }
.drop-overlay {
  position: absolute; inset: 4px; z-index: 40; pointer-events: none;
  display: flex; align-items: center; justify-content: center;
  background: color-mix(in srgb, var(--accent-soft) 70%, var(--bg));
  backdrop-filter: blur(2px);
  border: 2px dashed var(--accent); border-radius: var(--r-lg);
}
.drop-overlay-box {
  padding: 14px 26px; font-size: 15px; font-weight: 600;
  color: var(--accent); background: var(--surface);
  border: 1px solid var(--accent); border-radius: var(--r);
  box-shadow: var(--shadow-md);
}

/* ── project grid ── */
.project-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 14px; }
.project-card {
  position: relative;
  font: inherit; text-align: left; color: var(--text); cursor: pointer;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); padding: 20px; min-height: 118px;
  display: flex; flex-direction: column; justify-content: flex-end; gap: 5px;
  transition: border-color var(--med) var(--ease), transform var(--med) var(--ease),
              box-shadow var(--med) var(--ease), background var(--med) var(--ease);
}
.project-card:hover {
  border-color: var(--border-strong); background: var(--surface-2);
  transform: translateY(-2px); box-shadow: var(--shadow-md);
}
.project-card h3 { margin: 0; font-size: 15px; }
.project-card p { margin: 0; font-size: 12.5px; color: var(--text-faint); }
/* Date on the left, the card's menu on the right — as on a video card. The
   button is pulled out to the card's edge so the dots line up with the padding
   rather than sitting a button's worth of air inside it. */
.card-foot { display: flex; align-items: center; gap: 8px; margin-right: -7px; }
.card-foot p { min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.project-card-new {
  align-items: center; justify-content: center; gap: 8px;
  color: var(--text-faint); border-style: dashed; background: transparent;
}
.project-card-new:hover { color: var(--accent); border-color: var(--accent); background: var(--accent-soft); }
.project-card-new .plus { font-size: 22px; line-height: 1; font-weight: 300; }

/* ── video cards ── */
.video-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(258px, 1fr)); gap: 18px; }
.video-card {
  position: relative; cursor: pointer; color: var(--text);
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r-lg); overflow: hidden;
  display: flex; flex-direction: column;
  transition: border-color var(--med) var(--ease), transform var(--med) var(--ease),
              box-shadow var(--med) var(--ease);
}
.video-card:hover {
  border-color: var(--border-strong);
  transform: translateY(-2px); box-shadow: var(--shadow-md);
}

/* Cover art. The three stops come from the card's inline style — a blob of the
   lighter colour off the top-right over a diagonal base, which is what gives
   these their depth instead of reading as a flat swatch. */
/* 16:9, uncropped. A squarer cover would be taller still, but it would cut a
   quarter off the sides of every frame — and a motion-design frame is very
   often a logo or a lockup sitting exactly there. The card gets its height
   from the block underneath instead. */
.video-poster {
  position: relative; aspect-ratio: 16 / 9; overflow: hidden;
  background:
    radial-gradient(115% 90% at 78% 16%, var(--g2), transparent 62%),
    linear-gradient(155deg, var(--g1), var(--g3));
}
/* A frame from the cut itself, over the gradient rather than instead of it —
   the gradient is the loading state and the fallback. Faded in only once a
   frame has been decoded, so a card never flashes an empty black box. */
.video-preview {
  position: absolute; inset: 0; z-index: 0;
  width: 100%; height: 100%; object-fit: cover;
  opacity: 0; pointer-events: none; background: #000;
  transition: opacity var(--med) var(--ease);
}
.video-preview.ready { opacity: 1; }

/* Length of the cut, over the frame's bottom corner. Only the file knows it,
   so the badge arrives with the frame rather than being promised beforehand. */
.video-duration {
  position: absolute; right: 8px; bottom: 8px; z-index: 2;
  padding: 2px 7px; border-radius: var(--r-sm);
  font-size: 11px; font-weight: 600; font-variant-numeric: tabular-nums;
  color: #fff; background: rgba(9, 11, 16, 0.78);
  opacity: 0; transition: opacity var(--fast) var(--ease);
}
.video-duration.ready { opacity: 1; }
/* The status colours are soft translucent fills built for a dark panel, and
   would all but vanish on a bright gradient — so on the poster the pill keeps
   its text colour but sits on a scrim of its own. */
/* On the review screen this pill is a control in a toolbar and earns its size.
   Here it sits on the picture, where the picture should be the loudest thing —
   so it shrinks to a marker, keeps the status colour that makes the grid
   scannable, and takes a scrim to survive a bright frame under it. */
.video-poster .status-pill {
  position: absolute; top: 9px; left: 9px; z-index: 2;
  font-size: 10.5px; padding: 3px 9px; letter-spacing: 0.015em;
  background-color: rgba(9, 11, 16, 0.66); border-color: rgba(255, 255, 255, 0.14);
  backdrop-filter: blur(6px);
  box-shadow: 0 1px 6px rgba(0, 0, 0, 0.25);
}
.video-poster .status-select { padding-right: 21px; background-position: right 7px center; }
/* The card's own words, under the frame rather than over it. No divider: the
   frame's edge already separates them, and a rule between the two only adds
   another line to a grid that has plenty. */
.video-card-body {
  display: flex; flex-direction: column; gap: 2px;
  padding: 11px 12px 12px;
}
.video-card-name {
  margin: 0; font-size: 14px; font-weight: 600; letter-spacing: -0.01em;
  color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* One line, cut short if it has to be — a card can be narrow, and a wrapping
   line here would push every card in the row taller. */
.video-card-meta {
  display: flex; align-items: center; gap: 8px; font-size: 12px;
  margin-right: -7px;   /* dots out to the card's padding edge */
}
.video-card-meta .dim {
  min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* Everything the card can do lives behind this — right-click, or here. */
.card-menu-btn {
  flex: none; display: inline-flex; align-items: center; justify-content: center;
  /* The icon is 15×4; the box around it is what makes it clickable. */
  min-width: 30px; min-height: 26px; padding: 0; color: var(--text-faint);
}
.card-menu-btn:hover, .card-menu-btn:focus-visible { color: var(--text); }
.icon-dots { display: block; width: 15px; height: 4px; }

/* The card being dragged, and the folder (or "← ProjectName") it could land
   on — the browser draws its own drag ghost, so these only need to mark the
   two ends of the gesture. */
.video-card.dragging { opacity: 0.4; }
.video-card.drop-target-active {
  border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft);
}
.page-head .btn-link.drop-target-active { background: var(--accent-soft); color: var(--accent); }

/* A folder's cover: up to four of its videos' own posters, tiled — a peek
   inside rather than a fifth colour invented just for the folder. Reuses
   .video-poster for the 16:9 box and rounded clipping; only the fill differs. */
.folder-mosaic {
  display: grid; grid-template-columns: 1fr 1fr; grid-template-rows: 1fr 1fr;
  gap: 2px; background: var(--surface-3);
}
.mosaic-tile {
  /* position:relative so a fetched frame's inset:0 clips to this one cell
     instead of the whole mosaic — .video-preview otherwise sizes itself
     against the nearest positioned ancestor, which would be the mosaic. */
  position: relative; overflow: hidden;
  background:
    radial-gradient(115% 90% at 78% 16%, var(--g2), transparent 62%),
    linear-gradient(155deg, var(--g1), var(--g3));
}
/* An empty folder has nothing to tile, so it falls back to a plain gradient
   with a glyph — the same treatment an ungrouped video gets before its own
   frame arrives. */
.folder-mosaic.empty {
  display: flex; align-items: center; justify-content: center;
  background:
    radial-gradient(115% 90% at 78% 16%, var(--g2), transparent 62%),
    linear-gradient(155deg, var(--g1), var(--g3));
}
.folder-mosaic.empty .icon-16 { width: 30px; height: 30px; color: rgba(255, 255, 255, 0.55); }

/* ── context menu ── */
.context-menu {
  position: fixed; z-index: 60; min-width: 168px; padding: 5px;
  display: flex; flex-direction: column; gap: 1px;
  background: var(--surface-2); border: 1px solid var(--border-strong);
  border-radius: var(--r); box-shadow: var(--shadow-md);
}
.context-menu-item {
  font: inherit; font-size: 13px; text-align: left; cursor: pointer;
  background: none; border: none; color: var(--text);
  padding: 7px 10px; border-radius: var(--r-sm);
  transition: background var(--fast) var(--ease), color var(--fast) var(--ease);
}
.context-menu-item:hover, .context-menu-item:focus-visible {
  outline: none; background: var(--surface-3);
}
.context-menu-item.danger { color: var(--danger); }
.context-menu-item.danger:hover, .context-menu-item.danger:focus-visible {
  background: var(--danger-soft); color: var(--danger);
}

.badge {
  background: var(--surface-2); border: 1px solid var(--border);
  border-radius: var(--r-pill); padding: 2px 9px; font-size: 11px;
  font-weight: 500; color: var(--text-dim); flex: none;
}

/* ── status pill ── */
.status-pill {
  font: inherit; font-size: 11.5px; font-weight: 600; cursor: default;
  letter-spacing: 0.01em;
  border: 1px solid transparent; border-radius: var(--r-pill);
  padding: 4px 12px; flex: none;
}
button.status-pill:not(:disabled) { cursor: pointer; }
/* Keeps the pill shape while behaving as a menu — the arrow is drawn in, since
   appearance:none drops the native one. Each status carries its own arrow:
   currentColor does not reach inside a data-URI SVG (it is a separate
   document, so it would resolve to black), and the arrow has to match the
   pill's text. */
.status-select {
  appearance: none; cursor: pointer;
  padding-right: 25px; field-sizing: content;
  background-repeat: no-repeat; background-position: right 9px center;
  transition: filter var(--fast) var(--ease);
}
.status-select:hover { filter: brightness(1.2); }
.status-select option { background: var(--surface); color: var(--text); font-weight: 500; }
/* background-color, not the background shorthand: these classes combine with
   .status-select, whose dropdown arrow is a background-image. */
.status-review { background-color: var(--accent-soft); color: var(--accent); border-color: rgba(107, 147, 255, 0.25); }
.status-changes { background-color: var(--warn-soft); color: var(--warn); border-color: rgba(245, 166, 35, 0.25); }
.status-approved { background-color: var(--ok-soft); color: var(--ok); border-color: rgba(62, 207, 142, 0.25); }
.status-select.status-review {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6' viewBox='0 0 9 6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' fill='none' stroke='%236b93ff' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.status-select.status-changes {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6' viewBox='0 0 9 6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' fill='none' stroke='%23f5a623' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}
.status-select.status-approved {
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='9' height='6' viewBox='0 0 9 6'%3E%3Cpath d='M1 1l3.5 3.5L8 1' fill='none' stroke='%233ecf8e' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
}

/* ── the list a select drops down ──
   Everything above styles the closed select; the list it opens was drawn by
   the OS — a black panel with the system accent on the highlighted row and
   the system font. It was the one corner of the app the theme could not
   reach. base-select hands that list back to CSS.

   All of it is progressive enhancement: where base-select is missing the
   select keeps its own chevron and its native list, exactly as before. Which
   is also why the rules sit here, after those the closed pill depends on. */
@supports (appearance: base-select) {
  .select, .status-select { appearance: base-select; }

  /* Both selects already draw a chevron as a background image — the status one
     in the pill's own colour — so the icon base-select adds would be a second. */
  .select::picker-icon, .status-select::picker-icon { display: none; }

  .select::picker(select), .status-select::picker(select) {
    appearance: base-select;
    margin-top: 5px; padding: 5px;
    background: var(--surface-2);
    border: 1px solid var(--border-strong); border-radius: var(--r);
    box-shadow: var(--shadow-md);
  }

  .select option, .status-select option {
    display: flex; align-items: center; gap: 10px;
    padding: 7px 10px; border-radius: var(--r-sm);
    font: inherit; font-size: 13px; font-weight: 500; letter-spacing: normal;
    color: var(--text); background: transparent; cursor: pointer;
    transition: background var(--fast) var(--ease);
  }
  .select option:hover, .status-select option:hover,
  .select option:focus, .status-select option:focus {
    background: var(--surface-3);
  }
  /* The tick marks the current choice, so it goes to the trailing edge and the
     row keeps its own colour — a status must not be recoloured to say "this
     one is selected" when its colour is the thing it is saying. */
  .select option::checkmark, .status-select option::checkmark {
    order: 1; margin-left: auto; font-size: 11px; color: var(--accent);
  }
}

/* ── review screen ── */
.review-layout { flex: 1; min-height: 0; display: flex; flex-direction: column; }
.review-top {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 20px; border-bottom: 1px solid var(--border); flex: none;
  background: var(--surface);
}
.video-title {
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  font-size: 15px; font-weight: 600; letter-spacing: -0.01em;
}
.version-select { font-size: 12.5px; }

.review-stage { flex: 1; min-height: 0; display: flex; }
.player-area {
  flex: 1; min-width: 0; display: flex; flex-direction: column;
  padding: 20px; gap: 10px;
}

/* ── player ── */
.player { flex: 1; min-height: 0; display: flex; flex-direction: column; gap: 0; }
.video-wrap {
  position: relative; flex: 1; min-height: 0;
  background: #000; overflow: hidden;
  border: 1px solid var(--border); border-bottom: none;
  border-radius: var(--r) var(--r) 0 0;
  box-shadow: var(--shadow-md);
}
.player-video { position: absolute; inset: 0; width: 100%; height: 100%; }
.annot-canvas { position: absolute; inset: 0; width: 100%; height: 100%; pointer-events: none; }
.annot-canvas.drawing { pointer-events: auto; cursor: crosshair; touch-action: none; }

.player-controls {
  display: flex; align-items: center; gap: 4px; flex: none;
  background: var(--surface); border: 1px solid var(--border); border-top: none;
  border-radius: 0 0 var(--r) var(--r); padding: 9px 12px;
}
.ctrl-btn {
  font: inherit; background: none; border: none; color: var(--text-dim);
  cursor: pointer; padding: 7px; border-radius: var(--r-sm); display: inline-flex;
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.ctrl-btn:hover { background: var(--surface-3); color: var(--text); }
.ctrl-btn .icon, .icon { width: 18px; height: 18px; display: block; }

.timecode { font-size: 12.5px; letter-spacing: 0.02em; color: var(--text-dim); }
.tc-group { margin: 0 10px; white-space: nowrap; }
.tc-group .timecode:first-child { color: var(--text); font-weight: 500; }

/* The scrubber and its marker rail share one column so ticks line up with the
   track. The rail is inset by half a thumb width, since the track itself is
   shortened by the thumb at each end. */
.scrub-wrap { flex: 1; min-width: 60px; display: flex; flex-direction: column; gap: 2px; }
.scrub-markers { position: relative; height: 9px; margin: 0 6px; }
.scrub-marker {
  position: absolute; bottom: 0; transform: translateX(-50%);
  width: 3px; height: 9px; padding: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--marker, var(--accent));
  border: 1px solid transparent; border-radius: 2px; cursor: pointer;
  transition: height var(--fast) var(--ease), width var(--fast) var(--ease),
    border-radius var(--fast) var(--ease), background var(--fast) var(--ease),
    border-color var(--fast) var(--ease);
}
/* Widened hit area without changing how the tick looks. */
.scrub-marker::after { content: ""; position: absolute; inset: -3px -5px; }

/* A tick says "someone commented here"; who it was lives in the sidebar. On
   hover — and on the selected note — the tick grows into that author's avatar
   so the two read as the same person without crossing the screen to check.
   Same fill/border recipe as .avatar, so they are recognisably one thing. */
.scrub-marker .marker-initials {
  font-size: 8px; font-weight: 700; letter-spacing: 0.02em; line-height: 1;
  color: var(--marker); opacity: 0; transform: scale(0.5);
  transition: opacity var(--fast) var(--ease), transform var(--fast) var(--ease);
}
.scrub-marker:hover, .scrub-marker:focus-visible, .scrub-marker.active {
  width: 19px; height: 19px; border-radius: 50%;
  background: color-mix(in srgb, var(--marker) 18%, var(--surface));
  border-color: color-mix(in srgb, var(--marker) 55%, transparent);
  z-index: 1;
}
.scrub-marker:hover .marker-initials,
.scrub-marker:focus-visible .marker-initials,
.scrub-marker.active .marker-initials { opacity: 1; transform: scale(1); }

.scrub {
  flex: none; -webkit-appearance: none; appearance: none; height: 4px;
  background: var(--surface-3); border-radius: var(--r-pill);
  cursor: pointer; width: 100%;
  transition: height var(--fast) var(--ease);
}
.scrub:hover { height: 6px; }
.scrub::-webkit-slider-thumb {
  -webkit-appearance: none; appearance: none;
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--surface);
  box-shadow: var(--shadow-sm);
}
.scrub::-moz-range-thumb {
  width: 13px; height: 13px; border-radius: 50%;
  background: var(--accent); border: 2px solid var(--surface);
}

/* ── annotation toolbar ── */
.annot-toolbar {
  display: flex; align-items: center; gap: 8px; flex: none;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--r); padding: 8px 12px;
  box-shadow: var(--shadow-sm);
}
.tool-btn {
  font: inherit; font-size: 12.5px; color: var(--text-dim); cursor: pointer;
  background: none; border: 1px solid transparent; border-radius: var(--r-sm);
  padding: 5px 11px;
  transition: color var(--fast) var(--ease), background var(--fast) var(--ease);
}
.tool-btn:hover { color: var(--text); background: var(--surface-2); }
.tool-btn.active { color: var(--accent); background: var(--accent-soft); border-color: rgba(107, 147, 255, 0.3); }
.toolbar-sep { width: 1px; height: 18px; background: var(--border); }
.color-btn {
  width: 18px; height: 18px; border-radius: 50%; cursor: pointer;
  border: 2px solid transparent; padding: 0; flex: none; /* never squash to an oval */
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.4) inset;
  transition: transform var(--fast) var(--ease);
}
.color-btn:hover { transform: scale(1.12); }
.color-btn.active { border-color: #fff; transform: scale(1.12); }

/* ── comments panel ── */
.comments-panel {
  width: 360px; flex: none; display: flex; flex-direction: column; min-height: 0;
  border-left: 1px solid var(--border); background: var(--surface);
}
.comments-head {
  display: flex; align-items: center; justify-content: space-between; gap: 10px;
  padding: 14px 16px 12px; flex: none; border-bottom: 1px solid var(--border);
}
.comments-head h3 { margin: 0; }
.comments-filters { display: flex; align-items: center; gap: 10px; }

.comment-list { flex: 1; overflow-y: auto; min-height: 0; padding: 8px; }
.comment {
  padding: 11px 12px; border-radius: var(--r); cursor: pointer;
  border: 1px solid transparent; margin-bottom: 7px; position: relative;
  transition: background var(--fast) var(--ease), border-color var(--fast) var(--ease);
}
/* Hairline in the gap between notes. The card's own border is reserved for the
   selected state, so the rule is a pseudo-element rather than a border-top. */
.comment + .comment::before {
  content: ""; position: absolute; left: 12px; right: 12px; top: -4px;
  height: 1px; background: var(--border);
  transition: opacity var(--fast) var(--ease);
}
/* A filled card draws its own edge; the rule beside it would only add noise. */
.comment:hover::before, .comment.selected::before,
.comment:hover + .comment::before, .comment.selected + .comment::before { opacity: 0; }
.comment:hover { background: var(--surface-2); }
.comment.selected {
  background: var(--surface-2); border-color: var(--accent);
}
.comment.resolved { opacity: 0.45; }
.comment-meta { display: flex; align-items: center; gap: 7px; margin-bottom: 5px; }
.comment-author { font-weight: 600; font-size: 12.5px; letter-spacing: -0.01em; }
/* Same colour as this author's ticks on the timeline — that link is the point,
   so the tint is deliberately strong enough to match at a glance. */
.avatar {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 9px; font-weight: 700; letter-spacing: 0.02em;
  color: var(--avatar); background: color-mix(in srgb, var(--avatar) 18%, transparent);
  border: 1px solid color-mix(in srgb, var(--avatar) 45%, transparent);
}
.comment-reply .avatar { width: 17px; height: 17px; font-size: 8px; }
.comment-date { font-size: 11px; margin-left: auto; color: var(--text-faint); }
/* Resolve toggle: unlabelled, since it is the only checkbox on a note, and
   green once ticked so a resolved thread is legible at a glance. */
.resolve-check {
  flex: none; margin: 0; width: 15px; height: 15px; cursor: pointer;
  accent-color: var(--ok);
  opacity: 0.5; transition: opacity var(--fast) var(--ease);
}
.resolve-check:checked, .resolve-check:hover, .resolve-check:focus-visible { opacity: 1; }
.comment:hover .resolve-check, .comment.selected .resolve-check { opacity: 1; }
.comment-body { display: flex; align-items: baseline; gap: 8px; }
.comment-text { white-space: pre-wrap; word-break: break-word; line-height: 1.5; }
.annot-flag { font-size: 11px; flex: none; }
/* Every note carries three controls; at full strength they shout louder than
   the comments. Dimmed until the note is hovered, focused or selected — still
   present and readable, so nothing becomes hard to find. */
.comment-actions {
  display: flex; align-items: center; gap: 4px; margin-top: 7px;
  margin-left: -8px; /* pull the ghost buttons back into optical alignment */
  color: var(--text-faint); opacity: 0.65;
  transition: opacity var(--fast) var(--ease);
}
.comment:hover .comment-actions,
.comment:focus-within .comment-actions,
.comment.selected .comment-actions { opacity: 1; }

/* ── comment threads ── */
/* The rule down the left is what reads as "these belong to the note above". */
.comment-replies {
  margin: 10px 0 0 4px; padding-left: 14px;
  border-left: 2px solid var(--border);
  display: flex; flex-direction: column; gap: 10px;
}
.comment-reply .comment-meta { margin-bottom: 2px; }
.comment-reply .comment-author { font-size: 12px; }
.comment-reply .comment-text { font-size: 13px; color: var(--text-dim); }
.comment-reply .comment-actions { margin-top: 2px; }
.reply-box {
  margin: 10px 0 0 4px; padding-left: 14px;
  border-left: 2px solid var(--accent);
}
.reply-box .composer-text { width: 100%; min-height: 44px; }
.reply-box .composer-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 7px; }

.tc-chip {
  font: inherit; font-size: 11px; font-weight: 600;
  color: var(--accent); background: var(--accent-soft);
  border: 1px solid transparent; border-radius: var(--r-sm);
  padding: 2px 7px; cursor: pointer; flex: none;
  transition: background var(--fast) var(--ease);
}
.tc-chip.pinned { border-color: var(--accent); }
.tc-link:hover { background: rgba(107, 147, 255, 0.26); }

/* ── composer ── */
.composer { flex: none; padding: 12px 16px 16px; border-top: 1px solid var(--border); background: var(--surface); }
.composer-top { display: flex; align-items: center; gap: 8px; margin-bottom: 8px; }
.annot-toggle { font-size: 13px; margin-left: auto; }
.annot-toggle.active { background: var(--accent-soft); color: var(--accent); }
textarea.composer-text { resize: vertical; min-height: 58px; line-height: 1.5; }
.composer-actions { display: flex; justify-content: flex-end; margin-top: 8px; }

/* ── share dialog ── */
.share-dialog { min-width: 470px; }
.share-result { display: flex; gap: 8px; margin: 14px 0; }
.share-url { font-size: 12.5px; color: var(--accent); }
.share-list { display: flex; flex-direction: column; gap: 6px; max-height: 230px; overflow-y: auto; }
.share-item {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg); border: 1px solid var(--border);
  border-radius: var(--r-sm); padding: 9px 12px; font-size: 13px;
  transition: border-color var(--fast) var(--ease);
}
.share-item:hover { border-color: var(--border-strong); }

/* ── modal ── */
.modal-backdrop {
  position: fixed; inset: 0; z-index: 100;
  background: rgba(6, 8, 12, 0.66);
  backdrop-filter: blur(4px);
  display: flex; align-items: center; justify-content: center; padding: 20px;
  animation: fade-in var(--med) var(--ease);
}
.modal {
  background: var(--surface); border: 1px solid var(--border-strong);
  border-radius: var(--r-lg); padding: 26px 28px;
  min-width: 400px; max-width: 560px; max-height: 85vh; overflow-y: auto;
  box-shadow: var(--shadow-lg);
  animation: modal-in var(--med) var(--ease);
}
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }
@keyframes fade-in { from { opacity: 0; } }
@keyframes modal-in { from { opacity: 0; transform: translateY(8px) scale(0.98); } }

/* ── toast ── */
.toast {
  position: fixed; bottom: 24px; left: 50%; transform: translate(-50%, 16px);
  background: var(--surface-3); color: var(--text);
  border: 1px solid var(--border-strong); border-radius: var(--r);
  padding: 11px 18px; font-size: 13px; z-index: 200;
  opacity: 0; pointer-events: none;
  transition: opacity var(--med) var(--ease), transform var(--med) var(--ease);
  max-width: 80vw; box-shadow: var(--shadow-lg);
}
.toast.show { opacity: 1; transform: translate(-50%, 0); }
.toast[data-kind="error"] { border-color: var(--danger); color: #ffd8db; }

/* ── spinner ── */
.spinner-wrap {
  flex: 1; display: flex; flex-direction: column; align-items: center;
  justify-content: center; gap: 14px; padding: 60px; color: var(--text-faint);
}
.spinner {
  width: 26px; height: 26px; border-radius: 50%;
  border: 2.5px solid var(--surface-3); border-top-color: var(--accent);
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }

/* ── upload progress ── */
/* accent-color is ignored on <progress> in WebKit, which falls back to its
   green bar — the pieces below are what actually colour it. */
.upload-progress {
  width: 100%; height: 6px; accent-color: var(--accent);
  border: none; border-radius: var(--r-pill); overflow: hidden;
  appearance: none; background: var(--surface-3);
}
.upload-progress::-webkit-progress-bar { background: var(--surface-3); border-radius: var(--r-pill); }
.upload-progress::-webkit-progress-value {
  background: var(--accent); border-radius: var(--r-pill);
  transition: width var(--med) var(--ease);
}
.upload-progress::-moz-progress-bar { background: var(--accent); border-radius: var(--r-pill); }
.upload-progress-text { margin: 8px 0 0; color: var(--text-dim); }
/* Sits under the frame-rate row, indented to line up with its input. */
.fps-hint { margin: -6px 0 12px 102px; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .project-card:hover { transform: none; }
}

/* ── responsive: sidebar drops below the player ── */
@media (max-width: 860px) {
  .page { padding: 20px 16px; }
  .review-stage { flex-direction: column; overflow-y: auto; }
  .player-area { flex: none; padding: 12px; }
  .video-wrap { aspect-ratio: 16 / 9; flex: none; }
  .player { flex: none; }
  .comments-panel { width: 100%; border-left: none; border-top: 1px solid var(--border); max-height: 50vh; }
  .share-dialog { min-width: 0; }
  .modal { min-width: 0; width: 100%; }
  .fps-hint { margin-left: 0; }

  /* Rather than crushing the title to an ellipsis, let the strip wrap: the
     existing spacer becomes the line break, dropping controls to row two. */
  .review-top { flex-wrap: wrap; row-gap: 10px; padding: 10px 14px; }
  .review-top .spacer { flex-basis: 100%; height: 0; }
  .video-title { min-width: 0; }

  .annot-toolbar { flex-wrap: wrap; row-gap: 8px; }

  /* Give the scrubber its own full-width row instead of letting it overflow.
     Targets the wrapper, which is what the controls row actually contains. */
  .player-controls { flex-wrap: wrap; row-gap: 6px; }
  .scrub-wrap { order: 10; flex-basis: 100%; }
  .tc-group { margin: 0 6px; }
}
