/* =============================================================================
   Tokens — a única camada onde valor cru pode aparecer.
   Tela nova não escreve CSS: usa as macros de templates/_macros.html, que usam
   as classes daqui. Precisou de um valor novo? Vira token, não vira `style="..."`.
   ========================================================================== */
:root {
  /* cor */
  --bg: #0f1115;
  --panel: #171a21;
  --border: #262b36;
  --text: #e6e8ec;
  --muted: #8b93a3;
  --accent: #5b8cff;
  --ok: #34c07a;
  --warn: #e0a940;
  --err: #e05a5a;

  /* cor de fundo suave — mesmo tom do estado, com alpha */
  --accent-soft: #5b8cff33;
  --ok-soft: #34c07a33;
  --warn-soft: #e0a94033;
  --err-soft: #e05a5a33;
  --muted-soft: #33384433;
  --accent-faint: #5b8cff22;
  --disabled: #4a5060;
  --video-bg: #000;

  /* espaço */
  --sp-1: 2px;
  --sp-2: 4px;
  --sp-3: 6px;
  --sp-4: 8px;
  --sp-5: 10px;
  --sp-6: 12px;
  --sp-7: 14px;
  --sp-8: 16px;
  --sp-9: 20px;
  --sp-10: 28px;

  /* raio */
  --r-xs: 3px;
  --r-sm: 4px;
  --r-md: 6px;
  --r-lg: 10px;
  --r-round: 50%;

  /* tipografia */
  --fs-xs: 11px;
  --fs-sm: 12px;
  --fs-md: 13px;
  --fs-lg: 14px;
  --fs-base: 15px;
  --fs-h2: 16px;
  --fs-h1: 20px;
  --fs-display: 26px;
  --font: -apple-system, system-ui, "Segoe UI", Roboto, sans-serif;
  /* saída de programa (mensagem de erro, caminho, traceback) — largura fixa para não mentir sobre
     espaço e para caminho longo continuar legível */
  --font-mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* medidas de layout */
  --sidebar-w: 220px;
  --content-max: 1200px;
  --dot: 22px;
  --dot-sm: 9px;   /* a mesma bolinha, no tamanho que cabe numa linha de cartão */
  --icon-tile: 36px;
  --player-w: 300px;
  /* Card da Biblioteca: vídeo 9:16 em cima, largura toda do card, texto embaixo. A largura mínima
     do card é o que decide quantas colunas cabem — 210px dá ~5 colunas no desktop e 2 no celular
     (o media query estreita para 150px lá). Miniatura pequena não serve: reconhecer o clip de olho
     é a função da tela. */
  --clip-card-w: 210px;
}

/* =============================================================================
   Base e layout
   ========================================================================== */
* { box-sizing: border-box; }
body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: var(--font);
  font-size: var(--fs-base);
}
.layout { display: flex; min-height: 100vh; }
.sidebar {
  width: var(--sidebar-w);
  flex-shrink: 0;
  background: var(--panel);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  padding: var(--sp-8) var(--sp-6);
  gap: var(--sp-1);
}
.brand { color: var(--text); font-weight: 700; text-decoration: none; padding: 0 var(--sp-5) var(--sp-8); }
.nav-item {
  color: var(--muted);
  text-decoration: none;
  font-size: var(--fs-lg);
  padding: 9px var(--sp-5);
  border-radius: var(--r-md);
}
a.nav-item:hover { background: var(--border); color: var(--text); }
.nav-item.active { background: var(--accent-faint); color: var(--accent); font-weight: 600; }
.nav-item.disabled { color: var(--disabled); cursor: default; }
.nav-logout { margin-top: auto; }
.content { flex: 1; padding: var(--sp-9) var(--sp-10); max-width: var(--content-max); }
.content-plain { padding: var(--sp-8); max-width: 1100px; margin: 0 auto; }

/* =============================================================================
   Tipografia
   ========================================================================== */
h1 { font-size: var(--fs-h1); }
.subtitle { color: var(--muted); font-size: var(--fs-md); margin-top: calc(var(--sp-4) * -1); }
h2 { font-size: var(--fs-h2); color: var(--muted); margin-top: var(--sp-10); }
.note { color: var(--muted); font-size: var(--fs-sm); margin-top: var(--sp-9); }
.note code { background: var(--panel); padding: 1px 5px; border-radius: var(--r-sm); }
.empty { color: var(--muted); font-style: italic; }
/* frase que veio de fora do painel (hook do clip, trecho da fala) — macro `quote` */
.quote {
  border-left: 2px solid var(--border); padding-left: var(--sp-6);
  color: var(--text); font-size: var(--fs-md); line-height: 1.5; overflow-wrap: anywhere;
}
.error { color: var(--err); }
.meta { background: var(--panel); padding: var(--sp-6); border-radius: var(--r-md); overflow-x: auto; font-size: var(--fs-sm); }

/* =============================================================================
   Superfícies — card e panel
   ========================================================================== */
.cards-row, .panels-row { display: flex; gap: var(--sp-7); margin-top: var(--sp-8); flex-wrap: wrap; }
.card, .panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  padding: var(--sp-8);
  flex: 1;
  min-width: 180px;
}
.card-disabled { opacity: 0.55; }
.card-label { color: var(--muted); font-size: var(--fs-md); }
.card-value { font-size: var(--fs-display); font-weight: 700; margin-top: var(--sp-2); }
.card-sub { color: var(--muted); font-size: var(--fs-sm); margin-top: var(--sp-1); }
.panel { min-width: 260px; }
.panel-wide { flex-basis: 100%; }
/* título colado no topo do painel — antes era `style="margin-top:0"` repetido nas telas */
.panel > h2:first-child { margin-top: 0; }
.panel > .error-note { color: var(--err); margin-top: var(--sp-6); }
.panel.no-pad { padding: 0; overflow: hidden; }
.panel.no-pad table { margin-top: 0; }
.panel.no-pad th, .panel.no-pad td { padding: var(--sp-5) var(--sp-7); }

/* card com ícone à esquerda (concept 01) */
.card-icon-row { display: flex; gap: var(--sp-6); align-items: flex-start; }
.card-icon {
  width: var(--icon-tile);
  height: var(--icon-tile);
  flex-shrink: 0;
  border-radius: var(--r-lg);
  background: var(--accent-faint);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-h2);
}
.card-delta { font-size: var(--fs-sm); font-weight: 600; margin-top: var(--sp-1); }
.card-delta.up { color: var(--ok); }
.card-delta.down { color: var(--err); }

/* cabeçalho e rodapé de painel (concept 01, 06) */
.panel-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-6);
  margin-bottom: var(--sp-6);
}
.panel-head h2 { margin: 0; color: var(--text); font-size: var(--fs-h2); }
.panel.no-pad .panel-head { padding: var(--sp-8) var(--sp-8) 0; margin-bottom: var(--sp-4); }
.panel-foot { text-align: center; padding: var(--sp-5); border-top: 1px solid var(--border); }
.panel-foot a { color: var(--accent); text-decoration: none; font-size: var(--fs-md); }
.panel-foot a:hover { text-decoration: underline; }

/* estado vazio de painel inteiro */
.empty-state {
  color: var(--muted);
  font-style: italic;
  text-align: center;
  padding: var(--sp-10) var(--sp-8);
  font-size: var(--fs-md);
}

/* bloco recolhido (`<details>` nativo) — o que se consulta, não o que se lê sempre.
   O triângulo é desenhado com borda em vez de glifo: seta em fonte vira tofu no Chromium headless
   da captura, e marcador que não se lê não diz se o bloco está aberto. */
.expander {
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  background: var(--panel);
  margin-top: var(--sp-6);
}
.expander-head {
  display: flex;
  align-items: center;
  gap: var(--sp-5);
  padding: var(--sp-5) var(--sp-6);
  cursor: pointer;
  font-size: var(--fs-lg);
  list-style: none; /* Firefox */
}
.expander-head::-webkit-details-marker,
.filter-more-head::-webkit-details-marker { display: none; }
.expander-head::before,
.filter-more-head::before {
  content: "";
  flex-shrink: 0;
  border-left: 5px solid var(--muted);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 0.15s;
}
.expander[open] > .expander-head::before,
.filter-more[open] > .filter-more-head::before { transform: rotate(90deg); }
.expander-head:hover { color: var(--accent); }
.expander-title { font-weight: 600; }
/* a contagem encosta à direita: é resposta de relance, não continuação do título */
.expander-sub { color: var(--muted); font-size: var(--fs-sm); margin-left: auto; text-align: right; }
.expander-body { padding: 0 var(--sp-6) var(--sp-6); }
/* filho que já tem fundo próprio (o `pre.meta`, a tabela) não precisa de moldura dentro da moldura */
.expander-body > .meta { background: var(--bg); }
.expander-body > h2:first-child { margin-top: 0; }

/* =============================================================================
   Ações — botões
   ========================================================================== */
.page-header { display: flex; justify-content: space-between; align-items: center; }
.btn {
  display: inline-block;
  background: var(--accent);
  color: white;
  padding: var(--sp-4) var(--sp-7);
  border-radius: var(--r-md);
  font-size: var(--fs-md);
  font-weight: 600;
  text-decoration: none;
  cursor: pointer;
  border: none;
}
.btn-disabled { background: var(--border); color: var(--muted); cursor: default; }
/* `<button class="btn">` (macro `action_button`): botão não herda a fonte do documento sozinho */
button.btn { font-family: inherit; }
.btn-danger { background: var(--err); }
.action-form { display: inline-block; margin: 0; }
.btn-ghost { background: transparent; border: 1px solid var(--border); color: var(--muted); }
.btn-ghost:hover { color: var(--text); border-color: var(--muted); }
.btn-sm { padding: var(--sp-3) var(--sp-5); font-size: var(--fs-sm); }
.icon-btn {
  background: transparent;
  border: 1px solid var(--border);
  color: var(--muted);
  border-radius: var(--r-md);
  padding: var(--sp-2) var(--sp-3);
  font-size: var(--fs-md);
  cursor: pointer;
  line-height: 1;
}
.icon-btn:hover { color: var(--text); border-color: var(--muted); }

/* =============================================================================
   Navegação de conteúdo — abas e barra de filtros
   ========================================================================== */
.tabs { display: flex; gap: var(--sp-3); margin-top: var(--sp-8); border-bottom: 1px solid var(--border); }
.tab {
  color: var(--muted);
  text-decoration: none;
  font-size: var(--fs-md);
  padding: var(--sp-4) var(--sp-6);
  border-bottom: 2px solid transparent;
}
.tab:hover { color: var(--text); }
.tab.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
.toolbar { display: flex; gap: var(--sp-4); align-items: center; flex-wrap: wrap; margin-top: var(--sp-8); }
.toolbar .field { margin: 0; }

/* barra de filtros (A7) — campos em linha, ação no fim; quebra em telas estreitas */
.filter-bar {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-5);
  margin-top: var(--sp-8);
  padding: var(--sp-7);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.filter-bar .field { font-size: var(--fs-sm); }
.filter-bar .field input, .filter-bar .field select {
  font-size: var(--fs-md);
  padding: var(--sp-3) var(--sp-4);
}
/* `field(grow=True)` — o campo de busca é o único que cresce: é onde se digita frase, não número */
.filter-bar .field-grow { flex: 1; min-width: 180px; }
.filter-actions { display: flex; gap: var(--sp-4); margin-left: auto; }

/* "Mais filtros": todo campo que não é a busca mora numa gaveta recolhida.
   Fechada, é um chip que divide a linha com as ações; **aberta, toma a linha toda** (`[open]` muda a
   base do flex) e os campos quebram dentro dela como quebravam na barra. */
.filter-more { flex: 0 0 auto; }
.filter-more[open] { flex: 1 1 100%; }
.filter-more-head {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-4);
  padding: var(--sp-3) var(--sp-5);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  color: var(--muted);
  font-size: var(--fs-sm);
  cursor: pointer;
  list-style: none; /* Firefox */
  white-space: nowrap;
}
.filter-more-head:hover { color: var(--text); border-color: var(--muted); }
/* a contagem é o que impede a gaveta de esconder filtro ativo em silêncio — cor de acento porque é
   aviso de estado, não rótulo */
.filter-more-n {
  background: var(--accent-soft);
  color: var(--accent);
  font-weight: 600;
  padding: 0 var(--sp-3);
  border-radius: var(--r-lg);
}
.filter-more-fields {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--sp-5);
  margin-top: var(--sp-5);
  padding-top: var(--sp-5);
  border-top: 1px solid var(--border);
}

/* anterior/próximo (A7) */
.pager { display: flex; align-items: center; justify-content: center; gap: var(--sp-7); margin-top: var(--sp-9); }
.pager-label { color: var(--muted); font-size: var(--fs-sm); font-variant-numeric: tabular-nums; }

/* =============================================================================
   Cabeçalho do job
   ========================================================================== */
.job-header { margin-top: var(--sp-8); border-left: 3px solid var(--accent); }
.job-header-top { display: flex; justify-content: space-between; align-items: flex-start; }
.job-header-top h1 { margin-bottom: var(--sp-1); }
/* estado e ação na mesma coluna à direita: no celular a coluna deita e o botão segue o badge */
.job-header-actions { display: flex; flex-direction: column; align-items: flex-end; gap: var(--sp-5); }
.job-header-meta {
  display: flex;
  gap: var(--sp-10);
  margin-top: var(--sp-8);
  padding-top: var(--sp-7);
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}
.job-header-meta .card-label { display: block; margin-bottom: var(--sp-1); }

/* =============================================================================
   Timeline do pipeline
   ========================================================================== */
.pipeline { display: flex; align-items: flex-start; margin-top: var(--sp-6); overflow-x: auto; }
.pipeline-step { display: flex; flex-direction: column; align-items: center; min-width: 100px; text-align: center; }
.pipeline-dot { width: var(--dot); height: var(--dot); border-radius: var(--r-round); background: var(--border); border: 2px solid var(--border); }
.pipeline-label { font-size: var(--fs-sm); color: var(--muted); margin-top: var(--sp-3); }
.pipeline-detail { font-size: var(--fs-xs); color: var(--muted); }
.pipeline-line { flex: 1; height: 2px; background: var(--border); margin-top: 11px; min-width: var(--sp-9); }
.pipeline-step.state-done .pipeline-dot { background: var(--ok); border-color: var(--ok); }
.pipeline-step.state-done .pipeline-label { color: var(--ok); }
.pipeline-line.state-done { background: var(--ok); }
.pipeline-step.state-current .pipeline-dot { background: var(--accent); border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.pipeline-step.state-current .pipeline-label { color: var(--accent); font-weight: 600; }
.pipeline-step.state-failed .pipeline-dot { background: var(--err); border-color: var(--err); }
.pipeline-step.state-failed .pipeline-label { color: var(--err); }
.pipeline-line.state-failed { background: var(--err); }

/* as 9 etapas em vertical, com tempo e erro (A5) — mesmas cores de estado da tira */
.stages { list-style: none; margin: var(--sp-6) 0 0; padding: 0; }
.stage-row {
  display: flex; align-items: baseline; gap: var(--sp-6);
  padding: var(--sp-4) 0; border-bottom: 1px solid var(--border);
}
.stage-row:last-child { border-bottom: none; }
.stage-dot {
  width: var(--sp-5); height: var(--sp-5); flex-shrink: 0;
  border-radius: var(--r-round); background: var(--border);
}
.stage-main { flex: 1; min-width: 0; }
.stage-name { font-size: var(--fs-md); color: var(--muted); }
.stage-scope { font-size: var(--fs-xs); color: var(--disabled); }
.stage-detail { font-size: var(--fs-xs); color: var(--muted); margin-top: var(--sp-1); }
.stage-error {
  font-size: var(--fs-xs); color: var(--err); margin-top: var(--sp-2);
  background: var(--err-soft); border-radius: var(--r-sm); padding: var(--sp-2) var(--sp-4);
}
.stage-error a { color: var(--err); font-weight: 600; }
.stage-time {
  font-size: var(--fs-sm); color: var(--muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.stage-row.state-done .stage-dot { background: var(--ok); }
.stage-row.state-done .stage-name { color: var(--text); }
.stage-row.state-current .stage-dot { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.stage-row.state-current .stage-name { color: var(--accent); font-weight: 600; }
.stage-row.state-failed .stage-dot { background: var(--err); }
.stage-row.state-failed .stage-name { color: var(--err); font-weight: 600; }

/* =============================================================================
   Aviso de página inteira (A6)
   ========================================================================== */
.banner {
  margin-top: var(--sp-8);
  padding: var(--sp-6) var(--sp-8);
  border-radius: var(--r-md);
  border-left: 3px solid;
  font-size: var(--fs-md);
}
.banner-warn { background: var(--warn-soft); border-color: var(--warn); color: var(--warn); }
.banner-err { background: var(--err-soft); border-color: var(--err); color: var(--err); }
.banner-ok { background: var(--ok-soft); border-color: var(--ok); color: var(--ok); }
/* as linhas que o core respondeu ao comando (B2): monoespaçadas porque são a saída dele, palavra
   por palavra — a mesma que sairia no terminal, e é isso que as torna conferíveis */
.banner-lines {
  margin: var(--sp-4) 0 0; padding-left: var(--sp-7);
  font-family: var(--font-mono); font-size: var(--fs-sm); line-height: 1.6;
}

/* =============================================================================
   Fila de trabalho (A6) — uma linha por unidade em voo
   ========================================================================== */
.queue { list-style: none; margin: var(--sp-6) 0 0; padding: 0; }
.queue-row {
  display: flex; align-items: baseline; gap: var(--sp-6);
  padding: var(--sp-5) 0; border-bottom: 1px solid var(--border);
}
.queue-row:last-child { border-bottom: none; }
.queue-dot {
  width: var(--sp-5); height: var(--sp-5); flex-shrink: 0;
  border-radius: var(--r-round); background: var(--disabled);
}
.queue-main { flex: 1; min-width: 0; }
.queue-title { font-size: var(--fs-md); }
.queue-title a { color: var(--text); text-decoration: none; font-weight: 600; }
.queue-title a:hover { color: var(--accent); }
.queue-stage { color: var(--muted); }
/* a linha de contexto trunca em vez de quebrar: source_ref é URL longa e empurraria o alerta
   para fora da primeira tela */
.queue-sub {
  font-size: var(--fs-xs); color: var(--muted); margin-top: var(--sp-1);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.queue-worker { font-family: var(--font-mono); }
.queue-alert {
  font-size: var(--fs-xs); color: var(--warn); margin-top: var(--sp-2);
  background: var(--warn-soft); border-radius: var(--r-sm); padding: var(--sp-2) var(--sp-4);
}
.queue-time {
  font-size: var(--fs-sm); color: var(--muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.queue-row.state-running .queue-dot { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.queue-row.state-dispatched .queue-dot { background: var(--warn); }
.queue-row.state-waiting .queue-dot { background: var(--muted); }
.queue-row.state-blocked .queue-dot { background: var(--border); }
.queue-row.has-alert .queue-dot { background: var(--err); }
.queue-row.has-alert .queue-alert { color: var(--err); background: var(--err-soft); }

/* =============================================================================
   Central de Erros (A6) — um bloco por mensagem, não por ocorrência
   ========================================================================== */
.errors { list-style: none; margin: var(--sp-6) 0 0; padding: 0; }
.error-item { padding: var(--sp-6) 0; border-bottom: 1px solid var(--border); }
.error-item:last-child { border-bottom: none; }
.error-head { display: flex; align-items: flex-start; gap: var(--sp-6); justify-content: space-between; }
.error-msg {
  font-family: var(--font-mono); font-size: var(--fs-md); color: var(--text);
  word-break: break-word; min-width: 0;
}
.error-item.state-active .error-msg { color: var(--err); }
.error-meta { font-size: var(--fs-xs); color: var(--muted); margin-top: var(--sp-3); }
.error-hint {
  font-size: var(--fs-xs); color: var(--muted); margin-top: var(--sp-3);
  background: var(--muted-soft); border-radius: var(--r-sm); padding: var(--sp-2) var(--sp-4);
}
.error-units { margin-top: var(--sp-3); display: flex; flex-wrap: wrap; gap: var(--sp-4); }
.error-units a {
  font-size: var(--fs-xs); color: var(--muted); text-decoration: none;
  border: 1px solid var(--border); border-radius: var(--r-sm); padding: var(--sp-1) var(--sp-4);
}
.error-units a:hover { color: var(--text); border-color: var(--muted); }

/* =============================================================================
   Biblioteca de Clips (A7) — grade de cards, um por clip
   ========================================================================== */
.clip-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(var(--clip-card-w), 1fr));
  gap: var(--sp-7);
  margin-top: var(--sp-8);
}
.clip-card {
  display: flex;
  flex-direction: column;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
  overflow: hidden;
  min-width: 0;
}
/* o vídeo encosta nas bordas do card: moldura em volta de vídeo vertical só rouba pixel de vídeo */
.clip-thumb { position: relative; width: 100%; }
.clip-video, .clip-novideo {
  width: 100%;
  aspect-ratio: 9 / 16;
  display: block;
  background: var(--video-bg);
}
.clip-novideo {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  background: var(--bg);
  color: var(--muted);
  font-size: var(--fs-xs);
  text-align: center;
  padding: var(--sp-4);
}
.clip-novideo span { color: var(--disabled); }
/* a nota é o que o olho procura ao varrer a grade: fica SOBRE o vídeo, no alto — embaixo encostaria
   na barra de controles do player. Número tabular para as casas alinharem entre cards. */
.clip-score {
  position: absolute;
  top: var(--sp-4);
  right: var(--sp-4);
  background: var(--bg);
  color: var(--accent);
  font-weight: 700;
  font-size: var(--fs-md);
  font-variant-numeric: tabular-nums;
  padding: var(--sp-1) var(--sp-4);
  border-radius: var(--r-lg);
  border: 1px solid var(--border);
  white-space: nowrap;
}
.clip-body { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: var(--sp-3); padding: var(--sp-6); }
.clip-title {
  color: var(--text); text-decoration: none; font-weight: 600; font-size: var(--fs-md);
  line-height: 1.35; overflow-wrap: anywhere;
  /* três linhas: título de clip é frase inteira, e cortar em duas some com o final que identifica */
  display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical; overflow: hidden;
}
.clip-title:hover { color: var(--accent); }
/* o hook só aparece quando diz algo diferente do título (ver `_hook()` em queries/clips.py) */
.clip-hook {
  color: var(--muted); font-size: var(--fs-sm);
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.clip-chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--sp-3); }
.chip {
  color: var(--muted); font-size: var(--fs-xs);
  background: var(--muted-soft); border-radius: var(--r-lg); padding: var(--sp-1) var(--sp-4);
  white-space: nowrap;
}
.clip-meta { color: var(--muted); font-size: var(--fs-xs); }
.clip-meta a { color: var(--muted); }
.clip-meta a:hover { color: var(--accent); }
.clip-meta:last-child { margin-top: auto; }
.clip-error {
  font-size: var(--fs-xs); color: var(--err);
  background: var(--err-soft); border-radius: var(--r-sm); padding: var(--sp-2) var(--sp-4);
  overflow-wrap: anywhere;
}
/* ação opcional do card (B5, "Julgar"): encosta no rodapé do card para todos os botões da grade
   ficarem na mesma linha, com cards de altura diferente */
.clip-action { margin-top: auto; text-align: center; }

/* =============================================================================
   Aprendizado (B5) — um bloco por veredito

   O motivo é o conteúdo do bloco, não uma coluna: é o que se relê meses depois. A cor da borda
   esquerda diz aprovado/reprovado sem obrigar a ler o badge — a lista se varre com o olho.
   ========================================================================== */
.verdicts { list-style: none; display: flex; flex-direction: column; gap: var(--sp-6); margin-top: var(--sp-8); }
.verdict-row {
  background: var(--panel);
  border: 1px solid var(--border);
  border-left: 3px solid var(--muted);
  border-radius: var(--r-lg);
  padding: var(--sp-6) var(--sp-7);
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
}
.verdict-row.state-approved { border-left-color: var(--ok); }
.verdict-row.state-rejected { border-left-color: var(--err); }
.verdict-head { display: flex; align-items: center; gap: var(--sp-5); flex-wrap: wrap; }
.verdict-title { flex: 1; min-width: 0; font-weight: 600; font-size: var(--fs-md); overflow-wrap: anywhere; }
.verdict-when { color: var(--muted); font-size: var(--fs-sm); white-space: nowrap; }
.verdict-reason {
  color: var(--text); font-size: var(--fs-md); line-height: 1.45;
  border-left: 2px solid var(--border); padding-left: var(--sp-6);
  overflow-wrap: anywhere;
}
.verdict-marks { display: flex; flex-wrap: wrap; gap: var(--sp-3); }
.verdict-meta { color: var(--muted); font-size: var(--fs-xs); }
.verdict-meta a { color: var(--muted); }
.verdict-meta a:hover { color: var(--accent); }
.verdict-fp { font-family: var(--font-mono); color: var(--muted); }
/* rótulo que não é comparável entre rodadas: cor de aviso, porque é isso que ele é */
.verdict-nofp { color: var(--warn); }

/* =============================================================================
   Gerenciamento de Jobs (A8) — um cartão compacto por job

   Três faixas de altura fixa: cabeçalho (id, estado, etapa, %, menu), barra, fatos. A quarta só
   existe quando há falha. É a mesma forma no celular e no desktop — nada reflui, só estica —
   porque a tabela que isto substituiu virava nove linhas sem rótulo abaixo de 640px.
   ========================================================================== */
.job-list { list-style: none; margin: var(--sp-8) 0 0; padding: 0; display: flex; flex-direction: column; gap: var(--sp-5); }
.job-row {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-4);
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--sp-5) var(--sp-6) var(--sp-6) var(--sp-8);
}
.job-row:hover { border-color: var(--muted); }
/* a tira de estado é `inset shadow` e não `border-left`: assim o hover pode mexer na borda inteira
   sem apagar a cor do estado */
.job-row.state-pending { box-shadow: inset 3px 0 0 var(--muted); }
.job-row.state-running { box-shadow: inset 3px 0 0 var(--accent); }
.job-row.state-completed { box-shadow: inset 3px 0 0 var(--ok); }
.job-row.state-failed { box-shadow: inset 3px 0 0 var(--err); }
.job-row.state-cancelled { box-shadow: inset 3px 0 0 var(--disabled); }

.job-head { display: flex; align-items: center; gap: var(--sp-5); min-width: 0; }
.job-head .badge { flex-shrink: 0; }
.job-id {
  color: var(--text); text-decoration: none; font-weight: 700; font-size: var(--fs-lg);
  font-variant-numeric: tabular-nums; flex-shrink: 0;
}
/* o link do id cobre o cartão inteiro (o menu de ações sobe por cima com z-index) */
.job-id::after { content: ""; position: absolute; inset: 0; border-radius: inherit; }
.job-row:hover .job-id { color: var(--accent); }
/* o título do vídeo é a identidade do job e também o espaçador: ele encolhe e trunca antes de
   empurrar o % e o menu para fora */
.job-title {
  flex: 1; min-width: 0; font-size: var(--fs-md);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* faixa 2: onde o job está — as bolinhas e o nome da etapa atual */
.job-track { display: flex; align-items: center; gap: var(--sp-5); min-width: 0; }
.job-stage {
  min-width: 0; font-size: var(--fs-sm);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.job-stage-note { color: var(--muted); font-size: var(--fs-xs); }
/* `margin-left: auto` porque job concluído não desenha etapa nenhuma (o badge já disse tudo) e sem
   ela o % e o menu subiriam para o meio da faixa */
.job-pct {
  color: var(--muted); font-size: var(--fs-sm); font-variant-numeric: tabular-nums;
  flex-shrink: 0; margin-left: auto;
}

.job-facts { font-size: var(--fs-xs); color: var(--muted); }

/* gaveta de clips: nasce fechada, e o cabeçalho já conta quantos são (mesma regra do `expander`).
   `z-index` porque o cartão inteiro é um link — sem isso o toque na gaveta abriria o job. */
.job-drawer { position: relative; z-index: 1; }
.job-drawer-head {
  display: inline-flex; align-items: center; gap: var(--sp-4);
  font-size: var(--fs-xs); color: var(--muted); cursor: pointer; list-style: none;
}
.job-drawer-head::-webkit-details-marker { display: none; }
/* triângulo desenhado com borda, não glifo (mesma razão do `expander`: seta em fonte vira tofu) */
.job-drawer-head::before {
  content: "";
  border-left: 5px solid var(--muted);
  border-top: 4px solid transparent;
  border-bottom: 4px solid transparent;
  transition: transform 0.15s;
}
.job-drawer[open] > .job-drawer-head::before { transform: rotate(90deg); }
.job-drawer-head:hover { color: var(--text); }
.job-clips { list-style: none; margin: var(--sp-3) 0 0; padding: 0; }
.clip-line {
  display: flex; align-items: center; gap: var(--sp-4);
  padding: var(--sp-3) 0; border-top: 1px solid var(--border); font-size: var(--fs-xs);
}
.clip-line-dot {
  width: var(--sp-3); height: var(--sp-3); border-radius: var(--r-round);
  background: var(--disabled); flex-shrink: 0;
}
.clip-line.state-completed .clip-line-dot { background: var(--ok); }
.clip-line.state-running .clip-line-dot { background: var(--accent); }
.clip-line.state-failed .clip-line-dot { background: var(--err); }
.clip-line-id { color: var(--muted); font-variant-numeric: tabular-nums; flex-shrink: 0; }
.clip-line-title {
  flex: 1; min-width: 0; color: var(--text); text-decoration: none;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.clip-line-title:hover { color: var(--accent); }
.clip-line-meta { color: var(--muted); flex-shrink: 0; font-variant-numeric: tabular-nums; }
.job-drawer-foot {
  display: inline-block; margin-top: var(--sp-4);
  font-size: var(--fs-xs); color: var(--accent); text-decoration: none;
}
.job-drawer-foot:hover { text-decoration: underline; }

.job-error {
  font-size: var(--fs-xs); color: var(--err); background: var(--err-soft);
  border-radius: var(--r-sm); padding: var(--sp-2) var(--sp-4); overflow-wrap: anywhere;
}
/* falha já superada por um retry: continua à vista (erro não se esconde), mas não em vermelho —
   vermelho aqui diria que algo está quebrado agora */
.job-error.is-past { color: var(--warn); background: var(--warn-soft); }

/* menu de ações da linha (`<details>` nativo, sem JS) */
.menu { position: relative; display: inline-block; flex-shrink: 0; z-index: 1; }
.menu-btn {
  background: transparent; border: 1px solid var(--border); color: var(--muted);
  border-radius: var(--r-md); padding: var(--sp-2) var(--sp-5); font-size: var(--fs-md);
  line-height: 1.3; cursor: pointer; list-style: none;
}
.menu-btn::-webkit-details-marker { display: none; }
.menu-btn:hover, .menu[open] .menu-btn { color: var(--text); border-color: var(--muted); }
.menu-pop {
  position: absolute; right: 0; top: calc(100% + var(--sp-2)); z-index: 20;
  display: flex; flex-direction: column; min-width: 170px;
  background: var(--bg); border: 1px solid var(--border); border-radius: var(--r-md);
  padding: var(--sp-2); box-shadow: 0 8px 24px #00000080;
}
.menu-item {
  padding: var(--sp-4) var(--sp-5); border-radius: var(--r-sm);
  font-size: var(--fs-md); color: var(--text); text-decoration: none; white-space: nowrap;
}
a.menu-item:hover, button.menu-item:hover { background: var(--panel); color: var(--accent); }
a.menu-item.is-danger:hover, button.menu-item.is-danger:hover { color: var(--err); }
/* item que EXECUTA é `<button>` dentro de um `<form>` (macro `row_menu`, chave `post`): o reset
   existe para ele ficar idêntico ao item que navega — a diferença entre os dois é o método HTTP,
   não a aparência */
.menu-form { margin: 0; display: contents; }
button.menu-item {
  background: none; border: 0; font: inherit; font-size: var(--fs-md);
  text-align: left; width: 100%; cursor: pointer;
}
/* o que ainda não existe: cinza, com o motivo no `title` (ver a macro `row_menu`) */
.menu-item.is-disabled { color: var(--disabled); cursor: default; }
.menu-sep { border-top: 1px solid var(--border); margin: var(--sp-2) 0; }

/* bloco de resultados trocado sem recarregar (static/live.js): enquanto a busca não volta, a lista
   antiga continua legível mas apagada — tela em branco a cada tecla é pior que meio segundo de espera */
.is-loading { opacity: 0.45; pointer-events: none; transition: opacity 120ms ease-in; }
/* com o filtro respondendo sozinho, o botão não faz nada de novo */
.filter-bar.is-live .filter-submit { display: none; }

/* =============================================================================
   Tabela
   ========================================================================== */
table { width: 100%; border-collapse: collapse; margin-top: var(--sp-5); }
th, td { text-align: left; padding: var(--sp-4) var(--sp-5); border-bottom: 1px solid var(--border); font-size: var(--fs-md); }
th { color: var(--muted); font-weight: 500; }
.jobs-table tbody tr { cursor: pointer; }
.jobs-table tbody tr:hover { background: var(--panel); }
.detail-table th { width: 160px; }
.col-actions { text-align: right; white-space: nowrap; }

/* =============================================================================
   Indicadores — badge, barra, donut, gauge
   ========================================================================== */
.badge { padding: var(--sp-1) var(--sp-4); border-radius: var(--r-lg); font-size: var(--fs-xs); font-weight: 600; }
.badge-pending { background: var(--muted-soft); color: var(--muted); }
.badge-running { background: var(--accent-soft); color: var(--accent); }
.badge-completed { background: var(--ok-soft); color: var(--ok); }
.badge-success { background: var(--ok-soft); color: var(--ok); }
.badge-failed { background: var(--err-soft); color: var(--err); }
.badge-cancelled { background: var(--muted-soft); color: var(--muted); }
.badge-warn { background: var(--warn-soft); color: var(--warn); }
/* rótulos da Central de Erros (A6) — não são status do core, são leitura do painel */
.badge-falhando { background: var(--err-soft); color: var(--err); }
.badge-superado { background: var(--muted-soft); color: var(--muted); }
/* veredito do dono (B5): não é status de clip — é o que ELE decidiu, e por isso cor própria */
.badge-aprovado { background: var(--ok-soft); color: var(--ok); }
.badge-reprovado { background: var(--err-soft); color: var(--err); }
.badge-lg { padding: var(--sp-2) var(--sp-6); font-size: var(--fs-sm); }

.bar { display: inline-block; width: 60px; height: var(--sp-3); background: var(--border); border-radius: var(--r-xs); overflow: hidden; vertical-align: middle; margin-right: var(--sp-3); }
.bar-fill { height: 100%; background: var(--accent); }
.bar-fill.state-ok { background: var(--ok); }
.bar-fill.state-warn { background: var(--warn); }
.bar-fill.state-failed { background: var(--err); }

/* a `timeline` encolhida: as mesmas bolinhas, sem rótulo, para caber numa linha de cartão */
.dots { display: flex; align-items: center; flex-shrink: 0; }
.dot { width: var(--dot-sm); height: var(--dot-sm); border-radius: var(--r-round); background: var(--border); flex-shrink: 0; }
.dot-line { width: var(--sp-8); height: 2px; background: var(--border); flex-shrink: 0; }
.dot.state-done, .dot-line.state-done { background: var(--ok); }
.dot.state-current { background: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
.dot.state-failed, .dot-line.state-failed { background: var(--err); }
.bar-wide { width: 100%; display: block; margin-right: 0; }

/* donut com total no centro + legenda em lista (concept 01) */
.chart-row { display: flex; gap: var(--sp-8); align-items: center; flex-wrap: wrap; }
.donut-wrap { position: relative; flex-shrink: 0; }
.donut-center {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  pointer-events: none;
}
.donut-center strong { font-size: var(--fs-display); font-weight: 700; }
.donut-center span { color: var(--muted); font-size: var(--fs-sm); }
.legend { flex: 1; min-width: 160px; display: flex; flex-direction: column; gap: var(--sp-4); }
.legend-item { display: flex; align-items: center; gap: var(--sp-4); font-size: var(--fs-md); }
.legend-dot { width: var(--sp-4); height: var(--sp-4); border-radius: var(--r-round); flex-shrink: 0; }
.legend-label { flex: 1; color: var(--text); }
.legend-value { color: var(--muted); }

/* =============================================================================
   Lista com ícone/avatar (concept 01 "Próximos Uploads", 06 "Contas")
   ========================================================================== */
.list { display: flex; flex-direction: column; }
.list-item { display: flex; align-items: center; gap: var(--sp-6); padding: var(--sp-5) 0; border-bottom: 1px solid var(--border); }
.list-item:last-child { border-bottom: none; }
.list-avatar {
  width: var(--icon-tile);
  height: var(--icon-tile);
  flex-shrink: 0;
  border-radius: var(--r-lg);
  background: var(--accent-faint);
  color: var(--accent);
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: var(--fs-h2);
}
.list-main { flex: 1; min-width: 0; }
.list-title { font-size: var(--fs-lg); font-weight: 600; }
.list-sub { color: var(--muted); font-size: var(--fs-sm); }
.list-meta { text-align: right; font-size: var(--fs-sm); color: var(--muted); white-space: nowrap; }
.list-meta strong { display: block; color: var(--accent); font-size: var(--fs-md); }

/* =============================================================================
   Formulário — genérico, para qualquer tela (o login usa as mesmas classes)
   ========================================================================== */
.form-stack { display: flex; flex-direction: column; gap: var(--sp-6); align-items: flex-start; }
.form-stack .field { align-self: stretch; }
.form-stack .btn { align-self: stretch; text-align: center; }
.field { display: flex; flex-direction: column; gap: var(--sp-2); font-size: var(--fs-md); color: var(--muted); }
.field input, .field select, .field textarea {
  background: var(--panel); border: 1px solid var(--border); color: var(--text);
  padding: var(--sp-4) var(--sp-5); border-radius: var(--r-md); font-size: var(--fs-lg);
  font-family: var(--font);
}
.field-hint { font-size: var(--fs-sm); color: var(--muted); }
/* campos que só fazem sentido juntos ("de 30s a 60s"); deitam no celular como a barra de filtros */
.field-row { display: flex; gap: var(--sp-6); flex-wrap: wrap; }
.field-row .field { flex: 1; min-width: 150px; }

/* escolha entre poucos valores fechados, todos à vista (macro `choice_row`).
   O `<input type=radio>` continua lá — só sai da vista. Removê-lo custaria teclado, leitor de tela
   e o `required` do navegador, e obrigaria JS para uma tela que não tem nenhum. */
.choice { border: none; padding: 0; margin: 0; display: flex; flex-direction: column; gap: var(--sp-3); }
.choice-label { padding: 0; font-size: var(--fs-md); color: var(--muted); }
.choice-opts { display: flex; flex-wrap: wrap; gap: var(--sp-4); }
.choice-opt { position: relative; display: inline-flex; cursor: pointer; }
.choice-opt input { position: absolute; opacity: 0; width: 0; height: 0; }
.choice-chip {
  border: 1px solid var(--border);
  background: var(--bg);
  color: var(--muted);
  border-radius: var(--r-lg);
  padding: var(--sp-4) var(--sp-7);
  font-size: var(--fs-md);
  line-height: 1.2;
  user-select: none;
}
.choice-opt:hover .choice-chip { border-color: var(--muted); color: var(--text); }
.choice-opt input:checked + .choice-chip {
  border-color: var(--accent); background: var(--accent-faint); color: var(--text); font-weight: 600;
}
/* o foco tem que aparecer: sem isto, navegar por teclado num rádio invisível é navegar às cegas */
.choice-opt input:focus-visible + .choice-chip { outline: 2px solid var(--accent); outline-offset: 2px; }

/* passo numerado de formulário longo (macro `form_step`) */
.form-step { margin-top: var(--sp-8); }
.form-step-head { display: flex; gap: var(--sp-6); align-items: flex-start; }
.form-step-head h2 { margin: 0; }
.form-step-n {
  flex-shrink: 0; width: 28px; height: 28px; border-radius: 50%;
  background: var(--accent-soft); color: var(--accent);
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-md); font-weight: 600;
}
.form-step-sub { color: var(--muted); font-size: var(--fs-sm); margin: var(--sp-1) 0 0; }
.form-step-body { display: flex; flex-direction: column; gap: var(--sp-6); margin-top: var(--sp-7); }

.login-box { max-width: 320px; margin: 60px auto; }
.login-box form { display: flex; flex-direction: column; gap: var(--sp-6); margin-top: var(--sp-8); }
.login-box label { display: flex; flex-direction: column; gap: var(--sp-2); font-size: var(--fs-md); color: var(--muted); }
.login-box input {
  background: var(--panel); border: 1px solid var(--border); color: var(--text);
  padding: var(--sp-4) var(--sp-5); border-radius: var(--r-md); font-size: var(--fs-lg);
}
.login-box button {
  background: var(--accent); color: white; border: none; padding: var(--sp-5); border-radius: var(--r-md);
  font-size: var(--fs-lg); cursor: pointer; margin-top: var(--sp-4);
}

/* =============================================================================
   Player de clip — vertical, na proporção em que o render sai (9:16)
   ========================================================================== */
.player { display: flex; flex-direction: column; gap: var(--sp-4); width: var(--player-w); }
.player-video {
  width: 100%;
  aspect-ratio: 9 / 16;
  display: block;
  background: var(--video-bg);
  border: 1px solid var(--border);
  border-radius: var(--r-lg);
}
.player-caption { color: var(--muted); font-size: var(--fs-sm); }

/* =============================================================================
   Régua da fronteira — fase 4d ("loop de captura"). A faixa escura (`ruler-selected`) é a
   PROPOSTA do LLM; a clara (`ruler-folga`) é a janela renderizada além dela, onde pode estar o
   início/remate que o corte cortou. Largura igual à do player: os dois nascem como uma peça só.
   ========================================================================== */
.ruler { display: flex; flex-direction: column; gap: var(--sp-3); width: var(--player-w); }
.ruler-track {
  position: relative;
  height: var(--sp-9);
  background: var(--bg);
  border: 1px solid var(--border);
  border-radius: var(--r-sm);
  touch-action: none; /* o handle é quem decide o gesto — sem isto o scroll da página rouba o toque */
}
.ruler-folga { position: absolute; top: 0; bottom: 0; left: 0; background: var(--muted-soft); }
.ruler-folga-post { left: auto; right: 0; }
.ruler-selected {
  position: absolute; top: 0; bottom: 0;
  background: var(--accent-soft);
  border-left: 1px solid var(--accent);
  border-right: 1px solid var(--accent);
}
.ruler-handle {
  position: absolute; top: 50%; z-index: 1;
  width: var(--sp-8); height: calc(var(--sp-9) + var(--sp-6));
  background: var(--accent); border: 2px solid var(--panel); border-radius: var(--r-xs);
  transform: translate(-50%, -50%);
  cursor: ew-resize; padding: 0; touch-action: none;
}
.ruler-handle:focus-visible { outline: 2px solid var(--text); outline-offset: 2px; }
.ruler-handle.is-dirty { background: var(--warn); }
.ruler-readout {
  display: flex; align-items: center; gap: var(--sp-4);
  font-family: var(--font-mono); font-size: var(--fs-sm); color: var(--muted);
}
.ruler-time.is-dirty { color: var(--warn); font-weight: 600; }
.ruler-arrow { color: var(--muted); }
.ruler-reset {
  margin-left: auto; background: none; border: none; padding: 0;
  color: var(--accent); font-size: var(--fs-sm); text-decoration: underline; cursor: pointer;
}

/* =============================================================================
   Responsivo

   O menu lateral de 220px é ~40% da largura de um celular: sobrava metade da tela para o conteúdo,
   e a Biblioteca (que é grade de vídeo) ficava com uma coluna estreita. Abaixo de 900px o menu
   deita: vira uma tira horizontal rolável no topo, fixa, e o conteúdo fica com a largura toda.
   Os itens desabilitados (tela que ainda não existe) somem lá — não são clicáveis, e na tira
   horizontal eles só empurrariam para longe os cinco links que funcionam.
   ========================================================================== */
@media (max-width: 900px) {
  .layout { flex-direction: column; }
  .sidebar {
    width: 100%;
    flex-direction: row;
    align-items: center;
    gap: var(--sp-2);
    padding: var(--sp-4) var(--sp-5);
    border-right: none;
    border-bottom: 1px solid var(--border);
    overflow-x: auto;
    position: sticky;
    top: 0;
    z-index: 10;
  }
  .brand { padding: 0 var(--sp-5) 0 var(--sp-2); white-space: nowrap; }
  .nav-item { white-space: nowrap; font-size: var(--fs-md); }
  .sidebar .nav-item.disabled { display: none; }
  .nav-logout { margin-top: 0; }
  .content { padding: var(--sp-8) var(--sp-6); }
}

@media (max-width: 640px) {
  .jobs-table thead { display: none; }
  .jobs-table tr { display: block; border-bottom: 1px solid var(--border); padding: var(--sp-4) 0; }
  .jobs-table td { display: block; border: none; padding: var(--sp-1) 0; }
  /* o título desce para uma linha só dele (`order`), em vez de dividir a primeira com id, badge, %
     e menu — em 400px sobrariam ~120px para ele. Id, estado, % e menu continuam em cima, que é o
     que se varre com o olho. */
  .job-head { flex-wrap: wrap; }
  .job-title { order: 1; flex-basis: 100%; }
  /* card menor = duas colunas de vídeo em pé num celular, em vez de uma */
  :root { --clip-card-w: 150px; }
  .clip-body { padding: var(--sp-5); gap: var(--sp-2); }
  /* filtro: dois campos por linha (sete campos empilhados empurram a lista para fora da tela) */
  .filter-bar { padding: var(--sp-5); gap: var(--sp-4); }
  .filter-bar .field { flex: 1 1 44%; }
  .filter-bar .field-grow { flex-basis: 100%; }
  .filter-actions { margin-left: 0; }
}
