:root {
  --bg: #fafaf8;
  --pane: #ffffff;
  --border: #e5e3df;
  --text: #1f1d1a;
  --muted: #6b655c;
  --accent: #b8541d;
  --accent-strong: #9c4416;
  --accent-soft: #f7ecdf;
  --success: #2f8a4f;
  --amber: #a9701a;
  --amber-soft: #fbf0db;
  /* O `--amber` foi escolhido para números grandes (o KPI a 26px, onde o limiar
     WCAG é 3:1) e mede 4.18:1 sobre branco — abaixo dos 4.5:1 que texto normal
     exige. Os avisos de "sem tarifa" são prosa a 11px, e usá-lo tal e qual
     trocava um total honesto por um aviso ilegível. Este é o mesmo âmbar,
     escurecido até 5.9:1 sobre #ffffff (5.7:1 sobre o --bg). */
  --amber-text: #8a5a12;
  --user-bg: #eef4ff;
  --user-border: #c5d6f5;
  --assistant-bg: #ffffff;
  --tool-bg: #f3f1ec;
  --tool-border: #d9d4cb;
  --error-bg: #fdecec;
  --error-border: #f0b4b4;
  --code-bg: #2b2a26;
  --code-text: #f5f1e8;
  --user-accent: #2c5fb6;
  --code-inline-bg: rgba(0, 0, 0, 0.06);
  --hl-bg: #ffe28a;
  --hl-text: #1f1d1a;
  --hl-current-bg: #ffb347;
  --danger: #c0392b;
  --danger-strong: #a93226;
  --danger-soft: #fdecec;
}

:root[data-theme="dark"] {
  --bg: #1a1917;
  --pane: #242320;
  --border: #38362f;
  --text: #ece8e1;
  --muted: #9b948a;
  --accent: #e07b43;
  --accent-strong: #f0905a;
  --accent-soft: #392a1d;
  --success: #5fbf7a;
  --amber: #e0a93f;
  --amber-soft: #3a2f17;
  /* No escuro o --amber já mede 7.4:1 sobre o --pane: nada a corrigir. */
  --amber-text: #e0a93f;
  --user-bg: #1d2733;
  --user-border: #33455f;
  --assistant-bg: #242320;
  --tool-bg: #2a2823;
  --tool-border: #3d3a33;
  --error-bg: #3a2120;
  --error-border: #6a3a38;
  --code-bg: #141310;
  --code-text: #f3efe7;
  --user-accent: #74a7ff;
  --code-inline-bg: rgba(255, 255, 255, 0.09);
  --hl-bg: #e0a93c;
  --hl-text: #1a1917;
  --hl-current-bg: #ff9e2c;
  --danger: #e06b5c;
  --danger-strong: #c0392b;
  --danger-soft: #3a2120;
}

/* Diz ao browser qual das duas polaridades está mesmo activa, para os controlos
   nativos (scrollbars, <select>, pickers) não ficarem invertidos. Enquanto o
   modo claro era a AUSÊNCIA de data-theme isto não era preciso; agora que o
   atributo é sempre escrito, é. */
:root[data-theme="light"] { color-scheme: light; }
:root[data-theme="dark"] { color-scheme: dark; }

* { box-sizing: border-box; }

/* Accent-tinted scrollbars so they sit with the layout instead of the OS default. */
* {
  scrollbar-width: thin;
  scrollbar-color: var(--accent) transparent;
}
::-webkit-scrollbar { width: 10px; height: 10px; }
::-webkit-scrollbar-track { background: transparent; }
::-webkit-scrollbar-thumb {
  background: var(--accent);
  border-radius: 6px;
  border: 2px solid var(--pane);
}
::-webkit-scrollbar-thumb:hover { background: var(--accent-strong, var(--accent)); filter: brightness(1.12); }
::-webkit-scrollbar-corner { background: transparent; }

html, body { height: 100%; margin: 0; }
html {
  /* App-like feel: no rubber-band/pull-to-refresh, no font inflation on rotate,
     no grey tap flash on touch. */
  overscroll-behavior: none;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  -webkit-tap-highlight-color: transparent;
}
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, Roboto, sans-serif;
  font-size: 14px;
  color: var(--text);
  background: var(--bg);
  display: grid;
  grid-template-columns: var(--col-projects, 260px) var(--col-sessions, 320px) 1fr;
  grid-template-rows: 1fr auto;
  /* Zoom global da app (Definições → Aparência): escala texto + espaçamentos +
     colunas proporcionalmente. Como o zoom multiplica as dimensões, pedimos 1/z
     das unidades de viewport para o grid continuar a preencher exatamente o ecrã
     em vez de transbordar e gerar scroll. */
  zoom: var(--ui-zoom, 1);
  width: calc(100vw / var(--ui-zoom, 1));
  height: calc(100vh / var(--ui-zoom, 1));
  height: calc(100dvh / var(--ui-zoom, 1));
  overscroll-behavior: none;
  position: relative;
}
/* O terminal ao vivo é independente do zoom da app: cancela o zoom herdado para
   o xterm continuar em px reais (a fonte/tamanho do terminal têm config própria).
   Ao mudar o zoom, applyUiZoom() chama fitTerminal() para re-medir. */
#terminal-view { zoom: calc(1 / var(--ui-zoom, 1)); }

/* Os controlos de formulário NÃO herdam a fonte: o browser dá-lhes o seu
   próprio tipo de letra (Arial/Helvetica no macOS) e é preciso dizer-lhe
   explicitamente que não. Sem esta regra de base, cada campo novo nasce numa
   fonte diferente do resto — foi assim que 12 controlos do painel ⚙ acabaram
   em Arial enquanto os da aplicação estavam certos. A regra apanha também os
   controlos que ainda não existem, que é o ponto: uma classe nova não pode
   reintroduzir o problema. Ver docs/claude/ui-conventions.md. */
input, select, textarea, button {
  font-family: inherit;
}
.pane {
  background: var(--pane);
  border-right: 1px solid var(--border);
  display: flex;
  flex-direction: column;
  overflow: hidden;
}
#transcript { border-right: none; position: relative; }

.grid-view {
  position: fixed;
  inset: 0;
  z-index: 50; /* abaixo do .modal-overlay (100) — um alerta/confirmação por cima continua a aparecer */
  background: var(--bg);
  display: flex;
  flex-direction: column;
}
.grid-view.hidden { display: none; }
.grid-view-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  flex: none;
}
.grid-view-head h2 { margin: 0; font-size: 15px; }
.grid-view-empty {
  margin: auto;
  color: var(--muted);
}
.grid-view-empty.hidden { display: none; }
.grid-view-body {
  flex: 1;
  display: grid;
  gap: 8px;
  padding: 8px;
  min-height: 0;
}
.grid-panel {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  background: var(--pane);
}
/* A bola está do lado do dono. Realce na MOLDURA, não no ponto: o ponto verde já
   tem `animation: live-pulse` permanente em todos os painéis vivos aqui, e um
   segundo halo a expandir noutra cor é o mesmo efeito visual — o dono leu o halo
   de sempre como um chamamento ("o pulsar está a aparecer numa janela 'a
   trabalhar…'"). A moldura não compete com nada e lê-se de longe, com 4 painéis
   no ecrã.

   DUAS cores, porque os dois casos não se agem da mesma maneira (decisão do
   dono, nas palavras dele): VERDE quando está livre para falar e nada corre por
   baixo — terminou e aguarda mensagem, ou aguarda uma decisão dele; ÂMBAR
   quando pode falar na mesma mas ficou um shell pendente. O âmbar sinaliza a
   pendência, não um problema.

   `border-color` + `box-shadow` em vez de engrossar a borda: 1px a mais
   deslocava o conteúdo do painel. O anel é NÍTIDO e não difuso porque é normal
   ter 3 de 4 painéis realçados ao mesmo tempo — um glow de 12px sangrava para o
   vizinho por cima do gap de 8px do grid, borrando a fronteira em vez de a
   marcar. Sem animação, de propósito: a queixa que trouxe esta moldura era
   movimento indistinguível, e uma moldura a cores entre painéis cinzentos já é
   inequívoca.

   `color-mix` nos DOIS cabeçalhos com a mesma percentagem (e não `--amber-soft`
   num e uma mistura no outro) para nenhuma das cores ficar mais pesada que a
   outra por acidente — não existe `--success-soft` no ficheiro. */
.grid-panel.attn-pronto {
  border-color: var(--success, #5fbf7a);
  box-shadow: 0 0 0 1px var(--success, #5fbf7a);
}
.grid-panel.attn-pronto .grid-panel-head {
  background: color-mix(in srgb, var(--success, #5fbf7a) 14%, transparent);
}
.grid-panel.attn-pronto .grid-panel-state { color: var(--success, #5fbf7a); }
.grid-panel.attn-background {
  border-color: var(--amber, #e0a93f);
  box-shadow: 0 0 0 1px var(--amber, #e0a93f);
}
.grid-panel.attn-background .grid-panel-head {
  background: color-mix(in srgb, var(--amber, #e0a93f) 14%, transparent);
}
.grid-panel.attn-background .grid-panel-state { color: var(--amber, #e0a93f); }
.grid-panel-head {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 10px;
  font-size: 12px;
  cursor: pointer;
  border-bottom: 1px solid var(--border);
  flex: none;
}
.grid-panel-head:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
/* Badge de projeto: com 4 painéis de projetos diferentes (o caso de uso
   central), o título da conversa sozinho pode não desambiguar. Mesma pílula
   accent-soft do `.lg-count` do modal de sessões ao vivo. Encolhe primeiro
   (flex: 0 1 auto) — quem tem de sobrar é o título. */
.grid-panel-proj {
  flex: 0 1 auto;
  min-width: 0;
  max-width: 40%;
  padding: 2px 7px;
  border-radius: 9px;
  font: 600 10px/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--accent-soft);
  color: var(--accent);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.grid-panel-proj.hidden { display: none; }
.grid-panel-title {
  flex: 1;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* O estado da sessão ("a tua vez", "a trabalhar…", "à espera de ti"), os mesmos
   textos da barra do terminal da vista normal. `flex: none` para o título ceder
   espaço primeiro: entre truncar o título e truncar o estado, o estado é o que
   o dono está a procurar quando olha para a grelha. */
.grid-panel-state {
  flex: none;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
.grid-panel-state[hidden] { display: none; }
/* O ponto do cabeçalho carrega dois eixos: a cor diz ONDE a sessão corre
   (`.live-here`/`.live-elsewhere`), este pulso diz que ela está à ESPERA de
   alguém. Mesma animação do resto da app, para "precisa de ti" se ler igual em
   todas as superfícies. */
/* `flex: 1; min-height: 0` não é decoração: o `.grid-panel` é uma coluna flex e
   um filho `flex: 0 1 auto` dimensiona-se ao CONTEÚDO, não ao painel. Sem eles,
   um corpo com uma transcrição de dezenas de mensagens ficava MAIOR que o
   painel — `clientHeight === scrollHeight`, logo sem barra de scroll e com o
   excedente cortado pelo `overflow: hidden` do painel (via-se o topo da
   conversa e mais nada; o `scrollTop = scrollHeight` do
   loadGridPanelTranscript e o `if (atBottom)` do appendGridPanelEntries
   tornavam-se no-ops) — e o `FitAddon` media o próprio xterm em vez do painel,
   devolvendo o seu default de 24 linhas, que seguia por `pty.resize` (GLOBAL ao
   tmux) para o docado, o flutuante e os outros painéis do mesmo sid.
   Sem `position: relative` de propósito: os únicos descendentes absolutos aqui
   dentro são o `.code-copy` (contido pelo `.code-wrap`, esse relativo) e os
   internos do xterm (contidos pelo `.grid-panel-term`, que mantém o seu). */
.grid-panel-body { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.grid-panel-term { flex: 1; min-height: 0; position: relative; }
.grid-panel-term[hidden] { display: none; }
.grid-panel-transcript {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
  padding: 8px 10px;
}
.grid-panel-transcript[hidden] { display: none; }
.grid-panel-views { flex: none; margin-left: 4px; }
.grid-panel-views[hidden] { display: none; }
.grid-panel-views .view-tab { padding: 2px 5px; }

/* O renderMessage emite .mem-btn/.fork-btn e o enhanceCodeBlocks injecta
   .code-copy, mas os três handlers estão delegados no #transcript-body da vista
   normal: aqui dentro seriam botões MORTOS e visíveis. Ligá-los era pior — o
   saveToMemory lê renderedMessages[idx], o índice da OUTRA superfície, logo um
   clique agiria sobre a mensagem errada. O painel é leitura; para agir numa
   mensagem, o cabeçalho salta para a vista normal.
   A QUARTA família é o `.fc-acts` dos filechips (pré-visualizar/descarregar):
   o `renderMessage` emite o chip em estado `pending` e é o `hydrateFileChips`
   que lhe põe o tamanho e o href — mas esse varre só o `transcriptBody`, por
   isso aqui o chip fica eternamente `pending`, com um ✎ sem `href` e um ⤢ sem
   mime. Mesma decisão das outras três: escondê-lo em vez de o hidratar (o
   `hydrateFileChips` depende do `activeSession` para montar o URL, que não é
   necessariamente o sid deste painel). */
.grid-panel-transcript .mem-btn,
.grid-panel-transcript .fork-btn,
.grid-panel-transcript .code-copy { display: none; }
/* Regra à parte e com o `.filechip` pelo meio de propósito: `.filechip .fc-acts`
   (mais abaixo neste ficheiro) declara `display: flex` com a MESMA
   especificidade que `.grid-panel-transcript .fc-acts` teria, e vence por ser
   posterior — medido, as acções continuavam a aparecer. Três classes em vez de
   duas resolvem-no sem depender da ordem. */
.grid-panel-transcript .filechip .fc-acts { display: none; }
.grid-panel-empty {
  height: 100%;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 8px;
  color: var(--muted);
  padding: 12px;
  text-align: center;
  font-size: 12px;
}

/* Per-column background — the "Tema de cores" presets below (⚙ Definições)
   override these vars; unset (default/light) they all fall back to the
   existing shared vars (--pane/--bg/--accent-soft), so behaviour is unchanged
   unless a preset is picked.
   As skins aplicam-se sob AMBAS as polaridades: [data-theme="light"][data-skin="papel"]
   e [data-theme="dark"][data-skin="floresta"] são os dois casos. Cada skin
   redefine também --text/--muted, por isso não há o conflito que a versão
   anterior deste comentário descrevia (paletes escuras sob texto escuro).
   --row-active / --tab-active / --input-surface / --statusbar-bg cover the
   bits that read as "tied to the original (warm) palette" once the column
   backgrounds go blue: the selected/hovered row, the active session tab, the
   filter/search inputs, and the bottom usage bar.
   --border / --pane / --tool-bg / --assistant-bg / --accent-soft round out
   the rest of the surfaces a preset otherwise leaves on the old warm-dark
   values: --border is every pane/modal/row divider (incl. the projects↔
   sessions↔transcript separators), --pane is every "elevated neutral"
   surface (modals, the ⋯ overflow menu, chips/badges — anything that isn't
   one of the three main columns), --tool-bg/--assistant-bg are the
   transcript message bubbles (assistant blends into --pane-transcript like
   it does with --pane today; tool gets one step lighter for contrast), and
   --accent-soft is the hover fill used across icon buttons/menus/rows —
   kept as a translucent accent tint (instead of a flat warm-brown hex) so
   it reads correctly over any of these hues.
   --border is a translucent WHITE overlay, not a flat hex: painted on top
   of whatever surface it borders (--bg, a pane, --pane, --input-surface —
   all different lightnesses within one preset) it always reads as "a touch
   lighter than its own background", which is what makes a divider read as
   subtle rather than a hard line, and it's automatically the right hue
   since it's just a lightened version of the surface underneath.
   --bg is the icon-button / secondary-panel "flat surface" color (most
   small buttons across the app — .icon-btn, .view-tab, backlog/tunnel/
   account panel rows — use var(--bg) directly); overriding it per preset
   to the same recessed tone as --input-surface stops those buttons from
   sitting on the pane as a mismatched near-black square. body{background}
   is fully covered by the three columns, so this override is invisible
   except where --bg is used as an actual surface. */
#projects { background: var(--pane-projects, var(--pane)); }
#sessions { background: var(--pane-sessions, var(--pane)); }
#transcript { background: var(--pane-transcript, var(--pane)); }

/* Cada preset redefine também --text/--muted (cinzas com o viés de hue da
   palete — o warm-grey do tema base lê "sujo" sobre azul), --tool-border e
   --code-bg (as duas últimas superfícies que ficavam warm-órfãs no meio de
   colunas azuis: a moldura das bolhas de tool e o fundo dos blocos de código).
   Todos os pares texto/fundo mantêm ≥4.5:1 (AA) — verificado pelo script de
   auditoria de contraste (scratchpad/contrast-audit.mjs na sessão de origem). */
:root[data-theme="dark"][data-skin="escuro"] {
  --pane-projects: #12161d;
  --pane-sessions: #0e131a;
  --pane-transcript: #151b23;
  --input-surface: #0a0d12;
  --row-active: rgba(120, 150, 190, 0.14);
  --tab-active: rgba(120, 150, 190, 0.22);
  --statusbar-bg: #0c0f14;
  --bg: #0a0d12;
  --border: rgba(255, 255, 255, 0.08);
  --pane: #1b212a;
  --tool-bg: #1c232d;
  --tool-border: rgba(255, 255, 255, 0.10);
  --assistant-bg: #151b23;
  --accent-soft: rgba(224, 123, 67, 0.14);
  --text: #eaecef;
  --muted: #98a1ad;
  --code-bg: #0b0e13;
}

/* Papel — claro, quente, pouco azul. NÃO redefine --accent: o #b8541d da base
   dá 4.31:1 sobre as colunas e 3.91:1 sobre --bg, ambos acima do mínimo de 3.0,
   por isso não há razão de contraste para lhe mexer — e mexer contrariava a
   decisão de manter o mesmo terracota nos oito temas. */
:root[data-theme="light"][data-skin="papel"] {
  --pane-projects: #f6f1e7;
  --pane-sessions: #f2ece0;
  --pane-transcript: #fbf7f0;
  --input-surface: #f0e9db;
  --row-active: #e6dcc8;
  --tab-active: #e6dcc8;
  --statusbar-bg: #efe8da;
  --bg: #ede6d8;
  --border: #ddd3c0;
  --pane: #fffdf8;
  --tool-bg: #f0e9db;
  --tool-border: #dcd1bd;
  --assistant-bg: #fbf7f0;
  --accent-soft: #f2e4d2;
  --user-bg: #eef1e4;
  --user-border: #cfd6bc;
  --user-accent: #4a6b2f;
  --text: #2a2419;
  --muted: #6b6252;
  --code-bg: #2f2a20;
  --code-text: #f4ecdc;
}

/* Névoa — claro, frio, contraste alto. O oposto exato do Papel. */
:root[data-theme="light"][data-skin="nevoa"] {
  --pane-projects: #eef1f5;
  --pane-sessions: #e9edf2;
  --pane-transcript: #f8fafc;
  --input-surface: #e8edf3;
  --row-active: #dae2ec;
  --tab-active: #dae2ec;
  --statusbar-bg: #e6ebf1;
  --bg: #e3e8ef;
  --border: #d3dae3;
  --pane: #ffffff;
  --tool-bg: #e8edf3;
  --tool-border: #cfd8e2;
  --assistant-bg: #f8fafc;
  --accent-soft: #fbe9dc;
  --user-bg: #e4ecfa;
  --user-border: #bfd0ea;
  --user-accent: #23558f;
  --text: #16202b;
  --muted: #586675;
  --code-bg: #1b232d;
  --code-text: #eef2f7;
}

/* Floresta — escuro, verde-abeto. O terracota vira complementar e salta. */
:root[data-theme="dark"][data-skin="floresta"] {
  --pane-projects: #0d1614;
  --pane-sessions: #0a1211;
  --pane-transcript: #101b18;
  --input-surface: #071110;
  --row-active: rgba(110, 200, 165, 0.13);
  --tab-active: rgba(110, 200, 165, 0.20);
  --statusbar-bg: #081412;
  --bg: #071110;
  --border: rgba(255, 255, 255, 0.08);
  --pane: #16241f;
  --tool-bg: #17261f;
  --tool-border: rgba(255, 255, 255, 0.10);
  --assistant-bg: #101b18;
  --accent-soft: rgba(224, 123, 67, 0.15);
  --user-bg: #132420;
  --user-border: #2b4a40;
  --user-accent: #6fc9a8;
  --text: #e7efea;
  --muted: #8fa79c;
  --code-bg: #060f0e;
}

/* Ameixa — escuro, roxo quente sem ser castanho. */
:root[data-theme="dark"][data-skin="ameixa"] {
  --pane-projects: #16101c;
  --pane-sessions: #120d17;
  --pane-transcript: #1b1422;
  --input-surface: #0e0a12;
  --row-active: rgba(170, 140, 220, 0.14);
  --tab-active: rgba(170, 140, 220, 0.22);
  --statusbar-bg: #100b14;
  --bg: #0e0a12;
  --border: rgba(255, 255, 255, 0.09);
  --pane: #241a2c;
  --tool-bg: #261c2e;
  --tool-border: rgba(255, 255, 255, 0.11);
  --assistant-bg: #1b1422;
  --accent-soft: rgba(224, 123, 67, 0.16);
  --user-bg: #1e1a30;
  --user-border: #3d3557;
  --user-accent: #9d8bff;
  --text: #ece7f1;
  --muted: #a094ab;
  --code-bg: #0d0910;
}

/* Tinta — escuro, quase-preto neutro. #0a0a0a e não #000: o preto puro faz
   smear em OLED e dá uma aresta dura contra as bolhas. O --code-bg é a única
   superfície que vai a #000, por ser um bloco fechado e sem texto por cima. */
:root[data-theme="dark"][data-skin="tinta"] {
  --pane-projects: #0a0a0a;
  --pane-sessions: #060606;
  --pane-transcript: #0e0e0e;
  --input-surface: #030303;
  --row-active: rgba(255, 255, 255, 0.09);
  --tab-active: rgba(255, 255, 255, 0.14);
  --statusbar-bg: #080808;
  --bg: #030303;
  --border: rgba(255, 255, 255, 0.11);
  --pane: #141414;
  --tool-bg: #161616;
  --tool-border: rgba(255, 255, 255, 0.13);
  --assistant-bg: #0e0e0e;
  --accent-soft: rgba(224, 123, 67, 0.15);
  --user-bg: #101418;
  --user-border: #2b3238;
  --user-accent: #7fb0e8;
  --text: #ededed;
  --muted: #8d8d8d;
  --code-bg: #000000;
  --code-text: #ededed;
}

.msg-nav {
  position: absolute;
  bottom: 28px;
  right: 20px;
  display: flex;
  flex-direction: column;
  gap: 3px;
  z-index: 20;
}
.msg-nav-btn {
  width: 28px;
  height: 28px;
  border-radius: 6px;
  border: 1px solid var(--border);
  background: var(--pane);
  color: var(--muted);
  cursor: pointer;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  opacity: 0.7;
  transition: opacity 0.1s, border-color 0.1s, color 0.1s;
}
.msg-nav-btn:hover {
  opacity: 1;
  border-color: var(--accent);
  color: var(--accent);
}
/* Over the terminal: solid pane fill + accent outline so the buttons read
   clearly against xterm without the loud full-accent block. */
.msg-nav.over-terminal .msg-nav-btn {
  opacity: 1;
  background: var(--pane);
  border-color: var(--accent);
  color: var(--accent);
  box-shadow: 0 2px 8px rgba(0, 0, 0, 0.4);
}
.msg-nav.over-terminal .msg-nav-btn:hover { background: var(--accent); color: var(--bg); }

/* ── Semáforo do sistema (#nav-health, no cluster flutuante) ───────────────── */
.nav-health { position: relative; }
.nav-health .sys-dot-lg {
  width: 10px; height: 10px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 8px; font-weight: 900; line-height: 1; color: transparent;
  background: var(--muted);
}
#nav-health[data-sys-verdict="apto"] .sys-dot-lg { background: #34c759; }
#nav-health[data-sys-verdict="degradado"] .sys-dot-lg { background: var(--amber); }
#nav-health[data-sys-verdict="bloqueado"] .sys-dot-lg {
  background: var(--danger); color: #2a0f0c;
  animation: friday-attn-pulse 1.3s ease-in-out infinite;
}
/* O glifo NÃO é decorativo: o prefers-reduced-motion desta app é um kill switch
   GLOBAL (ver o bloco em style.css) e apaga o pulso. Sem o `!`, âmbar e vermelho
   ficavam a distinguir-se só pela cor — falha WCAG 1.4.1. */
#nav-health[data-sys-verdict="bloqueado"] .sys-dot-lg::after { content: "!"; }

/* Pele "chip": quando é o ÚNICO elemento renderizado (R1), um ponto de 10px
   lê-se como decoração — por isso ganha rótulo. */
.nav-health-label { display: none; }
#nav-health[data-sys-skin="chip"] {
  width: auto; height: 28px; border-radius: 14px; padding: 0 11px 0 9px;
  gap: 7px; font-size: 11px; font-weight: 650; white-space: nowrap;
  color: var(--amber); border-color: color-mix(in srgb, var(--amber) 45%, var(--border));
}
#nav-health[data-sys-skin="chip"][data-sys-verdict="bloqueado"] {
  color: var(--danger); border-color: color-mix(in srgb, var(--danger) 45%, var(--border));
}
#nav-health[data-sys-skin="chip"] .nav-health-label { display: inline; }

/* Fuga a R1 — o cluster inteiro está .hidden, mas o ponto tem de aparecer.
   O app.js põe .sys-escape no #msg-nav quando dotVisibility devolve 'chip'.
   NOTA: `.hidden` global (mais abaixo, "Generic visibility helper") é
   `display: none !important` — sem !important aqui esta regra nunca ganhava
   a cascata, por mais classes que somasse. */
.msg-nav.hidden.sys-escape { display: flex !important; }
.msg-nav.hidden.sys-escape > :not(#nav-health) { display: none; }

/* A pele `over-terminal` (accent sólido) pintaria o ponto de laranja SEMPRE —
   o semáforo deixaria de reportar estado. O botão perde a pele, mas o ponto
   mantém a cor do veredicto (os seletores `[data-sys-verdict=…] .sys-dot-lg`
   acima continuam a ganhar — por isso NÃO há aqui uma regra a repintar o
   próprio ponto com `background: inherit`, que os anularia). */
.msg-nav.over-terminal .nav-health { background: var(--pane); color: var(--muted); }

/* ── Painel de Estado do sistema (o que o semáforo abre; markup em
   public/system-ui.js → panelHtml) ────────────────────────────────────────
   Sem estas regras o painel caía no layout-bloco por omissão do browser: o
   `detail` e a etiqueta de evidência colavam-se ("disponívelinternal state") e
   as pills empilhavam. Usa só tokens do tema (var()/color-mix), por isso adapta
   a claro e escuro. Renderizado nos dois temas antes de entrar. */
.sys-panel { display: flex; flex-direction: column; }
.sys-verdict {
  display: flex; align-items: flex-start; gap: 11px;
  padding: 11px 13px; border-radius: 12px; margin-bottom: 6px;
  font-size: 12.5px; line-height: 1.45; color: var(--text);
}
.sys-verdict b { display: block; font-size: 13.5px; margin-bottom: 1px; }
.sys-verdict span { color: var(--muted); }
.sys-verdict::before { font-size: 17px; line-height: 1.2; flex: none; }
.sys-v-apto { background: color-mix(in srgb, var(--success) 14%, transparent); border: 1px solid color-mix(in srgb, var(--success) 40%, var(--border)); }
.sys-v-apto::before { content: "\25CF"; color: var(--success); }
.sys-v-degradado { background: var(--amber-soft); border: 1px solid color-mix(in srgb, var(--amber) 45%, var(--border)); }
.sys-v-degradado::before { content: "\25D0"; color: var(--amber); }
.sys-v-bloqueado { background: var(--danger-soft); border: 1px solid color-mix(in srgb, var(--danger) 45%, var(--border)); }
.sys-v-bloqueado::before { content: "!"; color: var(--danger); font-weight: 800; }

.sys-grp {
  display: flex; align-items: center; gap: 8px;
  font-size: 10px; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--muted); margin: 14px 0 3px; font-weight: 600;
}
.sys-grp-n { margin-left: auto; letter-spacing: 0; text-transform: none; font-size: 10.5px; font-weight: 400; }

.sys-row { display: flex; align-items: center; gap: 10px; padding: 9px 2px; border-bottom: 1px solid var(--border); }
.sys-row:last-child { border-bottom: none; }
.sys-dot { width: 7px; height: 7px; border-radius: 50%; flex: none; background: var(--muted); }
.sys-dot.sys-ok { background: var(--success); }
.sys-dot.sys-warn { background: var(--amber); }
.sys-dot.sys-bad { background: var(--danger); }
.sys-dot.sys-off { background: var(--muted); opacity: 0.5; }
.sys-dot.sys-unknown { background: var(--muted); }
.sys-main { flex: 1; min-width: 0; }
.sys-name { font-size: 12.5px; font-weight: 600; color: var(--text); }
.sys-sub { font-size: 10.5px; color: var(--muted); margin-top: 2px; display: flex; align-items: baseline; gap: 7px; flex-wrap: wrap; }
.sys-ev {
  font-size: 9px; letter-spacing: 0.03em; text-transform: uppercase;
  color: var(--muted); opacity: 0.65; border: 1px solid var(--border);
  border-radius: 4px; padding: 0 5px; white-space: nowrap; flex: none;
}

.sys-pill { font-size: 9.5px; font-weight: 700; padding: 3px 8px; border-radius: 20px; white-space: nowrap; flex: none; }
.sys-pill.sys-ok { background: color-mix(in srgb, var(--success) 16%, transparent); color: var(--success); }
.sys-pill.sys-warn { background: var(--amber-soft); color: var(--amber); }
.sys-pill.sys-bad { background: var(--danger-soft); color: var(--danger); }
.sys-pill.sys-off { background: color-mix(in srgb, var(--muted) 14%, transparent); color: var(--muted); }
.sys-pill.sys-unknown { background: color-mix(in srgb, var(--muted) 14%, transparent); color: var(--muted); }

.sys-act {
  font-size: 11px; font-weight: 600; color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
  background: var(--accent-soft); border-radius: 7px; padding: 4px 10px;
  white-space: nowrap; flex: none; cursor: pointer; font-family: inherit;
}
.sys-act:hover { background: color-mix(in srgb, var(--accent) 20%, transparent); }

.sys-fold { border-top: 1px solid var(--border); margin-top: 6px; }
.sys-fold summary { cursor: pointer; list-style: none; padding-top: 4px; }
.sys-fold summary::-webkit-details-marker { display: none; }
.sys-fold summary .sys-grp { margin: 8px 0 3px; }
.sys-fold summary .sys-grp::after { content: "\25B8"; margin-left: 6px; font-size: 9px; opacity: 0.6; letter-spacing: 0; }
.sys-fold[open] summary .sys-grp::after { content: "\25BE"; }

.sys-refresh {
  margin-top: 14px; align-self: flex-start;
  font-size: 12px; font-weight: 600; color: var(--muted);
  border: 1px solid var(--border); background: transparent;
  border-radius: 8px; padding: 6px 12px; cursor: pointer; font-family: inherit;
}
.sys-refresh:hover { border-color: var(--accent); color: var(--accent); }

.pane > header {
  padding: 14px 16px;
  border-bottom: 1px solid var(--border);
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
}
.pane h1, .pane h2 {
  font-size: 13px;
  font-weight: 600;
  margin: 0;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  color: var(--muted);
}
.muted { color: var(--muted); font-size: 12px; }

/* The transcript title doubles as the rename affordance once a session is open:
   click it to edit. The pencil hints at it on hover. */
#transcript-title { flex: 1; min-width: 0; }
#transcript-title.editable { cursor: text; }
#transcript-title.editable:hover { color: var(--text); }
#transcript-title.editable::after {
  content: " ✎";
  font-size: 0.85em;
  opacity: 0;
  transition: opacity 0.1s;
}
#transcript-title.editable:hover::after { opacity: 0.7; }

/* Projects header is two rows so the 6 action buttons never overlap the title
   when the column narrows (same fix as the sessions header). */
#projects > header { flex-direction: column; align-items: stretch; gap: 8px; }
/* Banda de título fixa a 28px (a altura do .icon-btn) e numa só linha, para casar
   com a banda de título das Sessões — assim as duas toolbars (e as divisórias dos
   headers) alinham horizontalmente. Em toque, a @media (hover:none),(pointer:coarse)
   volta a pôr flex-wrap, deixando a pill "a trabalhar" cair para uma 2ª linha. */
.phead-title { display: flex; align-items: center; gap: 7px; flex-wrap: nowrap; min-height: 28px; }
.phead-title .brand-mark { flex: 0 0 auto; width: 22px; height: 22px; filter: drop-shadow(0 0 4px rgba(224, 123, 67, 0.45)); }
/* Keep the brand at its content width so it never gets squeezed under the live
   pill — when there's no room, the live/sound tools wrap to a second line
   together (the live label can be wide: "2 a trabalhar"). */
.phead-title h1 { flex: 1 1 auto; min-width: min-content; }
.phead-tools { display: flex; align-items: center; gap: 7px; flex: 0 0 auto; }

/* Sessions header is two rows: the (possibly long) project name on top, the
   button toolbar below — so a long name never collides with the buttons. */
#sessions > header { flex-direction: column; align-items: stretch; gap: 8px; }
.shead-title { display: flex; align-items: center; gap: 7px; min-width: 0; min-height: 28px; }
#sessions-title { flex: 1; min-width: 0; overflow: hidden; white-space: nowrap; text-overflow: ellipsis; }
#sessions-count { flex: 0 0 auto; }
/* Tool icons never shrink (so they can't overlap in a narrow column) and wrap to
   a second row instead when the pane gets too tight (below ~320px). */
.shead-tools { display: flex; align-items: center; gap: 5px; flex-wrap: wrap; row-gap: 6px; }
.shead-tools > .icon-btn, .shead-tools > .overflow-wrap { flex: 0 0 auto; }
/* Thin divider between the create/sort cluster and labels/topics/memória. */
.shead-tools .shead-gap { width: 1px; height: 16px; background: var(--border); margin: 0 2px; flex: 0 0 auto; }

/* Sessions-header overflow menu (⋯) — rare actions live here so the bar stays short. */
.overflow-wrap { position: relative; display: inline-flex; }
.overflow-menu {
  position: absolute; top: calc(100% + 6px); right: 0; z-index: 60;
  min-width: 210px; padding: 5px;
  border: 1px solid var(--border); border-radius: 9px;
  background: var(--pane); box-shadow: 0 10px 28px rgba(0, 0, 0, 0.35);
  display: flex; flex-direction: column; gap: 2px;
  /* O menu vive dentro da .pane (overflow:hidden). Sob o zoom global — ou numa
     janela baixa — o menu de projetos (alto) ultrapassava o fundo da coluna e
     era cortado, deixando os últimos itens inacessíveis. Limitar ao espaço
     abaixo do header (≈216px = header + margem) e dar scroll interno. O /zoom
     compensa a multiplicação das unidades de viewport (ver .modal). */
  max-height: calc(100dvh / var(--ui-zoom, 1) - 216px);
  overflow-y: auto;
}
.overflow-menu .icon-btn {
  width: 100%; height: auto; justify-content: flex-start;
  gap: 10px; padding: 8px 10px; border: none; background: none; border-radius: 6px;
}
.overflow-menu .icon-btn:hover { background: var(--accent-soft); color: var(--accent); }
.overflow-menu .icon-btn svg { flex: 0 0 auto; }
.overflow-menu .ov-label { font-size: 13px; white-space: nowrap; }
/* Count badge sits inline at the row's right edge inside the menu. */
.overflow-menu .todos-count {
  position: static; margin-left: auto;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 8px;
}

/* Generic visibility helper for the resume/terminal controls. */
.hidden { display: none !important; }

/* ── Collapsible left columns (desktop "focus mode") ─────────────────────────
   A « in the projects/sessions header collapses that column to a 40px rail with
   a » expand button + vertical label; state persists in localStorage. Only on
   wide screens (>960px); below that the drawer / drill-down layout governs, so
   the collapse classes are inert there (see the responsive blocks). */
.pane-rail { display: none; }
.pane-resize { display: none; }
.collapse-btn { margin-left: 2px; }
@media (min-width: 961px) {
  body[data-collapse-projects] { grid-template-columns: 40px var(--col-sessions, 320px) 1fr; }
  body[data-collapse-sessions] { grid-template-columns: var(--col-projects, 260px) 40px 1fr; }
  body[data-collapse-projects][data-collapse-sessions] { grid-template-columns: 40px 40px 1fr; }

  body[data-collapse-projects] #projects > header,
  body[data-collapse-projects] #project-search,
  body[data-collapse-projects] #project-list { display: none; }
  body[data-collapse-projects] #projects .pane-rail { display: flex; }

  body[data-collapse-sessions] #sessions > header,
  body[data-collapse-sessions] #sessions > #session-tabs,
  body[data-collapse-sessions] #session-search,
  body[data-collapse-sessions] #tag-filter-bar,
  body[data-collapse-sessions] #session-list { display: none; }
  body[data-collapse-sessions] #sessions .pane-rail { display: flex; }

  .pane-rail {
    flex-direction: column;
    align-items: center;
    gap: 8px;
    padding: 10px 0;
    height: 100%;
    /* Many mirrored action buttons can exceed a short viewport — scroll instead
       of clipping. Hide the scrollbar so the 40px rail stays clean. */
    overflow-y: auto;
    overflow-x: hidden;
    scrollbar-width: none;
  }
  .pane-rail::-webkit-scrollbar { display: none; }
  .rail-expand {
    width: 28px; height: 28px;
    border: 1px solid var(--border); border-radius: 7px;
    background: var(--pane); color: var(--muted);
    display: flex; align-items: center; justify-content: center;
    cursor: pointer; flex: none;
    transition: color 0.1s, border-color 0.1s;
  }
  .rail-expand:hover { color: var(--accent); border-color: var(--accent); }
  /* ⚡ live indicator on the rail — the only one visible when projects is collapsed. */
  .rail-live {
    position: relative;
    width: 28px; height: 28px;
    border: 1px solid var(--border); border-radius: 7px;
    background: var(--bg); color: var(--muted);
    display: flex; align-items: center; justify-content: center;
    cursor: pointer; flex: none;
    transition: color 0.1s, border-color 0.1s;
  }
  .rail-live:hover { color: var(--accent); border-color: var(--accent); }
  .rail-live.live-here { color: #3fb950; border-color: #3fb950; background: rgba(63, 185, 80, 0.14); }
  .rail-live.live-elsewhere { color: #d29922; border-color: #d29922; background: rgba(210, 153, 34, 0.14); }
  .rail-live-count {
    position: absolute; top: -6px; right: -6px;
    min-width: 15px; height: 15px; padding: 0 3px; border-radius: 8px;
    background: var(--accent); color: #fff;
    font: 700 9px/15px ui-monospace, SFMono-Regular, Menlo, monospace; text-align: center;
  }
  /* No number → no bubble (the button stays clickable without an empty dot). */
  .rail-live-count:empty { display: none; }
  .rail-live.live-here .rail-live-count { background: #3fb950; color: #10240f; }
  .rail-live.live-elsewhere .rail-live-count { background: #d29922; color: #241a05; }
  /* Sessions-rail badge buttons (📌 tópicos · 🧠 memória · 🗒 notas). */
  .rail-badge-btn {
    position: relative;
    width: 28px; height: 28px;
    border: 1px solid var(--border); border-radius: 7px;
    background: var(--bg); color: var(--muted);
    display: flex; align-items: center; justify-content: center;
    cursor: pointer; flex: none;
    transition: color 0.1s, border-color 0.1s;
  }
  .rail-badge-btn:hover { color: var(--accent); border-color: var(--accent); }
  .rail-label {
    writing-mode: vertical-rl;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 10px; letter-spacing: 0.14em; text-transform: uppercase;
    color: var(--muted); user-select: none;
  }
  /* When showing the active session/project title (not the generic label). */
  .rail-label.active {
    color: var(--accent); text-transform: none; letter-spacing: 0.03em; font-size: 11px;
    max-height: 190px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }

  .pane-resize {
    display: block;
    position: absolute;
    top: 0;
    bottom: 0;
    width: 7px;
    /* centrar sobre a fronteira: a coluna ocupa até X, o handle fica em X-3px */
    z-index: 60;
    cursor: col-resize;
    /* faixa transparente; só pinta um fio fino ao hover/focus */
    background: transparent;
    transition: background 0.1s;
  }
  .pane-resize::before {
    content: "";
    position: absolute;
    top: 0; bottom: 0; left: 3px;
    width: 1px;
    background: transparent;
  }
  .pane-resize:hover::before,
  .pane-resize.dragging::before { background: var(--accent); }
  .pane-resize:hover, .pane-resize.dragging { background: color-mix(in srgb, var(--accent) 12%, transparent); }
  .pane-resize:focus-visible { outline: none; }
  .pane-resize:focus-visible::before {
    background: var(--accent);
    box-shadow: 0 0 0 2px color-mix(in srgb, var(--accent) 45%, transparent);
  }

  /* Posição default (sem vars inline). A Fase 2 sobrepõe `left` via style. */
  .pane-resize[data-resize="projects"] { left: calc(var(--col-projects, 260px) - 3px); }
  .pane-resize[data-resize="sessions"] {
    left: calc(var(--col-projects, 260px) + var(--col-sessions, 320px) - 3px);
  }

  /* Um separador não faz sentido sobre um rail colapsado: esconder o respetivo. */
  body[data-collapse-projects] .pane-resize[data-resize="projects"] { display: none; }
  body[data-collapse-sessions] .pane-resize[data-resize="sessions"] { display: none; }
  /* Com a grelha de terminais aberta, as três colunas estão tapadas por uma
     vista de ecrã inteiro — mas os handles são `position:absolute` com
     `z-index: 60` contra os 50 da `.grid-view`, e apareciam POR CIMA dos
     painéis (relatado com screenshot: um fio laranja a atravessar um
     terminal). Subir o z-index da vista arrumava a aparência e deixava o
     defeito: um controlo que redimensiona colunas invisíveis continuaria
     arrastável, a mexer em larguras que ninguém vê. Esconder é o mesmo remédio
     que as duas regras acima já aplicam à coluna colapsada.
     Nota para quem acrescentar outra vista de ecrã inteiro: a `.an-view`
     (analytics) escapa a isto por acidente, não por desenho — tem `z-index:
     60`, igual aos handles, e só ganha o empate por vir depois deles no DOM. */
  body[data-grid-open] .pane-resize { display: none; }

  body.resizing { cursor: col-resize; }
  body.resizing * { user-select: none !important; }
}
.rail-live[hidden], .rail-badge-btn[hidden] { display: none; }

/* Breadcrumb (project name) above the transcript title — surfaces the project
   identity that's lost when the projects column is collapsed (focus mode). */
.transcript-project { display: none; }
@media (min-width: 961px) {
  body[data-collapse-projects] #transcript-project:not(:empty) {
    display: block;
    font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
    font-size: 10px; letter-spacing: 0.04em; text-transform: uppercase;
    color: var(--accent); margin-bottom: 2px;
  }
  body[data-collapse-projects] #transcript-project:not(:empty)::after {
    content: " ›"; opacity: 0.55;
  }
}

/* ── Project groups (collapsible, user-defined sections in the projects column) ── */
.group-head {
  display: flex; align-items: center; gap: 7px;
  padding: 8px 8px 8px 10px;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 9.5px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.1em;
  color: var(--muted); cursor: pointer; user-select: none; border-radius: 7px;
  background: color-mix(in srgb, var(--gc, var(--accent)) 13%, transparent);
}
.group-head:hover { background: color-mix(in srgb, var(--gc, var(--accent)) 20%, transparent); }
.group-head .gchev { font-size: 9px; width: 10px; flex: none; transition: transform 0.12s; color: var(--gc, var(--muted)); }
.group-head.collapsed .gchev { transform: rotate(-90deg); }
.group-head .gname { color: var(--gc, var(--text)); font-size: 10.5px; font-weight: 700; letter-spacing: 0.06em; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.group-head .gcount { font-family: inherit; font-size: 10px; font-weight: 700; background: var(--accent-soft); border: none; border-radius: 6px; padding: 2px 7px; min-width: 18px; text-align: center; color: var(--accent-strong); flex: none; }
.group-head .gmore { margin-left: auto; opacity: 0; color: var(--gc, var(--muted)); font-size: 14px; line-height: 1; padding: 0 4px; flex: none; background: none; border: none; cursor: pointer; }
.group-head:hover .gmore,
.group-head:focus-within .gmore { opacity: 0.65; }
.group-head .gmore:hover { opacity: 1; color: var(--accent); }
/* Dark theme: the raw group colour is too dim on a near-black pane, so lighten
   the text toward white (keeps the hue) and slightly strengthen the fill. */
:root[data-theme="dark"] .group-head { background: color-mix(in srgb, var(--gc, var(--accent)) 22%, transparent); }
:root[data-theme="dark"] .group-head:hover { background: color-mix(in srgb, var(--gc, var(--accent)) 30%, transparent); }
:root[data-theme="dark"] .group-head .gname,
:root[data-theme="dark"] .group-head .gchev,
:root[data-theme="dark"] .group-head .gmore { color: color-mix(in srgb, var(--gc, var(--accent)) 55%, #ffffff); }
.group-head.drop-target { background: var(--accent-soft); outline: 1.5px dashed var(--accent); outline-offset: -1px; }
.proj-sect.drop-target { background: var(--accent-soft); outline: 1.5px dashed var(--accent); outline-offset: -1px; border-radius: 7px; }
.project-list-item.in-group { margin-left: 10px; }
.project-list-item.dragging { opacity: 0.45; }
/* Move-to-group row action (revealed on hover, left of the favorite star). */
.proj-move {
  position: absolute; top: 8px; right: 38px;
  width: 22px; height: 22px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--pane); color: var(--muted);
  display: none; align-items: center; justify-content: center;
  cursor: pointer; padding: 0;
}
.proj-move svg { display: block; }
li[data-id]:hover .proj-move,
li[data-id]:focus-visible .proj-move,
li[data-id]:has(:focus-visible) .proj-move { display: flex; }
.proj-move:hover { color: var(--accent); border-color: var(--accent); }

/* Copy-location button. Vive dentro do menu ⋯ (onde o .proj-menu-item abaixo
   anula esta posição absoluta); a posição só chega a valer nos projetos VAZIOS,
   que não têm menu e o mantêm na linha. */
.proj-copy {
  position: absolute; top: 8px; right: 66px;
  width: 22px; height: 22px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--pane); color: var(--muted);
  display: none; align-items: center; justify-content: center;
  cursor: pointer; padding: 0;
}
.proj-copy svg { display: block; }
li[data-id]:hover .proj-copy,
li[data-id]:focus-visible .proj-copy,
li[data-id]:has(:focus-visible) .proj-copy { display: flex; }
.proj-copy:hover { color: var(--accent); border-color: var(--accent); }
.proj-copy.ok { color: var(--accent); border-color: var(--accent); }

/* Indexar-projeto — mesmo afordância dos anteriores, mais um slot à esquerda.
   Só existe no markup quando há algo por indexar (projectIndexButtonHtml), por
   isso a sua ausência é ela própria informação. O ESTADO está sempre visível no
   ponto do subtítulo; aqui está só a ação, como nos irmãos. */
.proj-index {
  position: absolute; top: 8px; right: 94px;
  width: 22px; height: 22px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--pane); color: var(--amber);
  display: none; align-items: center; justify-content: center;
  cursor: pointer; padding: 0;
}
.proj-index svg { display: block; }
li[data-id]:hover .proj-index,
li[data-id]:focus-visible .proj-index,
li[data-id]:has(:focus-visible) .proj-index { display: flex; }
.proj-index:hover { color: var(--accent); border-color: var(--accent); }
.proj-index[disabled] { opacity: .45; cursor: not-allowed; }

/* Archive/unarchive — já não tem slot próprio na linha: mora dentro do menu ⋯,
   onde o layout vem todo do .proj-menu-item. Sobra só o estado: um projeto
   arquivado marca o item (é o que distingue "arquivar" de "desarquivar" a par
   do ícone).
   MERGE: origin/main afinava aqui o slot absoluto do .proj-arch (right:94px, a
   recuar para 122px quando havia um .proj-index irmão) para fechar um gap morto
   na linha. Essa correcção fica sem objecto — o botão saiu da linha, e manter as
   regras dela poria um `position:absolute` a lutar com o `position:static` que o
   .proj-menu-item usa para o desenhar como item de lista. */
.proj-arch.on { color: var(--accent); }

/* ── Menu ⋯ da linha de projeto ─────────────────────────────────────────────
   A linha tinha cinco botões e a coluna tem 260px: um sexto partia-a em duas
   (ver docs/claude/ui-conventions.md). As duas ações menos frequentes —
   arquivar e localização — desceram um nível, para aqui, ao lado de "mudar a
   pasta". O wrapper é `display: contents` de propósito: assim tanto o botão
   como o menu se posicionam contra o próprio <li>, e o menu pode ocupar a
   largura toda da linha em vez de ficar pendurado no slot do botão (com
   min-width fixo transbordava pela esquerda e a .list, que tem overflow, cortava-o). */
.proj-more-wrap { display: contents; }
.proj-more {
  position: absolute; top: 8px; right: 66px;
  width: 22px; height: 22px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--pane); color: var(--muted);
  display: none; align-items: center; justify-content: center;
  cursor: pointer; padding: 0;
}
.proj-more svg { display: block; }
/* Obrigatória: a regra acima dá `display` de autor e ganha ao `[hidden]` da
   folha do browser — sem isto, marcar o botão como hidden não o esconderia.
   Ver docs/claude/ui-conventions.md. */
.proj-more[hidden] { display: none; }
li[data-id]:hover .proj-more,
li[data-id]:focus-visible .proj-more,
li[data-id]:has(:focus-visible) .proj-more,
/* Com o menu aberto o ⋯ fica visível mesmo sem o rato em cima: senão, afastar
   o rato apagava o botão e deixava o menu a flutuar sem âncora. */
li[data-id]:has(.proj-menu:not([hidden])) .proj-more { display: flex; }
.proj-more:hover { color: var(--accent); border-color: var(--accent); }

.proj-menu {
  position: absolute; top: 34px; left: 8px; right: 8px; z-index: 30;
  padding: 4px;
  border: 1px solid var(--border); border-radius: 10px;
  background: var(--pane);
  box-shadow: 0 10px 28px rgb(0 0 0 / 0.28);
  display: flex; flex-direction: column; gap: 2px;
}
.proj-menu[hidden] { display: none; }
.proj-menu-item {
  /* `position: static` e `height: auto` anulam o slot absoluto que .proj-arch /
     .proj-copy têm enquanto botões de linha — dentro do menu são linhas de lista.
     `justify-content: flex-start` anula a TERCEIRA herança do mesmo sítio, e a
     única que se via: o `.proj-copy` de linha centra o seu conteúdo (é um quadrado
     de 22px com um ícone só), e sem este reset o item "Copiar localização" ficava
     com o ícone 11px à direita dos irmãos — três itens, dois alinhamentos. Só o
     .proj-copy sofria disto porque o bloco de botão-de-linha do .proj-arch já não
     existe (mora só no menu). Apanhado a renderizar, não a ler o CSS. */
  position: static; height: auto; width: 100%;
  display: flex; align-items: center; justify-content: flex-start; gap: 8px;
  padding: 7px 9px;
  border: none; border-radius: 7px;
  background: none; color: var(--text);
  font: inherit; font-size: 12.5px; line-height: 1.3;
  text-align: left; cursor: pointer;
}
.proj-menu-item svg { flex: none; color: var(--muted); }
.proj-menu-item:hover { background: var(--accent-soft); color: var(--accent-strong); }
.proj-menu-item:hover svg { color: var(--accent); }
.proj-menu-item.on svg { color: var(--accent); }

/* Move-to-group picker (inside the modal). */
.grp-picker { display: flex; flex-direction: column; gap: 8px; }
.grp-opt {
  display: flex; align-items: center; gap: 9px;
  padding: 9px 11px; border: 1px solid var(--border); border-radius: 9px;
  font-size: 13px; cursor: pointer;
}
.grp-opt:hover { border-color: var(--accent); }
.grp-opt.on { border-color: var(--accent); background: var(--accent-soft); }
.grp-opt input { accent-color: var(--accent); margin: 0; }
.grp-opt .gdot { width: 10px; height: 10px; border-radius: 3px; flex: none; }
.grp-opt.grp-new { color: var(--accent); border-style: dashed; font-weight: 600; }
.grp-opt.grp-new .gplus { display: inline-flex; }
.grp-opt.grp-new .gplus svg { width: 14px; height: 14px; }
/* Group colour swatches (manage modal). */
.grp-swatches { display: flex; flex-wrap: wrap; gap: 7px; }
.grp-swatch {
  width: 24px; height: 24px; border-radius: 7px; cursor: pointer;
  border: 2px solid transparent; padding: 0;
}
.grp-swatch.on { border-color: var(--text); box-shadow: 0 0 0 2px var(--pane) inset; }
.grp-delete {
  display: inline-flex; align-items: center; gap: 7px; justify-content: center;
  margin-top: 6px; padding: 8px 12px; border-radius: 8px;
}

/* Transcript header tem duas bandas (como #projects/#sessions): título em cima,
   toolbar (view-toggle + pesquisa/ficheiros/copiar) por baixo — assim os títulos,
   as toolbars e a divisória alinham horizontalmente com as colunas de lista.
   Bandas fixas a 28px (altura do .icon-btn). Em mobile a header já quebrava; aqui
   passa a ser sempre em coluna. */
#transcript > header { flex-direction: column; align-items: stretch; gap: 8px; }
.transcript-head-text {
  min-width: 0;
  min-height: 28px;
  display: flex;
  align-items: center;
  gap: 8px;
  overflow: hidden;
}
.transcript-head-text #transcript-meta { white-space: nowrap; }
/* Banda de ferramentas da conversa (mesma métrica de .shead-tools). Alinhada à
   direita para os botões ficarem por baixo do custo/cache (que o #transcript-meta
   mostra à direita da banda de título). */
.thead-tools { display: flex; justify-content: flex-end; align-items: center; gap: 6px; min-height: 28px; flex-wrap: wrap; row-gap: 6px; }
.thead-tools > .icon-btn, .thead-tools > #transcript-view-toggle { flex: 0 0 auto; }

/* Segmented Transcript|Terminal control (mirrors #session-tabs). */
/* Transcript-header controls are now uniform icon buttons (.icon-btn). The view
   toggle is two icon tabs; the active one gets the accent fill. */
.view-toggle { display: flex; gap: 4px; flex: none; }
.view-toggle .view-tab {
  width: 28px; height: 28px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: 1px solid var(--border); background: var(--bg);
  color: var(--muted); border-radius: 6px; cursor: pointer;
}
.view-toggle .view-tab svg { display: block; }
.view-toggle .view-tab:hover { border-color: var(--accent); color: var(--accent); }
.view-toggle .view-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }

.resume-btn { flex: none; }
/* Os DOIS botões do filtro (vista normal e grelha) comutam o mesmo estado, logo
   têm de o mostrar da mesma maneira — sem isto o da grelha ficava sempre com o
   aspeto de desligado. */
#tools-filter-btn.active,
#grid-tools-filter-btn.active { background: var(--accent); border-color: var(--accent); color: #fff; }

/* Grupo segmentado do header do transcript: [Transcrição · ferramentas ·
   Terminal/Continuar · Resumo]. A pill dá a moldura exterior (borda + cantos);
   cada célula perde a sua borda/radius e cola-se às vizinhas.
   Divisórias sem seletores frágeis: o container tem gap:1px sobre um fundo
   cor-de-borda, e cada célula tapa esse fundo — a linha de 1px só assoma nos
   gaps entre células REALMENTE visíveis (uma célula .hidden não ocupa espaço,
   logo não gera gap). Robusto a qualquer membro que apareça/desapareça. */
.seg-group {
  display: inline-flex; align-items: center; flex: none;
  gap: 1px; background: var(--border);
  border: 1px solid var(--border); border-radius: 8px; overflow: hidden;
}
.seg-group > * { background: var(--bg); }
.seg-group #tools-filter-btn,
.seg-group #resume-btn,
.seg-group .view-tab {
  /* height 26 + a borda exterior de 1px da pill (×2) = 28px, a mesma altura dos
     icon-btns ao lado na banda de ferramentas — para a divisória alinhar. */
  border: none; border-radius: 0; width: 30px; height: 26px;
}
/* Ênfase: o filtro de ferramentas pertence à vista Transcrição — cola-se a ela
   comendo a divisória do lado esquerdo (margin negativo tapa o gap de 1px),
   mantendo a divisória à direita que o separa das outras vistas. */
.seg-group #tools-filter-btn { margin-left: -1px; }
/* Estado ativo dentro da pill (declarado depois do reset e com especificidade
   ≥ → vence): mantém o fill accent do filtro/tab selecionada. */
.seg-group #tools-filter-btn.active,
.seg-group .view-tab.active { background: var(--accent); color: #fff; }
/* A pill tem borda própria de 1px (+2px sobre as células de 26px). No desktop
   fixamo-la exatamente a 28px (box-sizing:border-box → conteúdo 26px, as células
   encaixam) para a banda 2 e a divisória alinharem ao pixel com a coluna de
   sessões, sem depender do arredondamento da borda. Em toque não se aplica: as
   células crescem para 44px pela media (hover:none),(pointer:coarse). */
@media (hover: hover) and (pointer: fine) {
  .thead-tools > #transcript-view-toggle { height: 28px; }
}
/* Sem borda própria, o realce de hover é por fundo (não por border-color). */
.seg-group .icon-btn:not(.active):hover,
.seg-group .view-tab:not(.active):hover { background: var(--accent-soft); color: var(--accent); }

/* Danger icon button (terminate session). */
.icon-btn.danger { color: var(--danger); border-color: #f0c2bc; }
.icon-btn.danger:hover { background: var(--danger-soft); border-color: var(--danger); color: var(--danger); }

/* Esconder as chamadas de ferramentas nas DUAS superfícies que renderizam
   mensagens. A regra estava só no #transcript-body, por isso o filtro não
   alcançava a transcrição dos painéis da grelha; a classe da grelha vive no
   container (#grid-view-body) para os painéis criados depois a herdarem. */
#transcript-body.hide-tools .msg.tool,
#grid-view-body.hide-tools .msg.tool { display: none; }

/* Live terminal fills the pane under the header. */
#terminal-view {
  flex: 1;
  min-height: 0;
  display: flex;
  flex-direction: column;
  /* Âncora do #term-zoom-hud, que é absoluto e se centra sobre o terminal. */
  position: relative;
}
#terminal-toolbar {
  display: flex;
  align-items: center;
  /* flex-end, como a .thead-tools. Na linha cheia é indiferente — o
     #terminal-toolbar-left tem `flex:1` e come o espaço livre todo, por isso não
     sobra nada para distribuir — mas numa linha QUEBRADA o `space-between`
     anterior atirava os botões para as duas margens opostas em vez de os manter
     juntos à direita. */
  justify-content: flex-end;
  gap: 8px;
  /* Era a ÚNICA barra de ferramentas da app sem quebra de linha (a .thead-tools
     traz `flex-wrap` de origem, e o bloco táctil dá-o à .header-actions /
     .shead-tools / .phead-title). Sem ela a linha nunca partia: o
     #terminal-toolbar-left encolhia até 70px, o estado da sessão ficava a 0px e
     o chip "⚠ flags originais" — que é `flex:none` e não encolhe — passava a
     pintar por cima do primeiro botão. De base, e não só no bloco táctil,
     porque a condição é "o painel ficou estreito", não "é um telemóvel": no
     desktop o mesmo acontece com as duas colunas de lista abertas, onde
     `pointer: coarse` nunca dá verdade. */
  flex-wrap: wrap;
  row-gap: 6px;
  /* Como o #term-composer e a .term-keybar, que são as outras bandas fixas
     desta coluna. Sem isto a barra é um item encolhível de um flex column e,
     assim que quebra para duas linhas, o pai comprime-a de volta aos 49px do
     min-height e a segunda linha de botões pinta POR CIMA do terminal. */
  flex: none;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border);
  /* Iguala a altura à 2ª banda da coluna de sessões (#session-search): o input
     de pesquisa mede 32px vs os botões de 28px daqui, logo esta barra ficava 4px
     mais baixa. 32 (input) + 16 (padding 8×2) + 1 (borda) = 49px. Em toque os
     controlos crescem para 44px e a altura natural (>49) ganha, mantendo o par. */
  min-height: 49px;
}
/* Mobile-only composer (shown in the touch media query below). */
#term-composer { display: none; gap: 8px; padding: 8px; border-top: 1px solid var(--border); background: var(--pane); flex: none; }
#term-composer-input {
  flex: 1; min-width: 0; font: inherit; font-size: 16px; padding: 9px 12px;
  border: 1px solid var(--border); border-radius: 8px; background: var(--bg); color: var(--text);
}
#term-composer-input:focus { outline: none; border-color: var(--accent); }
#term-composer-send { flex: none; }
/* flex:1 so this region eats the free space and the action buttons cluster to
   the RIGHT. */
/* Sem `min-width: 0`, de propósito: o `min-width: auto` implícito faz esta
   região parar de encolher no seu conteúdo mínimo, que é o que dá à quebra de
   linha da barra alguma coisa para quebrar. Com `min-width: 0` a região colapsa
   primeiro e o `flex-wrap` acima nunca chegava a disparar. A reticência do
   estado continua a funcionar: quem a permite é o `min-width: 0` da .tstatus
   (que colapsa a 0 sozinha), não este. */
#terminal-toolbar-left { display: flex; align-items: center; gap: 10px; flex: 1; }
/* UM item da barra, não quatro. A barra quebra linha (ver #terminal-toolbar), e
   a quebra dá-se ENTRE itens: com os botões soltos, o ponto de quebra caía no
   meio do cacho. Medido em Chromium a 1600px de janela, com o painel direito
   entre 300 e 380px e a barra de flags cheia (skip-perms · modelo · telegram):
   a 300px ficava só o ⧉ na 1.ª linha, a 360-380px o botão VERMELHO de terminar
   sessão ficava sozinho na 2.ª. Agrupados, a barra tem dois itens e a única
   quebra possível é a que se quer — estado em cima, acções em baixo, juntas.
   `flex: none` porque o grupo nunca deve encolher: os alvos de toque têm
   tamanho mínimo e comprimi-los é a outra forma de partir a barra. */
#terminal-toolbar-actions { display: flex; align-items: center; gap: 8px; flex: none; }
.tstatus { display: inline-flex; align-items: center; gap: 7px; font-size: 12px; min-width: 0; }
.tstatus #terminal-status { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tdot { width: 8px; height: 8px; border-radius: 50%; flex: none; background: var(--muted); }
.tdot.on { background: #34c759; }
.tdot.warn { background: #c98a1d; }
.tdot.off { background: var(--muted); }
/* reattach warning: the live process kept its original flags (see ↻ relaunch). */
.reattach-chip {
  display: inline-flex; align-items: center; gap: 4px; flex: none;
  font-size: 10.5px; font-weight: 600;
  color: #9a6a12; background: var(--accent-soft);
  border: 1px solid #e8d3a6; border-radius: 999px; padding: 1px 8px;
  cursor: help;
}
:root[data-theme="dark"] .reattach-chip { color: #e0a93c; border-color: #5a4423; }

/* ── Launch-flag chips (read-only) ────────────────────────────────────────── */
/* Header bar = preview of what "Continuar" will run; terminal bar = the real
   flags of the live run. Both render the same .lf-chip set. */
.launch-flags {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 6px 16px; border-bottom: 1px solid var(--border);
  background: rgba(0, 0, 0, .015);
}
:root[data-theme="dark"] .launch-flags { background: rgba(255, 255, 255, .015); }
.launch-flags.hidden, .term-flags.hidden { display: none; }
.term-flags { display: inline-flex; align-items: center; gap: 6px; flex-wrap: wrap; min-width: 0; }
.lf-label { font-size: 11px; color: var(--muted); margin-right: 1px; }
.lf-chip {
  display: inline-flex; align-items: center; gap: 5px; flex: none;
  font-size: 11px; line-height: 1.4; white-space: nowrap;
  padding: 2px 8px; border-radius: 999px;
  border: 1px solid var(--border); color: var(--text); background: var(--bg);
}
.lf-chip .lf-dot { width: 6px; height: 6px; border-radius: 50%; background: var(--muted); flex: none; }
.lf-chip.on { border-color: #5aa469; background: rgba(90, 164, 105, .12); color: #3c7a4a; }
.lf-chip.on .lf-dot { background: #5aa469; }
:root[data-theme="dark"] .lf-chip.on { color: #bfe3c6; border-color: #3a5a40; }
.lf-chip.model { border-color: var(--accent); background: var(--accent-soft); color: var(--accent-strong); }
.lf-chip.extra { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 10.5px; color: var(--muted); }
.lf-gear { width: 24px; height: 24px; padding: 0; margin-left: auto; flex: none; }
.term-flags .lf-chip { font-size: 10.5px; padding: 1px 7px; }
/* ── Terminal helper bar (the ⌨ toggle group) ─────────────────────────────── */
/* Hidden until the ⌨ toggle (#nav-keys, in #msg-nav) opens it — the SAME
   mechanism on desktop AND touch. Open reveals the tmux nav keys (Esc/Tab/Ctrl/
   arrows). The text composer is mobile-only (desktop types straight into xterm)
   via its own touch-gated rule below, and rides the same toggle. The copy button
   is NOT here — it's a standalone button in the #msg-nav floating column. */
.term-keybar { display: none; }
.term-keybar.open {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 6px 8px;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  border-bottom: 1px solid var(--border);
  background: var(--pane);
  flex: none;
}
.term-keybar::-webkit-scrollbar { display: none; }
.term-keybar.open .kb {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: none;
  min-width: 36px;
  min-height: 32px;
  padding: 0 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--fg);
  font-size: 14px;
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  -webkit-user-select: none;
}
.term-keybar.open .kb:hover { border-color: var(--accent); color: var(--accent); }
.term-keybar.open .kb:active { background: var(--accent-soft); }
.term-keybar.open .kb.kb-mod.active {
  background: var(--accent);
  color: var(--bg);
  border-color: var(--accent);
}
/* The ⌨ toggle (virtual Esc/Tab/Ctrl/arrows) is TOUCH-ONLY — a physical keyboard
   already has those keys, so the desktop media query further down hides it. It
   sits at the BOTTOM of the column (bottom-anchored → a higher flex order pins ⌨
   nearest the screen edge); copy + arrows expand ABOVE it. */
.nav-keys { display: none; order: 1; }
.msg-nav.over-terminal .nav-keys { display: flex; }
.nav-keys.active { background: var(--accent); color: var(--bg); border-color: var(--accent); }
/* The copy button lives in the same floating #msg-nav column (next to ⌨), NOT in
   the keybar. Like ⌨, it only makes sense over the terminal, so it's shown there
   on BOTH desktop and touch (and stays out over a transcript). */
.nav-copy { display: none; }
.msg-nav.over-terminal .nav-copy { display: flex; }
.nav-copy.ok { color: var(--accent); border-color: var(--accent); }
/* The ⌨ toggle gates EVERY other button on BOTH desktop and touch: with the dock
   closed only ⌨ shows; clicking ⌨ (.dock-open) reveals the copy button + scroll
   arrows (and the keybar). Over a transcript (no .over-terminal) the arrows stay
   as before. */
.msg-nav.over-terminal:not(.dock-open) .msg-nav-btn:not(#nav-keys) { display: none; }
/* Fuga a R2 — a regra acima também apanharia o semáforo. Especificidade igual
   (1 id + 4 classes de cada lado, por causa do :not(#nav-keys)/:not([hidden])),
   por isso esta regra TEM de vir depois na cascata para desempatar a favor do
   ponto — não basta escrevê-la "junto" das outras regras do semáforo. O
   `[hidden]` é o que mantém verde calado: com o veredicto `apto` o app.js marca
   o botão hidden e ele obedece a R2 como qualquer outro. Ver
   docs/claude/system-check.md. */
.msg-nav.over-terminal:not(.dock-open) #nav-health:not([hidden]) { display: flex; }
/* Em desktop o ⌨ não faz sentido (teclado físico), por isso esconde-se sobre o
   terminal. Mas escondê-lo deixaria o R2 acima sem quem abrisse o dock, tapando
   também o copiar — daí o copiar passar a mostrar-se DIRETAMENTE sobre o terminal
   (as setas ficam escondidas: são para o transcript, não para o terminal ao vivo).
   Em toque nada disto se aplica: o ⌨ mantém-se e o dock revela teclas+copiar+setas. */
@media (hover: hover) and (pointer: fine) {
  .msg-nav.over-terminal .nav-keys { display: none; }
  .msg-nav.over-terminal:not(.dock-open) .msg-nav-btn#term-copy-btn { display: flex; }
}
@media (hover: none), (pointer: coarse) {
  /* Touch keys are bigger (44px min) for fat-finger targets. */
  .term-keybar.open .kb { min-width: 44px; min-height: 44px; }
}
/* Indicador transitório do tamanho, no meio do terminal. `pointer-events:none`
   para nunca comer um toque destinado ao terminal por baixo. */
.term-zoom-hud {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  z-index: 4;
  padding: 6px 13px;
  border-radius: 9px;
  background: color-mix(in srgb, var(--code-bg) 94%, transparent);
  border: 1px solid var(--border);
  color: var(--text);
  font-size: 12.5px;
  font-variant-numeric: tabular-nums;
  pointer-events: none;
  opacity: 1;
  transition: opacity .25s ease;
}
.term-zoom-hud[hidden] { display: none; }
.term-zoom-hud.fading { opacity: 0; }

#terminal-host {
  flex: 1;
  min-height: 0;
  /* Trava o pinch-zoom NATIVO do browser só nesta zona, para o gesto de dois
     dedos poder redimensionar a letra do terminal em vez de escalar a página
     (que partiria o cálculo de cols/rows do FitAddon). `pan-y` preserva o
     scroll vertical, que é o outro gesto que aqui faz falta. */
  touch-action: pan-y;
  padding: 6px 4px 6px 8px;
  /* Segue o preset de cores das colunas (o xterm recebe o MESMO valor via
     terminalBgColor() em app.js — mantê-los alinhados evita uma moldura). */
  background: var(--pane-transcript, #1a1917);
  overflow: hidden;
}
/* xterm fills its host. */
#terminal-host .xterm,
#terminal-host .xterm-viewport { height: 100% !important; }
/* xterm v6 (DOM renderer) não pinta theme.background no viewport — o #000 do
   xterm.css tapava o preset de colunas. Transparente deixa o fundo do host
   (var(--pane-transcript)) aparecer; as células do TUI pintam as suas cores. */
#terminal-host .xterm-viewport { background-color: transparent !important; }

/* ── Detach the terminal into a floating window ───────────────────────────── */
/* Placeholder shown in the pane while the terminal is floating/PiP. */
#terminal-detached {
  flex: 1;
  min-height: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
  background:
    repeating-linear-gradient(45deg, var(--pane), var(--pane) 12px, var(--bg) 12px, var(--bg) 24px);
}
#terminal-detached .td-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 12px;
  color: var(--muted);
  text-align: center;
}
#terminal-detached p { margin: 0; font-size: 13px; }

/* In-page floating shell (fallback A). position:fixed so it overlays the layout;
   moved geometry is persisted in localStorage. */
#terminal-float {
  position: fixed;
  z-index: 60;
  width: 720px;
  height: 460px;
  top: 90px;
  right: 28px;
  display: flex;
  flex-direction: column;
  background: var(--term-bg, #1a1917);
  border: 1px solid #000;
  border-radius: 10px;
  box-shadow: 0 16px 44px rgba(0, 0, 0, 0.42);
  overflow: hidden;
}
#terminal-float-head {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 7px 10px;
  background: #2a2823;
  color: #cfc8bb;
  font-size: 12px;
  cursor: move;
  user-select: none;
  border-bottom: 1px solid #000;
}
#terminal-float-head .tf-grip { color: #6b655c; }
#terminal-float-head .tf-title {
  flex: 1;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}
#terminal-float-head .tf-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 22px;
  border: 1px solid #4a463f;
  border-radius: 5px;
  background: transparent;
  color: #cfc8bb;
  cursor: pointer;
}
#terminal-float-head .tf-btn:hover { border-color: var(--accent); color: var(--accent); }
#terminal-float-head .tf-btn.danger { color: #e2a09a; border-color: #5a2a28; }
#terminal-float-head .tf-btn.danger:hover { color: var(--danger); border-color: var(--danger); background: #3a1f1d; }
#terminal-float-head .tf-status { font-size: 11px; color: var(--muted); margin-left: auto; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 130px; }
#terminal-float-body { flex: 1; min-height: 0; display: flex; flex-direction: column; }
/* the moved per-terminal host fills the float/pip body */
#terminal-float-body > .term-host { flex: 1; min-height: 0; }
/* A terminal's own xterm host — the unit moved between pane ⇄ float on detach. */
.term-host { height: 100%; width: 100%; min-height: 0; }
.term-host .xterm, .term-host .xterm-viewport { height: 100% !important; }

/* ── Session summary view (heuristic timeline) ─────────────────────────────── */
#summary-view { flex: 1; min-height: 0; overflow: auto; background: var(--bg); }
.sum-wrap { padding: 14px 18px; width: 100%; }
.sum-head { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.sum-title { font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.1em; text-transform: uppercase; color: var(--muted); flex: 1; }
.sum-stats { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.sum-chip { font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace; padding: 5px 8px; border-radius: 7px; background: var(--pane); border: 1px solid var(--border); color: var(--muted); }
.sum-chip b { color: var(--text); }
.sum-chip.err { color: #ef7b72; border-color: #5a2a28; }
.sum-chip.cost { color: var(--accent); border-color: #5a3b27; }
/* timeline */
.sum-tl { display: flex; flex-direction: column; }
.sum-ti { position: relative; padding: 9px 8px 9px 22px; border-radius: 8px; cursor: pointer; }
.sum-ti:hover { background: var(--pane); }
.sum-ti::before { content: ""; position: absolute; left: 6px; top: 14px; width: 8px; height: 8px; border-radius: 50%; background: var(--user-accent); box-shadow: 0 0 0 3px rgba(116, 167, 255, 0.15); }
.sum-ti::after { content: ""; position: absolute; left: 9.5px; top: 22px; bottom: -9px; width: 1px; background: var(--border); }
.sum-ti:last-child::after { display: none; }
.sum-ti .sum-t { font: 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--muted); }
.sum-ti .sum-q { font-size: 13px; color: var(--text); margin: 2px 0 4px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.sum-ti .sum-bdg { display: flex; gap: 8px; font: 600 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--muted); }
.sum-ti .sum-bdg .e { color: #ef7b72; }
.sum-ins { margin-top: 14px; border-top: 1px solid var(--border); padding-top: 12px; }
.sum-ins-h { font: 600 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace; letter-spacing: 0.08em; text-transform: uppercase; color: #5fc27e; margin-bottom: 7px; }
.sum-ins ul { margin: 0; padding-left: 18px; font-size: 13px; color: var(--text); }
.sum-ins li { margin-bottom: 4px; }
/* brief highlight when jumping to a turn from the timeline */
.msg.flash { animation: msgflash 1.2s ease-out; }
@keyframes msgflash {
  0%, 30% { background: var(--accent-soft); box-shadow: 0 0 0 2px var(--accent); border-radius: 8px; }
  100% { background: transparent; box-shadow: none; }
}
#terminal-float-resize {
  position: absolute;
  right: 0;
  bottom: 0;
  width: 16px;
  height: 16px;
  cursor: nwse-resize;
  background:
    linear-gradient(135deg, transparent 50%, #6b655c 50%, #6b655c 60%, transparent 60%, transparent 70%, #6b655c 70%, #6b655c 80%, transparent 80%);
}

/* Document PiP window body (styles are copied into the PiP document). */
.pip-body { margin: 0; height: 100vh; display: flex; flex-direction: column; background: #1a1917; }
.pip-body > .term-host { flex: 1; min-height: 0; }

@media (max-width: 960px) {
  #detach-btn { display: none !important; } /* floating only makes sense on desktop */
  #terminal-grid-btn { display: none !important; } /* grelha de 4 terminais só faz sentido em desktop */
  /* Copy-path / open-in-editor is a desktop-only affordance (see touch block). */
  li[data-id] .proj-copy { display: none !important; }
  /* Colapsar colunas só existe em ≥961px (o grid de 3 colunas); em mobile as
     colunas são vistas de ecrã inteiro, por isso os botões não têm efeito. */
  .collapse-btn { display: none !important; }
}

.list {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  flex: 1;
}
.list li {
  position: relative;
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.1s;
}
.list li:hover { background: var(--row-active, var(--accent-soft)); }
.list li.active {
  background: var(--row-active, var(--accent-soft));
  border-left: 3px solid var(--accent);
  padding-left: 13px;
}
/* Collapse + fade a row out in place (archive/delete) so the list closes the
   gap smoothly without a full re-render — keeps the scroll position. */
.list li.removing {
  overflow: hidden;
  opacity: 0;
  transform: translateX(-16px);
  pointer-events: none;
  padding-top: 0;
  padding-bottom: 0;
  border-bottom-width: 0;
  transition:
    max-height 0.28s ease,
    opacity 0.18s ease,
    transform 0.18s ease,
    padding 0.28s ease,
    border-bottom-width 0.28s ease;
}
.list .item-title {
  font-weight: 500;
  word-break: break-word;
  margin-bottom: 4px;
}
.proj-aliased {
  display: inline-block;
  width: 5px;
  height: 5px;
  margin-left: 6px;
  border-radius: 50%;
  background: var(--accent, #7a8cff);
  vertical-align: middle;
  opacity: 0.75;
}
.proj-rename-input {
  width: 100%;
  font: inherit;
  color: var(--text, inherit);
  background: var(--input-surface, var(--bg));
  border: 1px solid var(--accent, #7a8cff);
  border-radius: 4px;
  padding: 1px 5px;
  box-sizing: border-box;
}
.list .item-sub {
  font-size: 11px;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 8px;
}
/* D12: cobertura de indexação semântica por projeto — ponto de ESTADO na linha
   do projeto (coluna esquerda). É só um indicador: a ação de indexar vive no
   cluster de botões da linha (.proj-index), como as outras ações da coluna.
   Nomes de variáveis mapeados para os tokens reais deste ficheiro (não há
   --border-2/--text-2/--text-3/--warn aqui, ver nota em .gsearch-mode acima):
   --border, --muted e --amber cobrem os mesmos papéis. */
.sem-cov {
  font-size: 10px; color: var(--muted);
  display: inline-flex; align-items: center; gap: 3px;
}
.sem-cov svg { display: block; }
.sem-cov-done { color: var(--accent); }
.sem-cov-todo, .sem-cov-part { color: var(--amber); }
/* Um projeto sem nada a indexar não deve chamar a atenção: o ponto fica ao
   nível do resto dos metadados em vez de acender a cor de aviso. */
.sem-cov-none { opacity: .5; }
.sem-cov-frac { font-variant-numeric: tabular-nums; }
/* Session-row meta (Proposta C): line 2 = message counts + cost (right-aligned),
   line 3 = date/time on its own. */
.list .item-meta {
  font-size: 11px;
  color: var(--muted);
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 3px;
}
.list .counts {
  display: inline-flex;
  gap: 10px;
  align-items: center;
  font-variant-numeric: tabular-nums;
}
.list .cnt {
  display: inline-flex;
  align-items: center;
  gap: 2px;
  font-weight: 600;
}
.list .cnt .ar { font-size: 10px; line-height: 1; }
.list .cnt.up .ar { color: var(--user-accent); }
.list .cnt.down .ar { color: var(--accent); }
.list .item-meta .cost { margin-left: auto; } /* push the value to the right edge */
.list .item-date { font-size: 10.5px; color: var(--muted); }
.source-tag {
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  padding: 1px 5px;
  border-radius: 4px;
  border: 1px solid var(--border);
  color: var(--muted);
  background: var(--bg);
}
.source-tag.source-win {
  color: var(--accent);
  border-color: var(--accent);
  background: var(--accent-soft);
}
/* Remoto: discriminado pela flag `remote`, não pelo label (que o dono escolhe). */
.source-tag.source-remote { color: var(--user-accent); border-color: var(--user-border); background: var(--user-bg); }
.host-chip {
  margin: 8px 9px;
  padding: 6px 8px;
  border-radius: 6px;
  font-size: 11px;
  display: flex;
  align-items: center;
  gap: 6px;
}
/* Offline: âmbar forte — os projetos NÃO estão na lista. */
.host-chip-off { background: var(--amber-soft); border: 1px solid var(--amber); color: var(--amber); }
/* Parcial: mais discreto — o arquivo nativo ESTÁ lá; só falta o aninhado. Não é
   o mesmo problema que offline, e a cor não deve gritar como se fosse. */
.host-chip-partial { background: var(--pane); border: 1px solid var(--border); color: var(--muted); }
.host-chip-dot { width: 6px; height: 6px; border-radius: 50%; background: currentColor; flex: none; }
.rename-input {
  width: 100%;
  font: inherit;
  font-weight: 500;
  text-transform: none;
  letter-spacing: normal;
  padding: 3px 6px;
  border: 1px solid var(--accent);
  border-radius: 5px;
  background: var(--pane);
  color: var(--text);
  outline: none;
  box-shadow: 0 0 0 2px var(--accent-soft);
}

/* O toggle Ativos/Arquivados dos projetos é o MESMO componente que o das sessões:
   ícones 30×30, sem texto, dentro da linha de filtro (#project-search, como
   #session-tabs vive dentro de #session-search). Um único selector garante que os
   dois nunca divergem. */
#session-tabs,
#project-tabs {
  display: flex;
  gap: 4px;
  flex: none;
}
#session-tabs .tab,
#project-tabs .tab {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--input-surface, var(--bg));
  color: var(--muted);
  cursor: pointer;
  transition: background 0.1s, color 0.1s, border-color 0.1s;
}
#session-tabs .tab svg,
#project-tabs .tab svg { display: block; }
#session-tabs .tab:hover,
#project-tabs .tab:hover { border-color: var(--accent); }
#session-tabs .tab.active,
#project-tabs .tab.active {
  background: var(--tab-active, var(--accent-soft));
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  color: var(--accent-strong);
}

/* Overlays the title/meta text on hover instead of reserving space for it —
   reserving space (padding-right on .item-title) made wrapped titles reflow
   oddly on every line, not just the one under the cluster. The background +
   z-index keep the icons legible and above the text without touching layout. */
.item-actions {
  position: absolute;
  top: 8px;
  right: 10px;
  z-index: 1;
  display: none;
  gap: 6px;
  background: var(--accent-soft);
  border-radius: 6px;
  padding: 2px;
}
.list li:hover .item-actions,
.list li:focus-visible .item-actions,
.list li:has(:focus-visible) .item-actions { display: flex; }
.row-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 4px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: var(--pane);
  color: var(--muted);
  cursor: pointer;
}
.row-btn svg { display: block; }
.row-btn:hover {
  border-color: var(--accent);
  color: var(--accent);
}
.row-btn.danger:hover {
  border-color: var(--danger);
  color: var(--danger);
  background: var(--danger-soft);
}

#session-search {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border);
  background: var(--pane-sessions, var(--pane));
}
#session-search-input {
  flex: 1;
  font: inherit;
  font-size: 13px;
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--input-surface, var(--bg));
  color: var(--text);
  outline: none;
  min-width: 0;
}
#session-search-input:focus {
  border-color: var(--accent);
  box-shadow: 0 0 0 2px var(--accent-soft);
}
#session-search-input:disabled {
  opacity: 0.55;
  cursor: not-allowed;
}
#session-search-status {
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}

.snippet {
  margin-top: 4px;
  font-size: 11.5px;
  color: var(--muted);
  line-height: 1.4;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  white-space: pre-wrap;
  word-break: break-word;
}
.snippet-role {
  display: inline-block;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  font-size: 10px;
  margin-right: 6px;
  color: var(--accent);
}
.snippet-role.user { color: var(--user-accent); }
.snippet-role.system { color: var(--muted); }
.match-badge {
  display: inline-block;
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-size: 10.5px;
  font-weight: 600;
  padding: 1px 6px;
  border-radius: 10px;
  margin-left: 6px;
}

.empty-badge {
  display: inline-block;
  background: var(--accent-soft);
  color: var(--accent-strong);
  font-size: 10px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding: 1px 6px;
  border-radius: 10px;
  margin-right: 6px;
  vertical-align: middle;
}

/* Live-session dot: green = open in this FRIDAY, amber = running elsewhere.
   A soft pulse hints that the session is alive. */
.live-dot {
  display: inline-block;
  width: 8px;
  height: 8px;
  border-radius: 50%;
  margin-right: 6px;
  vertical-align: middle;
  flex: none;
}
.live-dot.live-here {
  background: #3fb950;
  box-shadow: 0 0 0 0 rgba(63, 185, 80, 0.5);
  animation: live-pulse 2s ease-out infinite;
}
.live-dot.live-elsewhere {
  background: #d29922;
}
@keyframes live-pulse {
  0% { box-shadow: 0 0 0 0 rgba(63, 185, 80, 0.5); }
  70% { box-shadow: 0 0 0 5px rgba(63, 185, 80, 0); }
  100% { box-shadow: 0 0 0 0 rgba(63, 185, 80, 0); }
}
@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
}

.cost {
  color: var(--accent);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.cost-meta {
  cursor: help;
}
/* Custo que exclui uso real (modelo sem tarifa na tabela): âmbar em vez do
   acento, para não se ler como um valor fechado. Usado no chip do cabeçalho,
   na linha da conversa e no total do card. */
.cost.miss,
.cost-summary .cs-total.miss {
  color: var(--amber-text);
}
.cost-summary .cs-miss {
  margin: -4px 0 8px;
  font-size: 11px;
  line-height: 1.35;
  color: var(--amber-text);
}
.cost-summary .cs-miss:empty { display: none; }

.cost-summary {
  max-width: none;
  margin: 0;
  padding: 12px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--pane);
}
.cost-summary .cs-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  margin-bottom: 8px;
}
.cost-summary .cs-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.cost-summary .cs-total {
  font-size: 18px;
  font-weight: 600;
  color: var(--accent);
  font-variant-numeric: tabular-nums;
}
.cost-summary table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
}
.cost-summary td {
  padding: 4px 6px;
  border-top: 1px solid var(--border);
  vertical-align: top;
}
.cost-summary tr:first-child td { border-top: none; }
.cost-summary .cs-model {
  font-weight: 600;
  white-space: nowrap;
}
.cost-summary .cs-tokens {
  color: var(--muted);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 11.5px;
}
.cost-summary .cs-cost {
  text-align: right;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
  color: var(--accent);
  font-weight: 600;
}

/* Tokens de saúde da cache — partilhados pelo card e pelo chip do cabeçalho */
:root { --cache-good: #2ea36b; --cache-warn: #cc9126; --cache-bad: #cf4b53; }
@media (prefers-color-scheme: dark) {
  :root { --cache-good: #43c489; --cache-warn: #e0a63a; --cache-bad: #e4626c; }
}
:root[data-theme="dark"] { --cache-good: #43c489; --cache-warn: #e0a63a; --cache-bad: #e4626c; }
:root[data-theme="light"] { --cache-good: #2ea36b; --cache-warn: #cc9126; --cache-bad: #cf4b53; }

/* Chip de cache no cabeçalho do transcript (visível também na vista Terminal) */
.cost-cache {
  font-size: 12.5px; font-weight: 600; font-variant-numeric: tabular-nums;
  margin-left: 8px; white-space: nowrap;
}
.cost-cache--good { color: var(--cache-good); }
.cost-cache--warn { color: var(--cache-warn); }
.cost-cache--bad  { color: var(--cache-bad); }

/* Badge de custo clicável → abre o card completo num popover */
.cost-meta-btn {
  display: inline-flex; align-items: baseline; gap: 0;
  background: none; border: 0; padding: 2px 4px; margin: 0; cursor: pointer;
  border-radius: 6px; font: inherit; color: inherit;
}
.cost-meta-btn:hover { background: var(--accent-soft); }
.cost-meta-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }

#cost-popover {
  position: fixed; z-index: 60; width: 420px; max-width: calc(92vw / var(--ui-zoom, 1)); overflow: hidden;
  border-radius: 10px; box-shadow: 0 12px 32px -8px rgba(0, 0, 0, 0.4);
}
#cost-popover .cost-summary { margin: 0; position: static; box-shadow: none; width: 100%; box-sizing: border-box; }
#cost-popover table { width: 100%; }
#cost-popover .cs-tokens { white-space: normal; overflow-wrap: anywhere; }
#cost-popover .cs-cache-top { row-gap: 6px; }
#cost-popover .cs-eff-bar { max-width: 130px; }
/* sparkline longo encolhe e faz scroll dentro da linha em vez de transbordar */
#cost-popover .cs-spark-row { min-width: 0; }
#cost-popover .cs-spark-row code { flex: 1 1 auto; min-width: 0; overflow-x: auto; white-space: nowrap; }
#cost-popover .cs-spark-label, #cost-popover .cs-spark-tot { flex: 0 0 auto; }

/* Painel de saúde da cache dentro do cartão de custo */
.cs-cache {
  --cs-c: var(--cache-good);
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
}
.cs-cache--good { --cs-c: var(--cache-good); }
.cs-cache--warn { --cs-c: var(--cache-warn); }
.cs-cache--bad  { --cs-c: var(--cache-bad); }

.cs-cache-top { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.cs-cache-title {
  font-size: 0.72em; font-weight: 700; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--muted);
}
.cs-hit {
  display: inline-flex; align-items: center; gap: 5px;
  font-variant-numeric: tabular-nums; font-weight: 700; color: var(--cs-c); white-space: nowrap;
}
.cs-hit-dot { width: 8px; height: 8px; border-radius: 50%; background: var(--cs-c); }
.cs-eff-bar {
  position: relative; flex: 1 1 80px; min-width: 60px; max-width: 200px; height: 6px;
  border-radius: 3px; background: var(--border); overflow: hidden;
}
.cs-eff-fill { position: absolute; inset: 0 auto 0 0; height: 100%; background: var(--cs-c); border-radius: 3px; }
.cs-cache-save { color: var(--muted); white-space: nowrap; }
.cs-cache-save strong { color: var(--text); }
/* Poupança que exclui os modelos sem tarifa: mesmo âmbar do resto dos números
   incompletos, para não se ler como um facto fechado. */
.cs-cache-save.miss strong { color: var(--amber-text); }

.cs-spark { margin-top: 8px; display: flex; flex-direction: column; gap: 3px; }
.cs-spark-row { display: flex; align-items: center; gap: 8px; }
.cs-spark-row code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  letter-spacing: 1px; line-height: 1; font-size: 0.95em;
}
.cs-spark-read { color: var(--cache-good); }
.cs-spark-create { color: var(--cache-warn); }
.cs-spark-label { display: inline-block; min-width: 62px; color: var(--muted); font-size: 0.8em; }
.cs-spark-tot { color: var(--muted); font-size: 0.78em; font-variant-numeric: tabular-nums; }
.cs-spark-hint { color: var(--muted); font-size: 0.78em; margin-top: 2px; }
.cs-tokens .cs-dot { margin: 0 6px; color: var(--border); }

mark.search-hit {
  background: var(--hl-bg);
  color: var(--hl-text);
  padding: 0 2px;
  border-radius: 2px;
}
mark.search-hit.current {
  background: var(--hl-current-bg);
  outline: 2px solid var(--accent);
}

/* In-pane find bar (transcript + resumo). */
.find-bar {
  display: flex; align-items: center; gap: 6px;
  padding: 7px 12px; border-bottom: 1px solid var(--border); background: var(--pane);
}
.find-bar .find-ic { color: var(--muted); flex: none; }
.find-bar input {
  flex: 1; min-width: 0; font: inherit; font-size: 13px; padding: 5px 8px;
  border: 1px solid var(--border); border-radius: 6px; background: var(--bg); color: var(--text); outline: none;
}
.find-bar input:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }
.find-bar .find-count {
  font: 11px ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--muted);
  flex: none; min-width: 36px; text-align: right;
}
.find-bar .icon-btn { width: 26px; height: 26px; }

#transcript-body {
  flex: 1;
  overflow-y: auto;
  padding: 24px;
  max-width: none;
}
.empty {
  color: var(--muted);
  text-align: center;
  margin-top: 80px;
}

/* Marca F.R.I.D.A.Y. — só no empty-state "sem sessão aberta", nunca sobre um
   transcript. Vive dentro do painel (não é o antigo body::after fixed com
   z-index 5, que se sobrepunha a conteúdo focável) e sai de cena assim que uma
   conversa abre. Os restantes .empty (a carregar, falhou, sem conteúdo) não a
   herdam — daí a classe própria. */
.empty-hero::before {
  content: "";
  display: block;
  width: 240px;
  height: 240px;
  margin: 0 auto 20px;
  background: url("/friday-watermark.webp") no-repeat center / contain;
  opacity: 0.55;
}

/* O WebP é um loop animado e não há como o pausar por CSS: sob reduced-motion
   troca-se pelo PNG, que é a mesma arte parada. */
@media (prefers-reduced-motion: reduce) {
  .empty-hero::before {
    background-image: url("/friday-watermark.png");
  }
}

.msg {
  margin: 0 0 16px;
  max-width: none;
  border-radius: 8px;
  padding: 12px 16px;
  border: 1px solid var(--border);
  background: var(--pane);
}
.msg.user {
  background: var(--user-bg);
  border-color: var(--user-border);
}
.msg.assistant {
  background: var(--assistant-bg);
}
/* Bloco de decisão: a escolha do dono quando lhe pediram um esclarecimento.
   Herda .msg.user (a cor dele) — a decisão é dele; o que muda é a estrutura. */
.msg.user.decision { padding-top: 10px; }
.dec-section + .dec-section {
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--user-border);
}
.dec-header {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--user-accent);
  margin-bottom: 6px;
}
.dec-question { font-size: 12.5px; color: var(--muted); margin-bottom: 8px; }
.dec-picks { list-style: none; margin: 0; padding: 0; }
.dec-pick {
  position: relative;
  padding-left: 20px;
  line-height: 1.6;
}
.dec-pick::before {
  content: "✔";
  position: absolute;
  left: 0;
  color: var(--user-accent);
}
.dec-tag {
  margin-left: 8px;
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
}
.dec-rejected { margin-top: 8px; }
.dec-rejected > summary { cursor: pointer; color: var(--muted); font-size: 11.5px; }
.dec-rejected ul { margin: 6px 0 0; padding-left: 20px; color: var(--muted); font-size: 12.5px; }

/* Bolha "sistema": o que o CLI injetou no canal do dono. O cinzento DERIVA-SE do
   tema (color-mix do --text sobre o --pane) em vez de sair de variáveis próprias:
   os 7 temas teriam de definir cada um o seu --system-bg, e o 8.º esquecer-se-ia.
   Herdar o --pane sem mais, que foi a primeira tentativa, não chegou — no tema
   claro o --pane é branco, e a bolha ficava indistinguível da do assistente
   (verificado no browser). A mistura resolve os dois sentidos de uma vez: escurece
   sobre um fundo claro, clareia sobre um escuro. */
.msg.system {
  font-size: 13px;
  background: color-mix(in srgb, var(--text) 7%, var(--pane));
  border-color: color-mix(in srgb, var(--text) 18%, var(--border));
}
.msg.system .msg-header { color: var(--muted); }
.msg.system > summary,
.msg.system details > summary { cursor: pointer; color: var(--muted); font-size: 11.5px; }
.msg.system details > .msg-body { margin-top: 8px; }

.msg.tool {
  background: var(--tool-bg);
  border-color: var(--tool-border);
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 12.5px;
}
.msg.tool.error {
  background: var(--error-bg);
  border-color: var(--error-border);
}

.msg-header {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted);
  margin-bottom: 8px;
}
.msg.user .msg-header { color: var(--user-accent); }
.msg.assistant .msg-header { color: var(--accent); }

.msg-body { line-height: 1.55; word-wrap: break-word; }
.msg-body p { margin: 0.4em 0; }
.msg-body p:first-child { margin-top: 0; }
.msg-body p:last-child { margin-bottom: 0; }
.msg-body pre {
  background: var(--code-bg);
  color: var(--code-text);
  padding: 10px 12px;
  border-radius: 6px;
  overflow-x: auto;
  font-size: 12.5px;
}
.msg-body code {
  background: var(--code-inline-bg);
  padding: 1px 5px;
  border-radius: 3px;
  font-family: ui-monospace, "SF Mono", Menlo, monospace;
  font-size: 0.92em;
}
.msg-body pre code {
  background: transparent;
  padding: 0;
  font-size: inherit;
}

/* Copy-to-clipboard affordance on every code/output block. The <pre> is moved
   into .code-wrap by enhanceCodeBlocks(); the button floats top-right and does
   not scroll with the block's horizontal overflow. */
.code-wrap { position: relative; }
.code-wrap > pre { margin: 0; }
.code-copy {
  position: absolute;
  top: 6px;
  right: 6px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--muted);
  cursor: pointer;
  opacity: 0;
  transition: opacity 0.12s ease, color 0.12s ease, border-color 0.12s ease;
}
.code-wrap:hover .code-copy,
.code-copy:focus-visible { opacity: 0.9; }
.code-copy:hover { color: var(--accent); border-color: var(--accent); opacity: 1; }
.code-copy.ok { color: #3a9d4b; border-color: #3a9d4b; opacity: 1; }
@media (hover: none) { .code-copy { opacity: 0.75; } }
.msg-body ul, .msg-body ol { margin: 0.4em 0; padding-left: 1.4em; }
.msg-body blockquote {
  border-left: 3px solid var(--border);
  margin: 0.6em 0;
  padding: 0.2em 0 0.2em 0.8em;
  color: var(--muted);
}
.msg-body a { color: var(--accent); }

/* Tabelas de markdown. O renderMarkdown() já emitia <table> correcto (marked
   com gfm:true) e o DOMPurify já o deixava passar — só que o style.css tinha
   regra para tudo o resto que o markdown produz (p, pre, code, ul/ol,
   blockquote, a) e nunca para a tabela. Sem regra própria caía no default do
   browser — border-spacing: 2px, padding: 1px, zero traços, cabeçalho
   centrado — que lê como texto encavalitado, não como tabela: era esse o
   "as tabelas da consola não aparecem na aplicação".
   As três superfícies partilham o mesmo renderMarkdown(), logo partilhavam o
   mesmo defeito; a regra é uma só para não voltar a divergir. */
.msg-body table, .fp-md table, .bk2-md table {
  /* Caixa de scroll própria, a mesma disciplina que o <pre> já tem: uma tabela
     larga scrolla dentro de si em vez de arrastar o painel inteiro na
     horizontal (a 390px levava as outras mensagens com ela). */
  display: block;
  width: max-content;
  max-width: 100%;
  overflow-x: auto;
  border-collapse: collapse;
  margin: 0.7em 0;
  font-size: 0.95em;
}
.msg-body thead, .fp-md thead, .bk2-md thead { background: var(--tool-bg); }
.msg-body th, .fp-md th, .bk2-md th {
  padding: 6px 10px;
  text-align: left;
  font-weight: 600;
  color: var(--text);
  border: 1px solid var(--border);
  white-space: nowrap;
}
.msg-body td, .fp-md td, .bk2-md td {
  padding: 6px 10px;
  vertical-align: top;
  border: 1px solid var(--border);
}

.tool-name { color: var(--accent); }
details { margin-top: 4px; }
details summary {
  cursor: pointer;
  color: var(--muted);
  font-size: 11px;
  user-select: none;
  padding: 2px 0;
}
details[open] summary { margin-bottom: 6px; }
details pre {
  background: var(--code-inline-bg);
  color: var(--text);
  padding: 8px 10px;
  border-radius: 4px;
  overflow-x: auto;
  margin: 0;
  white-space: pre-wrap;
  word-wrap: break-word;
}

.truncated-marker {
  display: block;
  margin-top: 6px;
  font-size: 11px;
  color: var(--muted);
  font-style: italic;
}

.modal-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
}
.modal-overlay.hidden { display: none; }
.modal {
  background: var(--pane);
  border: 1px solid var(--border);
  border-radius: 16px;
  /* Project accent as a top edge — ties every modal to the app's colour. */
  border-top: 3px solid var(--accent);
  padding: 0;
  max-width: 400px;
  width: calc(100% - 48px);
  overflow: hidden;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22);
  /* Nunca ultrapassar a viewport: head/actions fixos, corpo com scroll (abaixo).
     dvh para a barra do browser móvel não cortar.
     /var(--ui-zoom): sob o zoom global da app, as unidades de viewport são
     multiplicadas pelo zoom (o modal renderiza ×zoom); dividir aqui devolve o
     limite ao viewport FÍSICO, senão o modal fica mais alto que o ecrã e o
     head/actions ficam cortados. Mesma compensação em todos os overlays. */
  display: flex;
  flex-direction: column;
  max-height: calc((100dvh - 32px) / var(--ui-zoom, 1));
}
/* Header: title + always-present close (✕). The ✕ is the canonical dismiss —
   Escape and backdrop clicks are intentionally disabled (no accidental close). */
.modal-head {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 15px 18px 12px;
  border-bottom: 1px solid var(--border);
  flex: none;
}
.modal-title {
  font-size: 15px;
  font-weight: 650;
  color: var(--text);
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 8px;
}
.modal-title svg { flex: none; color: var(--accent); margin-right: 4px; }
.modal-close {
  flex: none;
  width: 30px;
  height: 30px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--muted);
  cursor: pointer;
  padding: 0;
  transition: color 0.1s, border-color 0.1s, background 0.1s;
}
.modal-close:hover { color: var(--accent); border-color: var(--accent); }
.modal-close svg { display: block; }
.modal-body {
  font-size: 13px;
  line-height: 1.5;
  color: var(--muted);
  padding: 16px 18px;
  overflow-y: auto;
  min-height: 0;
  flex: 1;
  /* confirmModal usa `body:` com \n\n para separar linhas (ex: estimativa de
     indexação + aviso) — sem isto o textContent colapsa tudo numa única linha. */
  white-space: pre-line;
}
.modal.wide { max-width: 500px; }
.purge-intro { margin: 0 0 12px; }
.purge-list {
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 260px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 8px;
}
.purge-list li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 10px;
  font-size: 12.5px;
  color: var(--text);
  border-bottom: 1px solid var(--border);
}
.purge-list li:last-child { border-bottom: none; }
.purge-open {
  color: var(--accent);
  text-decoration: none;
  font-weight: 600;
  white-space: nowrap;
}
.purge-open:hover { text-decoration: underline; }
.purge-kind {
  font-size: 9.5px;
  text-transform: uppercase;
  font-weight: 700;
  letter-spacing: 0.03em;
  padding: 1px 6px;
  border-radius: 8px;
  background: var(--accent-soft);
  color: var(--accent-strong);
  white-space: nowrap;
}
.purge-proj {
  font-weight: 600;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.purge-sid {
  margin-left: auto;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* ── Mudar a pasta do projeto ──────────────────────────────────────────────
   O .modal-body é `white-space: pre-line` (o confirmModal separa linhas com
   \n\n). Conteúdo rico tem de o repor, senão cada quebra de linha do markup
   vira quebra visível — a mesma correcção que o .ed-row já faz. */
.modal-body:has(.mv-field),
.modal-body:has(.mv-steps),
.modal-body:has(.mv-hint) { white-space: normal; }

.mv-label { display: block; font-size: 11px; font-weight: 650; color: var(--text);
  text-transform: uppercase; letter-spacing: 0.05em; margin-bottom: 6px; }
.mv-input { font: inherit; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 12.5px; width: 100%; padding: 8px 10px; border: 1px solid var(--border);
  border-radius: 8px; background: var(--bg); color: var(--text); }
.mv-input:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-soft); }
/* Um caminho absoluto longo não pode empurrar a largura do modal: o campo é
   100% do pai e o texto de prosa/monospace quebra em qualquer sítio. */
.mv-from { font-size: 11.5px; color: var(--muted); margin: 7px 0 4px; overflow-wrap: anywhere; }
.mv-from code, .mv-also code { font-family: ui-monospace, Menlo, monospace; font-size: 11.5px;
  background: var(--tool-bg); padding: 1px 5px; border-radius: 4px; overflow-wrap: anywhere; }
.mv-from .mv-novo { color: var(--accent); background: var(--accent-soft); }
.mv-alias { font-size: 11.5px; color: var(--muted); margin: 0 0 16px; }

.mv-sec { font-size: 11px; font-weight: 650; color: var(--text); text-transform: uppercase;
  letter-spacing: 0.05em; margin: 0 0 8px; }
.mv-sec .mv-count { font-weight: 400; text-transform: none; letter-spacing: 0; color: var(--muted); font-size: 11.5px; }

.mv-checks { list-style: none; margin: 0 0 16px; padding: 0;
  border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
.mv-checks li { display: flex; align-items: flex-start; gap: 9px; padding: 7px 11px;
  font-size: 12.5px; color: var(--text); border-bottom: 1px solid var(--border);
  line-height: 1.4; overflow-wrap: anywhere; }
.mv-checks li:last-child { border-bottom: none; }
.mv-checks li.blocked { background: var(--danger-soft); }
/* Âmbar, NÃO vermelho: um aviso é um dos três passos finais idempotentes que
   não correu. O move manteve-se de pé — pintá-lo de erro leria como o
   contrário do que aconteceu. */
.mv-checks li.mv-warn { background: var(--amber-soft); }
.mv-warn .mv-ico { color: var(--amber-text); }
.mv-ico { flex: none; margin-top: 1px; }
.mv-ok .mv-ico { color: var(--success); }
.blocked .mv-ico { color: var(--danger); }
.mv-txt { flex: 1; min-width: 0; }
.mv-why { display: block; font-size: 11.5px; margin-top: 2px; color: var(--danger); }
.mv-detail { color: var(--muted); font-weight: 400; }
.mv-code { font-family: ui-monospace, Menlo, monospace; font-size: 10.5px; color: var(--danger);
  background: var(--pane); border: 1px solid var(--error-border); padding: 0 5px;
  border-radius: 4px; margin-left: auto; flex: none; align-self: center; }

.mv-moves { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 12px; }
.mv-chip { display: inline-flex; align-items: baseline; gap: 6px; padding: 4px 10px;
  border: 1px solid var(--border); border-radius: 20px; background: var(--bg);
  font-size: 11.5px; color: var(--muted); }
.mv-chip b { color: var(--text); font-size: 12.5px; font-weight: 650; font-variant-numeric: tabular-nums; }

.mv-hint { display: flex; gap: 8px; align-items: flex-start; font-size: 11.5px;
  line-height: 1.45; padding: 9px 11px; border-radius: 8px; }
.mv-hint svg { flex: none; margin-top: 1px; }
.mv-hint.warn { background: var(--amber-soft); color: var(--amber-text); border: 1px solid var(--tool-border); }
.mv-hint.bad { background: var(--danger-soft); color: var(--danger); border: 1px solid var(--error-border); }

.mv-steps { list-style: none; margin: 0 0 16px; padding: 0; }
.mv-steps li { display: flex; align-items: center; gap: 9px; padding: 5px 0;
  font-size: 12.5px; color: var(--text); }
.mv-steps li svg { color: var(--success); flex: none; }

.mv-also { border: 1px solid var(--border); border-left: 3px solid var(--amber-text);
  border-radius: 8px; padding: 11px 13px; background: var(--bg); }
.mv-also ul { list-style: none; margin: 0; padding: 0; }
.mv-also li { font-size: 12px; color: var(--text); padding: 3.5px 0; overflow-wrap: anywhere; }

/* O botão preso tem de PARECER preso. `.btn` não tem regra `:disabled` nenhuma
   no resto do ficheiro — visto a renderizar, um pré-voo bloqueado mostrava o
   mesmo botão laranja com o mesmo cursor de clicável, e clicá-lo não fazia
   nada sem dizer porquê. Escopado a ESTE modal (o mesmo cuidado do
   `.rc-edit-save:disabled`): mudar o `.btn:disabled` geral tocaria os ~26
   sítios que também desativam botões, e isso é um achado à parte. */
.modal:has(.mv-field) #modal-confirm:disabled { opacity: 0.5; cursor: default; }

/* Num ecrã táctil o campo é o único alvo do modal fora dos botões do rodapé. */
@media (pointer: coarse) {
  .mv-input { min-height: 44px; font-size: 16px; }
}

.modal-actions {
  display: flex;
  justify-content: flex-end;
  gap: 8px;
  padding: 12px 18px 16px;
  border-top: 1px solid var(--border);
  flex: none;
}
.btn {
  font: inherit;
  font-size: 13px;
  font-weight: 500;
  padding: 7px 14px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
  color: var(--text);
  cursor: pointer;
}
.btn:hover { border-color: var(--accent); }
.btn.danger {
  background: var(--danger);
  border-color: var(--danger);
  color: #fff;
}
.btn.danger:hover { background: var(--danger-strong); border-color: var(--danger-strong); }
.btn.primary {
  background: var(--accent);
  border-color: var(--accent);
  color: #fff;
}
.btn.primary:hover { filter: brightness(1.06); }

/* Single-instance guard overlay — sits above everything (search 200, modal 100). */
.instance-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.5);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 1000;
}
.instance-overlay.hidden { display: none; }
.instance-card {
  background: var(--pane);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 24px 26px;
  max-width: 420px;
  width: calc(100% - 48px);
  text-align: center;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.28);
}
.instance-card h2 {
  font-size: 16px;
  font-weight: 600;
  margin: 0 0 10px;
  color: var(--text);
}
.instance-card p {
  font-size: 13px;
  line-height: 1.55;
  color: var(--muted);
  margin: 0 0 20px;
}
.instance-actions {
  display: flex;
  justify-content: center;
  gap: 10px;
}

.gsearch-overlay {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.35);
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding-top: calc(10vh / var(--ui-zoom, 1)); /* compensar zoom (ver .modal) */
  z-index: 200;
}
.gsearch-overlay.hidden { display: none; }
.gsearch {
  background: var(--pane);
  border: 1px solid var(--border);
  border-radius: 10px;
  width: 640px;
  max-width: calc(100% - 48px);
  max-height: calc(70vh / var(--ui-zoom, 1)); /* compensar zoom (ver .modal) */
  display: flex;
  flex-direction: column;
  overflow: hidden;
  box-shadow: 0 18px 48px rgba(0, 0, 0, 0.22);
}
#gsearch-input {
  font: inherit;
  font-size: 16px;
  padding: 14px 16px;
  border: none;
  border-bottom: 1px solid var(--border);
  background: var(--pane);
  color: var(--text);
  outline: none;
}
#gsearch-input:focus {
  border-bottom-color: var(--accent);
  box-shadow: inset 0 -2px 0 var(--accent);
}
.gsearch-status {
  padding: 6px 16px;
  font-size: 11px;
  border-bottom: 1px solid var(--border);
}
.gsearch-status:empty { display: none; }
.gsearch-tagbar {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border);
}
.gsearch-tagbar.hidden { display: none; }
.gsearch-archived-toggle {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 8px 16px;
  font-size: 11px;
  color: var(--muted);
  cursor: pointer;
  border-bottom: 1px solid var(--border);
  user-select: none;
}
.gsearch-archived-toggle input { cursor: pointer; margin: 0; }
.gsearch-arch-badge {
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 1px 6px;
  border-radius: 8px;
  border: 1px solid var(--border);
  color: var(--muted);
  white-space: nowrap;
}
.gsearch-results {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
}
.gsearch-results li {
  padding: 10px 16px;
  border-bottom: 1px solid var(--border);
  cursor: pointer;
  transition: background 0.1s;
}
.gsearch-results li:last-child { border-bottom: none; }
.gsearch-results li.selected,
.gsearch-results li:focus,
.gsearch-results li:hover { background: var(--accent-soft); }
.gsearch-row-head {
  display: flex;
  align-items: baseline;
  gap: 8px;
  margin-bottom: 3px;
}
.gsearch-project {
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
}
.gsearch-title {
  font-weight: 500;
  word-break: break-word;
  flex: 1;
}
.gsearch-date {
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
}
.gsearch-capped {
  padding: 8px 16px;
  font-size: 11px;
  color: var(--muted);
  font-style: italic;
  cursor: default;
}

.gsearch-miss {
  padding: 8px 16px;
  background: var(--amber-soft);
  color: var(--amber);
  border-left: 2px solid var(--amber);
  font-size: 11.5px;
  cursor: default;
}
/* Parcial ≠ offline: o host respondeu, só não vimos tudo. Discreto, como o chip
   da coluna (.host-chip-partial) — não deve gritar em âmbar como um host morto. */
.gsearch-miss.gsearch-partial {
  background: var(--pane);
  color: var(--muted);
  border-left-color: var(--border);
}

/* Toggle texto/significado (pesquisa semântica). Nomes de variáveis mapeados
   para os tokens reais deste ficheiro (não há --border-2/--text-2/--text-3/--warn
   aqui): --border, --muted e --amber cobrem os mesmos papéis. */
.gsearch-modes { display: flex; gap: 6px; padding: 6px 10px 0; }
/* Desligada, o refreshSemanticChip faz innerHTML = '' mas a <div> continua no
   layout — sem esta regra ela ainda gasta os 6px de padding vertical e empurra
   a barra de datas para baixo (o invariante "byte a byte" com a `main`). */
.gsearch-modes:empty { display: none; }
.gsearch-mode {
  font-size: 11px; padding: 3px 10px; min-height: 24px; min-width: 24px;
  border: 1px solid var(--border); border-radius: 11px;
  background: transparent; color: var(--muted); cursor: pointer;
}
.gsearch-mode.active { border-color: var(--accent); color: var(--accent); }
.gsearch-mode[disabled] { opacity: .45; cursor: not-allowed; }
.gsearch-mode.todo { border-color: var(--amber); color: var(--amber); }
.gsearch-score {
  font-size: 10px; font-variant-numeric: tabular-nums;
  color: var(--muted); margin-left: 6px;
}
.gsearch-src-badge { font-size: 10px; color: var(--muted); margin-left: 6px; }
/* Alvos de toque: em ecrã táctil os chips têm de cumprir 44×44 (rules/audit/ux). */
@media (pointer: coarse) {
  .gsearch-mode { min-height: 44px; min-width: 44px; padding: 0 16px; }
}
/* Modo "significado": tagbar/datebar/arquivadas não são usados pela pesquisa
   semântica (ver updateSemanticFilterState em app.js) — esbatem-se para não
   parecerem filtros activos. O `inert` que o JS aplica já trata do foco/clique;
   isto é só o sinal visual. */
.gsearch-filter-inert { opacity: .4; }

.icon-btn {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 28px;
  height: 28px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--text);
  cursor: pointer;
  transition: border-color 0.1s, color 0.1s;
}
.icon-btn:hover { border-color: var(--accent); color: var(--accent); }
/* O `display: flex` acima é regra de autor: ganha ao `[hidden] { display: none }`
   da folha do browser. Sem isto, `btn.hidden = true` em JS não esconde botão
   nenhum — foi assim que o #lock-btn ficou visível sem password configurada.
   A regra é da classe, não de um botão: era per-botão (só `.live-btn`) e o
   `.lock-btn`, criado depois, nasceu com o mesmo defeito por não ser copiado. */
.icon-btn[hidden] { display: none; }

/* Empty (deletable) projects: de-emphasised so the live ones read first. */
.list li.project-empty .item-title { color: var(--muted); font-style: italic; }

/* Projects filter — same component as the sessions search (#session-search). */
#project-search {
  display: flex; align-items: center; gap: 8px;
  padding: 8px 16px; border-bottom: 1px solid var(--border); background: var(--pane-projects, var(--pane));
}
#project-search input {
  flex: 1; font: inherit; font-size: 13px; padding: 6px 10px;
  border: 1px solid var(--border); border-radius: 6px;
  background: var(--input-surface, var(--bg)); color: var(--text); outline: none; min-width: 0;
}
#project-search input:focus { border-color: var(--accent); box-shadow: 0 0 0 2px var(--accent-soft); }

/* Favoritos / Recentes section labels (B). */
.list li.proj-sect {
  padding: 9px 12px 3px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 9.5px; letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--muted); cursor: default; border-bottom: 0;
}
.list li.proj-sect:hover { background: transparent; }

/* Favorite star (B): always gold for favorites, faint+hover-revealed otherwise. */
.proj-star {
  position: absolute; top: 8px; right: 10px;
  width: 22px; height: 22px; padding: 0;
  display: inline-flex; align-items: center; justify-content: center;
  border: none; background: none; cursor: pointer;
  color: var(--border); opacity: 0; transition: opacity 0.1s, color 0.1s;
}
.proj-star svg { display: block; }
.proj-star.on { color: #e0a93c; opacity: 1; }
.list li:hover .proj-star,
.list li:focus-visible .proj-star,
.list li:has(:focus-visible) .proj-star,
.proj-star:focus-visible { opacity: 1; }
.proj-star:hover { color: #e0a93c; }

/* Launch dialogs (new session / new project / channel choice). */
.launch-form { display: flex; flex-direction: column; gap: 9px; }
.launch-form label { font-size: 11px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; text-transform: uppercase; letter-spacing: 0.05em; color: var(--muted); }
.launch-form .tag-input {
  font-family: inherit; /* redundante com a regra de base, explícito por ser a classe que falhou */ width: 100%; font-size: 13px; border-radius: 8px; padding: 7px 10px; }
.launch-form select.tag-input { appearance: auto; }
.launch-check { display: flex; align-items: center; gap: 7px; font-family: var(--sans, inherit); font-size: 12.5px; text-transform: none; letter-spacing: 0; color: var(--text); }
.launch-check input { width: auto; }
.launch-note { font-size: 11.5px; color: var(--muted); margin: 2px 0 0; }
.launch-note.err { color: var(--danger); }

/* Formulário de arranque — barra do campo, contador, anexos. */
.lf-inbar { display: flex; align-items: center; justify-content: space-between; gap: 8px; margin-top: 14px; }
.lf-inbar label { margin: 0; }
.lf-tools { display: flex; align-items: center; gap: 8px; }
.lf-count { font-size: 11px; color: var(--muted); font-variant-numeric: tabular-nums; }
.lf-count.warn { color: var(--accent-strong, var(--accent)); font-weight: 600; }
/* `hidden` só esconde se a regra de autor não ganhar — ver ui-conventions.md. */
.lf-count[hidden], .lf-hint[hidden], .lf-erro[hidden] { display: none; }
.lf-clip { width: 28px; height: 28px; }
.lf-ta { resize: vertical; line-height: 1.55; min-height: 88px; }
.lf-ta.drop { border-style: dashed; border-color: var(--accent); background: var(--accent-soft); }
.lf-ta.err { border-color: var(--danger); }
.lf-hint { font-size: 11px; color: var(--accent-strong, var(--accent)); font-weight: 600; margin: 7px 0 0; }
.lf-erro { display: flex; gap: 8px; margin-top: 12px; padding: 8px 10px; border: 1px solid var(--danger);
  background: var(--danger-soft); border-radius: 9px; color: var(--danger); font-size: 11.5px; line-height: 1.45; }
@media (pointer: coarse) {
  .lf-clip { width: 44px; height: 44px; }
}

/* Chips de anexo (formulário de arranque). Seletores prefixados com
   `.lf-chips` de propósito — NÃO `.lf-chip` sozinho: essa classe já existe
   acima ("Launch-flag chips") para os pills de modelo/canal do cabeçalho de
   sessão, com forma diferente (pílula, border-radius: 999px). Uma regra
   `.lf-chip { … border-radius: 9px … }` solta neste bloco ganhava à de cima
   por ordem de cascata e desarredondava os pills existentes em todo o resto
   da app; `.lf-chips .lf-chip` só apanha os chips que vivem dentro da tira de
   anexos (`chipsHtml()`, public/launch-form.js), que é onde `.lf-chips` é o
   único contentor. */
.lf-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 8px; }
/* SEM `flex-wrap: wrap` aqui (regressão da ronda anterior, achado IMPORTANT
   da re-revisão): estava na base para `.lf-erro-msg` (abaixo) poder cair
   para uma segunda linha, mas isso reparte thumb/nome/(tamanho+✕) por linhas
   separadas ANTES de `text-overflow: ellipsis` entrar em jogo, em qualquer
   chip cujo nome passe de ~11 caracteres a 190px de largura — e o nome por
   omissão de uma captura de ecrã do macOS ("Captura de ecrã 2026-07-25, às
   14.30.12.png") é exactamente esse caso: medido a 92px/3 linhas com `wrap`,
   38px/1 linha sem. O estado normal (pronto/a-enviar) NUNCA tem
   `.lf-erro-msg` no markup — só `chipsHtml()` o acrescenta quando
   `estado==='falhou'` — por isso o `wrap` só faz falta ao `.falhou` (ver
   abaixo), nunca à base. */
.lf-chips .lf-chip { position: relative; overflow: hidden; display: flex; align-items: center; gap: 7px; max-width: 190px;
  padding: 4px 6px 4px 4px;
  border: 1px solid var(--border); border-radius: 9px; background: var(--bg); font-size: 11.5px; color: var(--text); }
.lf-chips .lf-chip .lf-bar { position: absolute; left: 0; bottom: 0; height: 2px; background: var(--accent); transition: width .15s linear; }
@media (prefers-reduced-motion: reduce) { .lf-chips .lf-chip .lf-bar { transition: none; } }
.lf-chips .lf-chip .lf-thumb { flex: none; width: 28px; height: 28px; border-radius: 6px; background: var(--accent-soft); overflow: hidden; }
/* Miniatura real (achado 9) — só existe no markup quando `a.thumbUrl` está
   definido (ficheiro reconhecido como imagem); o `<span>` vazio acima cobre
   os outros casos, o mesmo placeholder neutro de sempre. */
.lf-chips .lf-chip .lf-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lf-chips .lf-chip .lf-nome { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.lf-chips .lf-chip .lf-tam { flex: none; font-size: 10.5px; color: var(--muted); }
.lf-chips .lf-chip .lf-rm { flex: none; width: 24px; height: 24px; border-color: transparent; background: none; }
/* A subir: SEM opacity — baixava o contraste do texto abaixo do mínimo AA
   (WCAG 1.4.3, achado 7 da revisão) precisamente no estado em que o dono mais
   precisa de o ler (quantos % faltam, que ficheiro é). A moldura tracejada
   usa o mesmo vocabulário visual que `.lf-ta.drop` já usa para "em curso";
   nome e tamanho ficam a 100% de opacidade. */
.lf-chips .lf-chip.a-enviar { border-style: dashed; border-color: var(--accent); }
/* Falhado: mais largo e livre para crescer em altura — a razão do erro
   (`.lf-erro-msg` abaixo) precisa de espaço que os 190px normais não davam
   (achado 2). A cor continua a marcar o estado, mas deixa de ser o ÚNICO
   sinal: o texto ao lado é que carrega a informação. */
/* `flex-wrap: wrap` só aqui, no `.falhou` — nunca na base (ver comentário
   acima). Só o chip falhado tem `.lf-erro-msg` no markup, e é só ele que
   precisa de uma segunda linha. */
.lf-chips .lf-chip.falhou { border-color: var(--danger); color: var(--danger); max-width: 260px; flex-wrap: wrap; }
/* `flex-basis: 100%` força esta linha para o início da PRÓXIMA linha do flex
   (truque padrão de flex-wrap, activo só em `.falhou` acima) — sem precisar
   de um wrapper de "primeira linha" à volta de thumb/nome/tamanho/✕ em
   chipsHtml(). */
.lf-chips .lf-chip .lf-erro-msg { flex-basis: 100%; font-size: 10.5px; line-height: 1.4; color: var(--danger); white-space: normal; margin-top: 2px; }
.lf-chips .lf-chip .lf-retry { flex: none; width: 24px; height: 24px; border-color: transparent; background: none; margin-left: auto; }
@media (pointer: coarse) {
  .lf-chips .lf-chip .lf-rm, .lf-chips .lf-chip .lf-retry { width: 44px; height: 44px; }
}

/* Fonte do terminal (⚙ Definições → Aparência). Linha de duas colunas
   (família | tamanho) local a este bloco — o .launch-form continua a empilhar
   em coluna única em todo o resto do modal, que é o que os outros painéis
   esperam. Colapsa para uma coluna em ecrã estreito. */
.term-row { display: grid; grid-template-columns: 1fr 84px; gap: 9px 14px; align-items: end; }
.term-col { display: flex; flex-direction: column; gap: 6px; min-width: 0; }
.term-font-row { display: flex; gap: 6px; align-items: center; }
.term-font-row select { flex: 1; min-width: 0; }
.term-font-row .btn { flex: none; white-space: nowrap; }
@media (max-width: 520px) {
  .term-row { grid-template-columns: 1fr; }
}

/* Controlo de zoom global da app (aba Aparência): −/+ com a percentagem no meio
   e o botão de repor. */
.zoom-row { display: flex; align-items: center; gap: 8px; }
.zoom-row .zoom-step { font-size: 18px; line-height: 1; font-weight: 600; }
.zoom-row .zoom-val {
  min-width: 52px; text-align: center; font-variant-numeric: tabular-nums;
  font-weight: 600;
}
.zoom-row .btn { margin-left: 4px; white-space: nowrap; }

/* Amostra da fonte do terminal: várias linhas (caixa/dígitos/símbolos) para a
   fonte ser mesmo julgável, não um snippet cortado. Reflete os campos em
   direto, antes de o Guardar os empurrar para os terminais abertos. Scroll
   horizontal (não corte) em tamanhos grandes — o objetivo é ver os glifos,
   não perdê-los para um ellipsis. */
.term-font-preview {
  border: 1px solid var(--border); border-radius: 8px; padding: 9px 11px;
  background: #1a1917; color: #e8e6e3; white-space: pre; overflow-x: auto;
  display: flex; flex-direction: column; gap: 4px; line-height: 1.35;
}

/* Aba Pesquisa (⚙) — barra de progresso da indexação semântica. */
/* Rótulo à esquerda, ações à direita, na MESMA linha — o mesmo arranjo que a
   coluna de projetos usa. Antes os botões caíam numa linha própria por baixo da
   nota, o que empurrava o bloco para baixo sem acrescentar informação nenhuma. */
.cfg-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; }
.cfg-head label { margin: 0; }
.cfg-head-acts { display: flex; gap: 6px; flex: none; }

.sem-bar { height: 5px; border-radius: 3px; background: var(--border); overflow: hidden; margin: 6px 0 10px; }
.sem-bar > i { display: block; height: 100%; background: var(--accent); transition: width .3s ease; }

/* Aba Pesquisa (⚙) — endereço do servidor de embeddings, botão Testar e aviso
   de remoto (D13/D14, agnóstico ao protocolo). */
.sem-url-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 2px 0 4px; }
.sem-url-row .btn { min-height: 24px; min-width: 24px; }
.sem-test-models { display: block; margin-top: 2px; color: var(--muted); }
.sem-remote-warn {
  margin: 2px 0 10px;
  padding: 8px 10px;
  border: 1px solid var(--danger);
  border-radius: 8px;
  background: var(--danger-soft);
  font-size: 12.5px;
}
.sem-remote-warn p { margin: 0 0 6px; }
.sem-remote-warn .launch-check { min-height: 24px; padding: 4px 0; }
/* Campo de chave de API (D14) — mesma linha flex do endereço acima. */
.sem-key-row { display: flex; align-items: center; gap: 10px; margin: 2px 0 4px; }
.sem-key-row input { flex: 1; min-width: 0; }
.sem-key-row .btn { min-height: 24px; min-width: 24px; }
@media (hover: none), (pointer: coarse) {
  .sem-url-row .btn { min-height: 44px; min-width: 44px; }
  .sem-remote-warn .launch-check { min-height: 44px; }
  .sem-key-row .btn { min-height: 44px; min-width: 44px; }
}

/* ⚙ Definições em abas (Geral · Lançamento · Aparência). */
#cfg-tabs { display: flex; gap: 4px; margin-bottom: 12px; }
#cfg-tabs .tab {
  flex: 1;
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--input-surface, var(--bg));
  color: var(--muted);
  cursor: pointer;
  transition: background 0.1s, color 0.1s, border-color 0.1s;
}
#cfg-tabs .tab:hover { border-color: var(--accent); }
#cfg-tabs .tab.active {
  background: var(--tab-active, var(--accent-soft));
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  color: var(--accent-strong);
}

/* Both settings panels stack in the same grid cell so the track sizes to the
   taller one — switching tabs never shrinks/grows the modal. */
#cfg-panels { display: grid; }
#cfg-panels > [data-cfg-panel] { grid-area: 1 / 1; }
#cfg-panels > [data-cfg-panel][data-cfg-hidden] { visibility: hidden; pointer-events: none; }
/* Campos condicionais DENTRO de um painel (ex: o campo Modelo morfa com o motor:
   enum claude OU tags ollama, mais o endereço próprio do ollama) colapsam mesmo —
   ao contrário dos painéis, não há altura a reservar. */
#cfg-model[data-cfg-hidden],
#cfg-ollama-model[data-cfg-hidden],
#cfg-launch-addr[data-cfg-hidden] { display: none; }

/* Abas genéricas de modal (idioma do #cfg-tabs, reutilizável). O painel 🌐 usa-as.
   Painel inativo: display:none → o modal encolhe para o painel ATIVO (a lista de
   passkeys é ilimitada; não queremos o modal sempre com a altura do maior painel). */
.modal-tabs { display: flex; gap: 4px; margin-bottom: 12px; }
.modal-tabs .tab {
  flex: 1;
  font: inherit;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  padding: 6px 8px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--input-surface, var(--bg));
  color: var(--muted);
  cursor: pointer;
  transition: background 0.1s, color 0.1s, border-color 0.1s;
}
.modal-tabs .tab:hover { border-color: var(--accent); }
.modal-tabs .tab.active {
  background: var(--tab-active, var(--accent-soft));
  border-color: color-mix(in srgb, var(--accent) 45%, var(--border));
  color: var(--accent-strong);
}
.modal-panels > [data-tp-panel][data-tp-hidden] { display: none; }

/* Picker de tema (⚙ Definições → Aparência): um swatch por tema, com as três
   colunas na ordem em que aparecem no ecrã, para se ver a palete antes de a
   escolher. Uma lista só — claro, escuro, sistema e as cinco skins vivem todos
   no mesmo eixo. */
.theme-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(104px, 1fr)); gap: 8px; }
.theme-opt {
  display: flex; flex-direction: column; align-items: stretch; gap: 5px;
  padding: 7px; border: 1px solid var(--border); border-radius: 10px;
  background: var(--input-surface, var(--bg)); cursor: pointer;
}
.theme-opt:hover { border-color: var(--accent); }
.theme-opt.active { border-color: var(--accent); background: var(--accent-soft); }
.theme-swatch { display: flex; height: 26px; border-radius: 6px; overflow: hidden; border: 1px solid rgba(128, 128, 128, 0.28); }
.theme-swatch i { flex: 1; display: block; }
.theme-name { font-size: 11.5px; font-weight: 600; text-align: center; line-height: 1.25; color: var(--muted); text-transform: none; letter-spacing: 0; }
.theme-cap { font-size: 9.5px; text-align: center; line-height: 1.2; color: var(--muted); text-transform: uppercase; letter-spacing: 0.05em; }
.theme-opt.active .theme-name { color: var(--text); }
/* Foco de teclado visível nos pickers (WCAG 2.4.7/2.4.13-friendly). */
#cfg-tabs .tab:focus-visible,
.modal-tabs .tab:focus-visible,
.theme-opt:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
/* Alvos ≥44px em ecrã táctil (WCAG 2.5.8 + regra da casa p/ coarse pointer). */
@media (hover: none), (pointer: coarse) {
  #cfg-tabs .tab, .modal-tabs .tab { min-height: 44px; }
  .theme-opt { min-height: 44px; }
}

/* Global live-sessions indicator in the projects header. */
.live-btn { width: auto; padding: 0 9px; gap: 5px; }
/* Soft tinted pill so it reads as a status, not just an icon. Colours work on
   both themes (bright fg + translucent bg). */
.live-btn.live-here { color: #3fb950; border-color: #3fb950; background: rgba(63, 185, 80, 0.14); }
.live-btn.live-elsewhere { color: #d29922; border-color: #d29922; background: rgba(210, 153, 34, 0.14); }
/* The active-session count, shown as a clear bold number + label beside the icon
   ("2 ativas"), instead of a tiny hard-to-read badge. */
.live-count {
  font-size: 12.5px; font-weight: 700; line-height: 1;
  font-variant-numeric: tabular-nums;
}
/* The word after the count is set per-state by syncLiveIndicator (via data-live-label):
   "a trabalhar" / "a aguardar" / "terminou" / "ativa(s)" / "fora daqui". */
.live-count::after { content: attr(data-live-label); font-weight: 600; font-size: 10.5px; opacity: 0.85; }
/* Live-sessions modal list. */
.live-manager { display: flex; flex-direction: column; gap: 8px; }
.live-row {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 11px; border: 1px solid var(--border); border-radius: 9px;
}
.live-meta { flex: 1; min-width: 0; }
.live-title { font-weight: 600; font-size: 13px; }
.live-sub {
  font-size: 11px; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.live-actions { display: flex; gap: 6px; flex: none; }
/* Last-interaction column in the live-sessions modal. */
.live-when {
  flex: none; font-size: 11px; color: var(--muted);
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; white-space: nowrap;
}
/* Per-project group headers inside the live-sessions modal. */
.live-group {
  display: flex; align-items: center; gap: 8px; margin: 13px 4px 3px;
}
.live-group:first-child { margin-top: 2px; }
.live-group .lg-dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }
.live-group .lg-dot.live-here { background: #3fb950; }
.live-group .lg-dot.live-elsewhere { background: #d29922; }
.live-group .lg-label {
  font-size: 12px; font-weight: 600; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; flex: 0 1 auto;
}
.live-group .lg-line { flex: 1; height: 1px; background: var(--border); }
.live-group .lg-count {
  flex: none; padding: 2px 7px; border-radius: 9px;
  font: 600 10px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--accent-soft); color: var(--accent);
}

/* Project picker (move-chat modal). */
.pick-list { display: flex; flex-direction: column; gap: 6px; }
.pickrow {
  display: flex; align-items: center; gap: 10px;
  padding: 8px 10px; border: 1px solid var(--border); border-radius: 8px; cursor: pointer;
}
.pickrow:hover { background: var(--accent-soft); }
.pickrow.act { background: var(--accent-soft); border-color: var(--accent); }
.pickrow .radio {
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--muted); flex: none;
}
.pickrow.act .radio {
  border-color: var(--accent);
  background: radial-gradient(circle, var(--accent) 0 3px, transparent 4px);
}
.pick-title { font-weight: 600; font-size: 13px; }
.pick-sub { font-size: 11px; color: var(--muted); margin-left: 6px; }

/* Per-response "copiar para memória" button (in the assistant msg header). */
.msg.assistant .msg-header { display: flex; align-items: center; gap: 8px; }
.mem-btn {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; font-family: inherit;
  padding: 2px 8px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--pane); color: var(--muted);
  cursor: pointer; opacity: 0; transition: opacity 0.1s, border-color 0.1s, color 0.1s;
}
.msg.assistant:hover .mem-btn,
.mem-btn:focus-visible { opacity: 1; }
.mem-btn:hover { border-color: var(--accent); color: var(--accent); }
.mem-btn svg { display: block; }

/* Fork affordance on user messages (mirrors .mem-btn). */
.fork-btn {
  margin-left: auto;
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 11px; font-family: inherit;
  padding: 2px 8px; border-radius: 6px;
  border: 1px solid var(--border); background: var(--pane); color: var(--muted);
  cursor: pointer; opacity: 0.55; transition: opacity 0.1s, border-color 0.1s, color 0.1s;
}
.msg.user:hover .fork-btn,
.fork-btn:focus-visible { opacity: 1; }
.fork-btn:hover { border-color: var(--accent); color: var(--accent); }
.fork-btn svg { display: block; }
/* Fork-point preview inside the fork modal. */
.fork-ctx {
  font-size: 12px; color: var(--muted);
  background: var(--bg); border: 1px solid var(--border); border-left: 3px solid var(--accent);
  border-radius: 8px; padding: 8px 10px; white-space: pre-wrap; word-break: break-word;
}

.header-actions { display: flex; align-items: center; gap: 6px; }
#restart-btn:disabled { cursor: default; opacity: 0.5; }
#restart-btn.spinning { animation: friday-spin 0.8s linear infinite; }
@keyframes friday-spin { to { transform: rotate(360deg); } }

/* Usage bar — bottom strip mirroring the CLI HUD's rate-limit windows (5h / 7d).
   Lives in the body grid's second row, spanning all three columns; collapses to
   nothing when hidden (stale or no cache). Data comes from /api/usage. */
.usage-bar {
  grid-column: 1 / -1;
  display: flex;
  align-items: center;
  gap: 18px;
  padding: 7px 16px;
  border-top: 1px solid var(--border);
  background: var(--statusbar-bg, var(--pane));
  font-size: 12px;
  color: var(--muted);
  white-space: nowrap;
  overflow: hidden;
}
.usage-bar.hidden { display: none; }
.usage-seg {
  display: flex;
  align-items: center;
  gap: 8px;
  min-width: 0;
}
.usage-seg .usage-label {
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--accent);
}
.usage-track {
  width: 90px;
  height: 6px;
  border-radius: 3px;
  background: var(--accent-soft);
  overflow: hidden;
  flex: none;
}
.usage-fill {
  display: block;
  height: 100%;
  background: var(--accent);
  border-radius: 3px;
}
.usage-seg.warn .usage-fill { background: #d9890f; }
.usage-seg.crit .usage-label,
.usage-seg.crit .usage-pct { color: var(--danger); }
.usage-seg.crit .usage-fill { background: var(--danger); }
/* Stale window (cached data is past its reset): dimmed + muted fill, help cursor
   to invite the explanatory tooltip. */
.usage-seg.stale { opacity: 0.5; cursor: help; }
.usage-seg.stale .usage-fill { background: var(--muted); }
.usage-pct { font-weight: 600; color: var(--text); font-variant-numeric: tabular-nums; }
.usage-reset { color: var(--muted); }
/* Running-release segment: pinned to the right of the footer, always visible. */
.usage-ver { margin-left: auto; flex: none; }
.usage-ver .usage-rev { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; color: var(--text); }
/* Servidor a correr código anterior ao do disco. Fica em âmbar no rodapé porque
   é a única superfície sempre visível — o mesmo facto já existia no painel de
   estado do sistema, mas lá só se vê quem o for abrir de propósito, e este é um
   estado que se manifesta como "a funcionalidade não funciona". */
.usage-ver.stale .usage-rev,
.usage-ver.stale .usage-label { color: var(--amber-text); }
.usage-stale {
  margin-left: 6px;
  padding: 0 6px;
  border: 1px solid var(--amber-text);
  border-radius: 8px;
  background: var(--amber-soft);
  color: var(--amber-text);
  font-size: 10px;
  font-weight: 650;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}
.usage-div { color: var(--border); }
/* Git segment: branch name + optional worktree chip (no gauge). */
.usage-seg.usage-git { gap: 6px; }
.usage-git-ic { display: flex; color: var(--accent); flex: none; }
.usage-branch {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-weight: 600;
  color: var(--text); min-width: 0; overflow: hidden; text-overflow: ellipsis;
}
.usage-wt {
  font-size: 9.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--accent-strong); background: var(--accent-soft); border-radius: 5px; padding: 1px 6px; flex: none;
}
/* Model segment: which model produced the open session's most recent turn. */
.usage-seg.usage-model { gap: 6px; }
.usage-model-ic { display: flex; color: var(--accent); flex: none; }
.usage-model-name { font-weight: 600; color: var(--text); white-space: nowrap; }

/* Branch badge — shown on isolated sessions in the session list. */
.wt-badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 1px 7px; border-radius: 10px; font-size: 11px; line-height: 1.6;
  background: color-mix(in srgb, var(--accent) 18%, transparent);
  color: var(--accent);
  border: 1px solid color-mix(in srgb, var(--accent) 35%, transparent);
  white-space: nowrap;
  margin-left: 6px;
  vertical-align: middle;
}

/* ─── Mobile back button ─────────────────────────────────────────────────────
   A "‹ Voltar" affordance in the sessions/transcript headers. Hidden on desktop
   (the 3-pane grid is always visible there); shown only at the mobile breakpoint
   where one pane is visible at a time. */
.mobile-back {
  display: none;
  font: inherit;
  font-size: 12px;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  padding: 4px 8px;
  margin-right: 4px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--bg);
  color: var(--accent);
  cursor: pointer;
  flex: none;
  white-space: nowrap;
}
.mobile-back:hover { border-color: var(--accent); }

/* ─── Tablet: collapse to 2 columns (sessions + transcript) ──────────────────
   The projects pane becomes an off-canvas drawer toggled by the same back
   button, which here reads "Projetos" and opens the drawer instead of doing a
   level change. */
@media (max-width: 960px) and (min-width: 641px) {
  body {
    grid-template-columns: 300px 1fr;
  }
  #projects {
    position: fixed;
    top: 0;
    bottom: 0;
    left: -290px;
    width: 280px;
    z-index: 150;
    transition: left 0.2s ease;
    box-shadow: 2px 0 16px rgba(0, 0, 0, 0.18);
  }
  body[data-drawer="open"] #projects { left: 0; }
  /* Backdrop behind the open drawer. */
  body[data-drawer="open"]::before {
    content: "";
    position: fixed;
    inset: 0;
    background: rgba(0, 0, 0, 0.35);
    z-index: 140;
  }
  #sessions { grid-column: 1; }
  #transcript { grid-column: 2; }
  /* The sessions header gets the drawer toggle. */
  #back-to-projects { display: inline-flex; align-items: center; }
}

/* ─── Phone: one pane at a time (drill-down) ─────────────────────────────────
   Projetos → Sessões → Transcrição/Terminal. body[data-mobile-level] decides
   which single pane is on screen; JS flips it as the user drills in / backs out. */
@media (max-width: 640px) {
  body {
    grid-template-columns: 1fr;
    grid-template-rows: 1fr auto;
  }
  /* Every pane stacks into the single column / single row; only the active one
     is shown via the level attribute below. */
  #projects, #sessions, #transcript {
    grid-column: 1;
    grid-row: 1;
    border-right: none;
  }
  #projects, #sessions, #transcript { display: none; }
  body[data-mobile-level="projects"] #projects { display: flex; }
  body[data-mobile-level="sessions"] #sessions { display: flex; }
  body[data-mobile-level="transcript"] #transcript { display: flex; }

  /* Back buttons drive the drill-up. */
  body[data-mobile-level="sessions"] #back-to-projects { display: inline-flex; align-items: center; }
  body[data-mobile-level="transcript"] #back-to-sessions { display: inline-flex; align-items: center; }

  /* Headers: keep title + count/back on one tidy row. */
  .pane > header { flex-wrap: wrap; row-gap: 6px; padding: 12px 14px; }
  #transcript > header { gap: 6px; }
  /* O header já é sempre em coluna; o antigo flex-basis:100% (para forçar a quebra
     num layout em row) deixou de ser necessário e num flex-column mexeria na altura. */
  .transcript-head-text { flex-basis: auto; }
  /* The grouped controls wrap under the title on a narrow screen — nudge the
     whole pill (not the cells inside it, which must stay aligned). */
  .seg-group { margin-top: 2px; }

  /* Modals + global search: hug the viewport edges. */
  .modal { width: calc(100% - 28px); max-width: none; }

  /* Tab bars (#cfg-tabs, .modal-tabs): at this width six flex:1 tabs squeeze
     labels like "Notificações" past readability. Let the bar itself scroll
     horizontally instead — each tab keeps its natural width, the rest of the
     modal body already reflows fine at this breakpoint. */
  #cfg-tabs, .modal-tabs {
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  #cfg-tabs::-webkit-scrollbar, .modal-tabs::-webkit-scrollbar { display: none; }
  #cfg-tabs .tab, .modal-tabs .tab { flex: 0 0 auto; white-space: nowrap; }

  .gsearch-overlay { padding-top: calc(6vh / var(--ui-zoom, 1)); }
  .gsearch { width: calc(100% - 24px); max-width: none; max-height: calc(82vh / var(--ui-zoom, 1)); }
  #gsearch-input { font-size: 16px; } /* keeps iOS from zooming on focus */

  /* Cost summary + messages use the full width. */
  .cost-summary, .msg { max-width: none; }
  #transcript-body { padding: 16px 12px; }

  /* msg-nav: smaller and lifted above the usage bar so it never traps content
     or sits under the on-screen keyboard. */
  .msg-nav { bottom: 14px; right: 12px; gap: 4px; }
  .msg-nav-btn { width: 34px; height: 34px; opacity: 0.85; }

  /* Usage bar: allow it to wrap rather than clip on a tiny width. */
  .usage-bar { gap: 10px 14px; flex-wrap: wrap; white-space: normal; padding: 6px 12px; }
  .usage-track { width: 64px; }

  /* …e desaparece por completo sobre o terminal ao vivo: aqui a vista Terminal
     ocupa o ecrã inteiro (uma pane de cada vez) e, por poder envolver duas ou
     três linhas, a barra tirava ao tmux a altura que ele mede em linhas — cada
     linha perdida conta. Regra derivada do DOM (`:has`) e não de um atributo
     em JS, porque quem esconde/mostra o #terminal-view é o paneShow e o
     clearTranscriptPane não passa por lá: um atributo ficaria desactualizado.
     O guarda do nível é necessário — voltar a "Sessões" não desmonta o
     terminal, só esconde a pane que o contém. */
  body[data-mobile-level="transcript"]:has(#terminal-view:not(.hidden)) .usage-bar { display: none; }

  /* Header actions stay compact. */
  .header-actions { gap: 4px; }
}

.tag-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 4px;
  margin-top: 4px;
}
.tag-chip {
  font-size: 11px;
  line-height: 1;
  padding: 3px 7px;
  border-radius: 999px;
  color: #fff;
  background: var(--tag, #666);
  white-space: nowrap;
}

.tag-popover {
  position: fixed;
  z-index: 300;
  width: 220px;
  background: var(--pane);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 12px 32px rgba(0, 0, 0, 0.28);
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}
.tag-popover.busy { pointer-events: none; opacity: 0.6; }
.tag-pop-list {
  display: flex;
  flex-direction: column;
  max-height: 240px;
  overflow-y: auto;
}
.tag-pop-item {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 5px 6px;
  border-radius: 6px;
  cursor: pointer;
  font-size: 12px;
}
.tag-pop-item:hover { background: var(--accent-soft); }
.tag-pop-item input { margin: 0; cursor: pointer; }
.tag-pop-swatch {
  width: 12px;
  height: 12px;
  border-radius: 50%;
  flex: 0 0 auto;
}
.tag-pop-name { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* A linha do popover tem duas zonas: o alvo grande de ligar/desligar e o ⋯ dos
   verbos do vocabulário. O ⋯ é sempre visível — revelá-lo em :hover deixava-o
   inexistente em ecrã táctil. */
.tag-pop-row { display: flex; align-items: center; gap: 2px; }
.tag-pop-row .tag-pop-item { flex: 1; min-width: 0; }
.tag-pop-more { width: 24px; height: 24px; flex: 0 0 auto; border-color: transparent; background: none; }
.tag-pop-more:hover { border-color: var(--border); background: var(--bg); }
.tag-pop-empty {
  font-size: 11px;
  color: var(--muted);
  padding: 6px;
}
.tag-pop-new {
  border-top: 1px solid var(--border, #333);
  padding-top: 6px;
}
.tag-pop-new .tag-input { width: 100%; }
.tag-input {
  font-size: 11px;
  border: 1px solid var(--border, #444);
  border-radius: 999px;
  padding: 2px 8px;
  background: transparent;
  color: inherit;
  width: 80px;
}

#tag-filter-bar {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  padding: 6px 16px;
  border-bottom: 1px solid var(--border, #333);
}
#tag-filter-bar.hidden { display: none; }
.gsearch-datebar {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
  padding: 8px 16px;
  border-bottom: 1px solid var(--border, #333);
  font-size: 11px;
  color: var(--muted);
}
.gsearch-datebar.active {
  background: var(--accent-soft);
}
.date-filter-icon { font-size: 13px; line-height: 1; }
.date-filter-sep { color: var(--muted); }
.gsearch-datebar input[type="date"] {
  font-size: 11px;
  font-family: inherit;
  padding: 2px 6px;
  border-radius: 6px;
  border: 1px solid var(--border, #555);
  background: var(--bg);
  color: inherit;
  color-scheme: light dark;
}
.date-clear {
  font-size: 11px;
  padding: 2px 7px;
  border-radius: 999px;
  border: 1px solid var(--border, #555);
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.date-clear.hidden { display: none; }
.tag-filter-chip {
  font-size: 11px;
  padding: 3px 8px;
  border-radius: 999px;
  border: 1px solid var(--tag, #666);
  color: var(--tag, #ccc);
  background: transparent;
  cursor: pointer;
}
.tag-filter-chip.active {
  background: var(--tag, #666);
  color: #fff;
}
.tag-mode {
  font-size: 11px;
  font-weight: 700;
  padding: 3px 9px;
  border-radius: 999px;
  border: 1px solid var(--border, #555);
  background: transparent;
  color: inherit;
  cursor: pointer;
}
.label-manager { display: flex; flex-direction: column; gap: 10px; }
/* Grid em vez de flex-wrap: à largura normal do modal o conteúdo (chip + lápis +
   paleta + hex + lixo) não cabia numa linha e quebrava a meio — chip+lápis numa
   linha, paleta+hex+lixo noutra, tornando ambíguo a qual etiqueta pertencia o
   lixo. Áreas nomeadas fixam chip/lápis/hex/lixo numa linha de topo estável; só
   a paleta (que pode encolher — os swatches embrulham dentro da própria célula)
   cede espaço. O border+bg agrupa visualmente cada etiqueta como um bloco único,
   mesmo quando a paleta acaba por cair para uma segunda linha em ecrãs estreitos. */
.label-row {
  display: grid;
  grid-template-columns: auto auto minmax(60px, 1fr) auto auto;
  grid-template-areas: "chip rename palette custom trash";
  align-items: center;
  gap: 8px 10px;
  padding: 8px 10px;
  border: 1px solid var(--border, #333);
  border-radius: 8px;
  background: var(--input-surface, var(--bg));
}
.label-row .tag-chip { grid-area: chip; min-width: 80px; text-align: center; }
.label-row .label-rename { grid-area: rename; }
.label-row .pal-grid { grid-area: palette; min-width: 0; }
.label-row .pal-custom { grid-area: custom; }
.label-row .label-forget { grid-area: trash; }
.pal-grid { display: flex; flex-wrap: wrap; gap: 4px; }
.pal-swatch {
  width: 18px; height: 18px; border-radius: 50%;
  border: 2px solid transparent; cursor: pointer; padding: 0;
}
.pal-swatch.active { border-color: var(--text, #fff); }
.pal-custom { display: inline-flex; align-items: center; gap: 6px; }
.pal-custom .pal-color {
  width: 26px; height: 26px; padding: 0; border: 1px solid var(--border, #333);
  border-radius: 6px; background: none; cursor: pointer;
}
.pal-custom .pal-hex { width: 88px; font-family: var(--mono, monospace); }
/* Herdam a caixa do .icon-btn (28×28, acima do mínimo de 24 do WCAG 2.5.8);
   aqui só fica a cor, que é o que os distingue. */
.label-rename { color: var(--muted); }
.label-rename:hover { color: var(--text); }
.label-rename-input { min-width: 60px; }
.label-new { display: flex; gap: 6px; margin-top: 6px; }
.label-new .tag-input { width: auto; flex: 1; }
/* Ecrã estreito: mesmo depois de a paleta encolher ao mínimo, chip+lápis+hex+lixo
   já não cabem numa só linha. Em vez de deixar o wrap acontecer ao acaso (o
   defeito original), força-se deliberadamente a paleta para uma segunda linha
   completa — chip/lápis/hex/lixo continuam juntos na primeira, e o bloco
   (border+bg de .label-row) deixa claro que ambas as linhas são da mesma etiqueta. */
@media (max-width: 640px) {
  .label-row {
    grid-template-columns: auto auto auto auto;
    grid-template-areas:
      "chip rename custom trash"
      "palette palette palette palette";
  }
}
/* Corner badge — absolutely positioned so the 15px icon stays centred at full size
   inside the 28px button (in-flow it competed for width and shrank the icon). */
.todos-count {
  position: absolute; top: -5px; right: -5px;
  display: inline-flex; align-items: center; justify-content: center;
  min-width: 15px; height: 15px; padding: 0 4px;
  border-radius: 8px; font: 700 9px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  background: var(--accent, #3b82f6); color: #fff;
  border: 1.5px solid var(--pane); box-sizing: content-box;
}
/* The explicit display above otherwise overrides the [hidden] attribute's
   display:none, so an empty backlog would still show a "0" badge. */
.todos-count[hidden] { display: none; }

/* A .btn that pairs an icon with a label (Adicionar, Abrir, …). */
.btn-icon { display: inline-flex; align-items: center; gap: 6px; }
.btn-icon svg { display: block; }

/* 🧠 Memória do projeto panel */
.mem-panel { display: flex; flex-direction: column; gap: 6px; }
.mem-score { display: flex; align-items: center; gap: 12px; padding: 4px 2px 12px; }
.mem-ring {
  width: 46px; height: 46px; border-radius: 50%;
  background: conic-gradient(var(--accent, #d08b3a) calc(var(--pct) * 1%), #2a2e37 0);
  display: flex; align-items: center; justify-content: center; flex: 0 0 auto;
}
.mem-ring span {
  width: 36px; height: 36px; border-radius: 50%; background: var(--panel, #16181d);
  display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 12px;
}
.mem-score-meta { font-size: 13px; line-height: 1.5; }
.mem-rows { display: flex; flex-direction: column; }
.mem-row { display: flex; align-items: center; gap: 10px; padding: 11px 2px; border-bottom: 1px solid #23272f; }
.mem-row:last-child { border-bottom: none; }
.mem-main { flex: 1; min-width: 0; }
.mem-name { font-size: 13px; font-weight: 600; display: flex; align-items: center; gap: 8px; }
.mem-sub { font-size: 11px; color: #8a8f99; margin-top: 2px; }
.mem-pill { font-size: 10px; font-weight: 700; padding: 3px 8px; border-radius: 20px; white-space: nowrap; }
.mem-ok { background: #16331f; color: #5fd089; }
.mem-warn { background: #3a2f12; color: #e0b24a; }
.mem-bad { background: #3a1a1a; color: #ef6b6b; }
.mem-delg { background: #1f2733; color: #6aa3e0; }
.mem-unknown { background: #2a2e37; color: #9aa0ab; }
.mem-treat { font-size: 12px; white-space: nowrap; }
.mem-footer { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-top: 12px; padding-top: 12px; border-top: 1px solid #2a2e37; }
.mem-foot-note { margin-top: 8px; }

/* --- Workspace files: links, file chips, preview, terminal drop -------------- */

/* A: transcript links open in a new tab — mark them with a small ↗. */
.msg-body a[target="_blank"]::after,
.fp-md a[target="_blank"]::after { content: "↗"; font-size: 0.78em; margin-left: 2px; opacity: 0.6; }

/* B: file chip rendered for each Write/Edit the claude run produced. */
.filechip {
  display: flex; align-items: center; gap: 10px;
  background: var(--bg); border: 1px solid var(--border); border-radius: 9px;
  padding: 7px 9px; margin: 8px 0 0; font-family: system-ui, -apple-system, sans-serif;
}
.filechip .fc-ic {
  width: 30px; height: 30px; flex: none; border-radius: 7px;
  display: grid; place-items: center; background: var(--accent-soft); color: var(--accent);
}
.filechip .fc-meta { min-width: 0; flex: 1; }
.filechip .fc-fn {
  font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px; color: var(--text);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.filechip .fc-det { font-size: 11px; color: var(--muted); }
.filechip .fc-acts { display: flex; gap: 6px; flex: none; }
.filechip.pending .fc-acts { opacity: 0.4; pointer-events: none; }
.filechip.missing { opacity: 0.55; }
.filechip.missing .fc-acts { display: none; }
.fc-acts .fc-dl { text-decoration: none; }

/* Inline image thumbnail (sits right after its chip). */
.fc-thumb {
  display: block; margin: 8px 0 0; max-width: min(320px, 100%); max-height: 220px;
  border: 1px solid var(--border); border-radius: 9px; cursor: zoom-in; object-fit: contain;
}

/* B: file preview in the modal shell. */
.fp-bar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.fp-bar .fp-size { font-size: 12px; color: var(--muted); }
.fp-bar .btn { margin-left: auto; }
.fp-img { text-align: center; }
.fp-img img { max-width: 100%; max-height: calc(70vh / var(--ui-zoom, 1)); border-radius: 8px; }
.fp-pre {
  margin: 0; padding: 12px 14px; background: var(--code-bg); color: var(--code-text);
  border-radius: 8px; font-size: 12.5px; max-height: calc(65vh / var(--ui-zoom, 1)); overflow: auto; white-space: pre-wrap;
  word-break: break-word;
}
.fp-md { max-height: calc(65vh / var(--ui-zoom, 1)); overflow: auto; }

/* C: terminal drop target highlight. */
.term-host { position: relative; }
.term-host.drag-over { outline: 2px dashed var(--accent); outline-offset: -6px; }
.term-host.drag-over::after {
  content: "⇣  Larga para partilhar com a sessão"; position: absolute; inset: 0;
  display: grid; place-items: center; pointer-events: none;
  background: color-mix(in srgb, var(--accent) 12%, transparent);
  color: var(--accent); font: 600 14px system-ui, -apple-system, sans-serif; z-index: 5;
}
#terminal-host { position: relative; }

/* --- Artifacts tray v2: master-detail ---------------------------------------- */
.modal.xwide { max-width: 820px; }
.a2 { display: grid; grid-template-columns: 200px 1fr; height: min(calc(66vh / var(--ui-zoom, 1)), 560px); }
.a2-side {
  border-right: 1px solid var(--border); background: var(--pane2);
  display: flex; flex-direction: column; padding: 10px; gap: 2px; overflow: auto;
}
.a2-cat {
  display: flex; align-items: center; gap: 10px; padding: 9px 10px; border-radius: 9px;
  cursor: pointer; color: var(--muted); font-size: 13px; border: 1px solid transparent;
  background: transparent; text-align: left; width: 100%;
}
.a2-cat .a2-ci { display: flex; color: var(--faint); }
.a2-cat .a2-cl { flex: 1; }
.a2-cat .a2-cc {
  font-size: 11px; background: var(--bg); border: 1px solid var(--border);
  border-radius: 20px; padding: 0 7px; color: var(--muted);
}
.a2-cat:hover { background: var(--bg); }
.a2-cat.on { background: var(--accent-soft); border-color: var(--accent); color: var(--text); }
.a2-cat.on .a2-ci { color: var(--accent); }
.a2-grow { flex: 1; }
.a2-all { width: 100%; justify-content: center; margin-top: 8px; }

.a2-main { display: flex; flex-direction: column; min-width: 0; }
.a2-toolbar { display: flex; align-items: center; gap: 8px; padding: 11px 13px; border-bottom: 1px solid var(--border); }
.a2-search {
  flex: 1; display: flex; align-items: center; gap: 7px; min-width: 0;
  background: var(--bg); border: 1px solid var(--border); border-radius: 8px; padding: 6px 9px; color: var(--muted);
}
.a2-search input { border: 0; background: transparent; color: var(--text); outline: none; width: 100%; font-size: 12.5px; }
.a2-tbtn {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px; white-space: nowrap;
  border: 1px solid var(--border); background: var(--bg); color: var(--muted); border-radius: 8px; padding: 6px 9px; cursor: pointer;
}
.a2-tbtn.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

.a2-chips { display: flex; gap: 7px; padding: 9px 13px 2px; flex-wrap: wrap; }
.a2-chip {
  font-size: 11.5px; border: 1px solid var(--border); background: var(--bg); color: var(--muted);
  border-radius: 20px; padding: 3px 10px; cursor: pointer;
}
.a2-chip.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }

.a2-list { flex: 1; overflow: auto; padding: 9px 13px 13px; }
.a2-row {
  display: flex; align-items: center; gap: 10px; padding: 7px 9px;
  border: 1px solid var(--border); border-radius: 9px; background: var(--bg); margin-bottom: 7px;
}
.a2-row.sel { border-color: var(--accent); background: var(--accent-soft); }
.a2-ck {
  width: 17px; height: 17px; flex: none; border-radius: 5px; border: 1.5px solid var(--border);
  display: grid; place-items: center; color: #1a1208; font-size: 11px; cursor: pointer;
}
.a2-ck.on { background: var(--accent); border-color: var(--accent); }
.a2-ric { width: 30px; height: 30px; flex: none; border-radius: 7px; display: grid; place-items: center; background: var(--accent-soft); color: var(--accent); }
.a2-ric.code { background: var(--user-bg); color: var(--user-accent); }
.a2-ric.link { background: var(--user-bg); color: var(--user-accent); }
.a2-thumb { width: 34px; height: 34px; flex: none; border-radius: 7px; object-fit: cover; border: 1px solid var(--border); background: var(--tool-bg); }
.a2-m { min-width: 0; flex: 1; }
.a2-fn { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.a2-fn.lk { color: var(--user-accent); }
.a2-d { font-size: 11px; color: var(--faint); }
.a2-acts { display: flex; gap: 5px; flex: none; }
.a2 .mini {
  width: 27px; height: 27px; border-radius: 7px; border: 1px solid var(--border);
  background: transparent; color: var(--muted); display: grid; place-items: center; cursor: pointer; text-decoration: none;
}
.a2 .mini:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.a2 .mini.danger:hover { border-color: var(--danger); color: var(--danger); background: var(--danger-soft); }
.a2 .mini.ok { border-color: var(--success, #3a8a4f); color: var(--success, #3a8a4f); }
.a2-empty { padding: 14px 4px; }

.a2-bulk { display: flex; align-items: center; gap: 10px; padding: 10px 13px; border-top: 1px solid var(--border); background: var(--pane2); }
.a2-bn { font-size: 12.5px; color: var(--text); }
.a2-sp { flex: 1; }

.a2-pvhead { display: flex; align-items: center; gap: 9px; padding: 11px 13px; border-bottom: 1px solid var(--border); }
.a2-back { display: inline-flex; align-items: center; gap: 6px; font-size: 12.5px; color: var(--muted); background: transparent; border: 1px solid var(--border); border-radius: 8px; padding: 6px 9px; cursor: pointer; }
.a2-back:hover { border-color: var(--accent); color: var(--accent); }
.a2-pf { font-family: ui-monospace, "SF Mono", Menlo, monospace; font-size: 12.5px; flex: 1; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.a2-pvbody { flex: 1; overflow: auto; padding: 15px; }
.a2-pvimg { display: block; max-width: 100%; max-height: calc(60vh / var(--ui-zoom, 1)); margin: 0 auto; border-radius: 8px; }
.a2-pvfill { min-height: 100%; }

@media (max-width: 700px) {
  /* Stack as flex (like .bk2). The grid kept a fixed height that squeezed the
     category row into a broken clipped strip — flex-column + wrap fixes it. */
  .a2 { display: flex; flex-direction: column; height: auto; max-height: calc(80vh / var(--ui-zoom, 1)); }
  .a2-side {
    flex-direction: row; flex-wrap: wrap; gap: 6px; overflow: visible;
    border-right: 0; border-bottom: 1px solid var(--border); padding: 8px;
  }
  .a2-cat { width: auto; white-space: nowrap; }
  .a2-cat .a2-ci { display: none; }
  .a2-grow, .a2-all { display: none; }
  .a2-main { flex: 1; min-height: 0; }
  /* The 5 per-row actions squeezed the filename to a few chars — drop the action
     cluster to its own line and give the icon+name the full first line. */
  .a2-row { flex-wrap: wrap; }
  .a2-acts { flex-basis: 100%; justify-content: flex-end; margin-top: 2px; }
  .a2 .mini { width: 34px; height: 34px; }
}

/* --- Attention: live indicator / status pills / sounds ----------------------- */
@keyframes friday-attn-pulse {
  0%, 100% { box-shadow: 0 0 0 0 rgba(224, 169, 63, 0.5); }
  50% { box-shadow: 0 0 0 5px rgba(224, 169, 63, 0); }
}
/* ⚡ when a session needs you: amber + pulsing badge. */
.live-btn.live-attn, .rail-live.live-attn { border-color: var(--amber, #e0a93f); color: var(--amber, #e0a93f); }
.live-btn.live-attn .live-count, .rail-live.live-attn .rail-live-count {
  background: var(--amber, #e0a93f); color: #241a06; animation: friday-attn-pulse 1.2s ease-in-out infinite;
}
#sound-btn.muted { color: var(--muted); opacity: 0.7; }

/* Terminal status dot: activity states from the registry. */
.tdot.busy { background: var(--success, #5fbf7a); }
.tdot.wait { background: var(--amber, #e0a93f); animation: friday-attn-pulse 1.2s ease-in-out infinite; }

/* Live-modal: per-row state pill + waiting highlight. */
.live-row.attn { border-radius: 9px; background: var(--amber-soft, #3a2f17); box-shadow: inset 0 0 0 1px rgba(224,169,63,0.4); }
/* Shrinkable so a long "à espera · <motivo>" truncates instead of pushing the
   row's action buttons out of place / overlapping (esp. in the narrow mobile modal). */
.live-pill { display: inline-flex; align-items: center; gap: 6px; font-size: 11px; border-radius: 20px; padding: 2px 9px; border: 1px solid var(--border); flex: 0 1 auto; min-width: 0; max-width: 45%; }
.live-pill .lp-d { width: 7px; height: 7px; border-radius: 50%; flex: none; }
.live-pill .lp-t { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.live-pill.busy { color: var(--success, #5fbf7a); border-color: #2c5c3c; background: rgba(95,191,122,0.1); }
.live-pill.busy .lp-d { background: var(--success, #5fbf7a); }
.live-pill.wait { color: var(--amber, #e0a93f); border-color: #5c4a23; background: var(--amber-soft, #3a2f17); }
.live-pill.wait .lp-d { background: var(--amber, #e0a93f); }
.live-pill.idle { color: var(--muted); background: var(--pane2, #2a2823); }
.live-pill.idle .lp-d { background: var(--faint, #7c756a); }
.lg-attn { font-size: 10.5px; color: var(--amber, #e0a93f); background: var(--amber-soft, #3a2f17); border: 1px solid #5c4a23; border-radius: 20px; padding: 0 7px; margin-right: 6px; }
.live-attn-bar { margin: 0 0 10px; padding: 8px 11px; border-radius: 9px; font-size: 12.5px; color: var(--amber, #e0a93f); background: var(--amber-soft, #3a2f17); border: 1px solid #5c4a23; }

/* Painel Acesso remoto (🌐) — config */
.tp-url { font-size: 12px; color: var(--accent); text-decoration: none; word-break: break-all; }
.tp-url:hover { text-decoration: underline; }
.tp-note { font-size: 12px; color: var(--muted); }
.tp-sep { border: 0; border-top: 1px solid var(--border); margin: 10px 0 4px; }
.tp-inwrap, .mh-inwrap { display: flex; gap: 6px; align-items: center; }
.tp-inwrap .tag-input, .mh-inwrap .tag-input { flex: 1 1 auto; }
.tp-inwrap .icon-btn, .mh-inwrap .icon-btn { flex: 0 0 auto; }
.icon-btn.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent); }

/* Gate de acesso remoto: secção separada e claramente rotulada — o segredo do
   gate (FRIDAY_TUNNEL_SECRET) não é o Token do provider. Estados theme-aware. */
.tp-gate-label { display: block; font-weight: 600; margin-top: 4px; }
.launch-note.ok { color: var(--success); }
.launch-note.warn { color: var(--amber); }
/* Pip de estado por aba do painel do túnel (guiado, não trancado): neutro = leve
   ponto de currentColor; verde = passo satisfeito; âmbar = precisa de ação. */
.tp-tab-pip {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-left: 6px; vertical-align: middle;
  background: currentColor; opacity: .3;
}
.tp-tab-pip.ok { background: var(--success); opacity: 1; }
.tp-tab-pip.warn { background: var(--amber); opacity: 1; }
/* QR do link de enroll da passkey. Fundo branco fixo + margem (quiet zone) para
   ler bem mesmo em tema escuro; pixelated mantém os módulos nítidos ao escalar. */
.tp-passkey-qr {
  display: block;
  width: 220px; height: 220px; max-width: 100%;
  margin: 8px auto 4px;
  background: #fff; border-radius: 8px;
  image-rendering: pixelated;
}

/* --- Aba Máquinas (federação multi-host) ------------------------------------ */
.mh-section { margin: 2px 0; }
.mh-h { font-size: 12.5px; color: var(--text); font-family: var(--sans, inherit); }
.mh-warn { color: var(--amber, #e0a93f); }
.mh-list { list-style: none; margin: 4px 0 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.mh-empty { margin: 2px 0 0; }
.mh-tok { display: flex; align-items: center; gap: 8px; border: 1px solid var(--border); border-radius: 9px; padding: 6px 9px; background: var(--bg); }
.mh-tok-label { flex: 1 1 auto; font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.mh-tok-value { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 12px; }
.mh-host { border: 1px solid var(--border); border-radius: 11px; padding: 10px 11px; background: var(--bg); display: flex; flex-direction: column; gap: 6px; margin-bottom: 8px; }
.mh-host-key { margin: 0; font-family: ui-monospace, SFMono-Regular, Menlo, monospace; opacity: 0.8; }
.mh-add { align-self: flex-start; }

/* --- Acesso remoto: control (status + power toggle) -------------------------- */
.tp-control { border: 1px solid var(--border); border-radius: 11px; background: var(--bg); padding: 12px 13px; }
.tp-control.is-on { border-color: color-mix(in srgb, var(--success, #5fbf7a) 45%, var(--border)); }
.tp-control.is-conn { border-color: color-mix(in srgb, var(--amber, #e0a93f) 45%, var(--border)); }
.tp-ctl-head { display: flex; align-items: center; gap: 11px; }
.tp-ctl-ic { width: 34px; height: 34px; border-radius: 9px; flex: none; display: grid; place-items: center; background: var(--pane2); color: var(--faint); }
.tp-control.is-on .tp-ctl-ic { background: rgba(95, 191, 122, 0.14); color: var(--success, #5fbf7a); }
.tp-control.is-conn .tp-ctl-ic { background: var(--amber-soft, #3a2f17); color: var(--amber, #e0a93f); }
.tp-ctl-tt { flex: 1; min-width: 0; font-size: 14px; }
.tp-ctl-tt strong { font-weight: 600; }
.tp-ctl-prov { color: var(--muted); font-weight: 400; font-size: 12px; }
.tp-power { width: 40px; height: 40px; border-radius: 50%; flex: none; display: grid; place-items: center; cursor: pointer; border: 2px solid #4a463d; color: var(--faint); background: none; transition: border-color .15s, color .15s, box-shadow .15s; }
.tp-power:hover { filter: brightness(1.15); }
.tp-power.is-on { border-color: var(--success, #5fbf7a); color: var(--success, #5fbf7a); background: radial-gradient(circle at 50% 40%, rgba(95, 191, 122, 0.18), transparent 70%); box-shadow: 0 0 0 5px rgba(95, 191, 122, 0.08); }
.tp-power.is-conn { border-color: var(--amber, #e0a93f); color: var(--amber, #e0a93f); animation: friday-attn-pulse 1.2s ease-in-out infinite; }
.tp-power:disabled { cursor: default; }
.tp-ctl-url { display: flex; align-items: center; gap: 6px; margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border); }
.tp-ctl-url a { flex: 1; min-width: 0; display: flex; align-items: center; gap: 6px; color: var(--user-accent, #6fb3e0); text-decoration: none; font-family: ui-monospace, Menlo, monospace; font-size: 12px; }
.tp-ctl-url a span { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.tp-url-copy { width: 28px; height: 28px; flex: none; }
.tp-url-copy.ok { color: var(--success, #5fbf7a); border-color: var(--success, #5fbf7a); }
.tp-control .tp-note { margin-top: 8px; font-size: 11.5px; color: var(--amber, #e0a93f); }

/* Pull-to-refresh indicator: grows as the list is dragged down on touch. */
.ptr-ind {
  height: 0;
  overflow: hidden;
  flex: none;
  display: flex;
  align-items: center;
  justify-content: center;
  color: var(--muted);
  transition: height 0.18s ease;
}
.ptr-spin {
  width: 18px;
  height: 18px;
  border: 2px solid var(--border);
  border-top-color: var(--accent);
  border-radius: 50%;
}
.ptr-ind.ready .ptr-spin { border-top-color: var(--accent-strong); transform: rotate(180deg); transition: transform 0.15s; }
.ptr-ind.loading .ptr-spin { animation: friday-spin 0.7s linear infinite; }

/* Toasts (replace window.alert): transient, non-blocking, announced via aria-live.
   z-index above modals so an error during a modal flow is still seen. */
#toast-host {
  position: fixed;
  left: 50%;
  transform: translateX(-50%);
  bottom: calc(20px + env(safe-area-inset-bottom));
  z-index: 2000;
  display: flex;
  flex-direction: column;
  gap: 8px;
  align-items: center;
  pointer-events: none;
  max-width: min(calc(92vw / var(--ui-zoom, 1)), 460px);
}
.toast {
  pointer-events: auto;
  padding: 11px 15px;
  border-radius: 10px;
  font-size: 13px;
  line-height: 1.4;
  box-shadow: 0 6px 24px rgba(0, 0, 0, 0.22);
  border: 1px solid var(--border);
  background: var(--pane);
  color: var(--text);
  opacity: 0;
  transform: translateY(8px);
  transition: opacity 0.2s, transform 0.2s;
}
.toast.show { opacity: 1; transform: none; }
.toast-error { border-color: var(--danger); background: var(--danger-soft); color: var(--danger-strong); }

/* Safe-area: keep content clear of notch / home-bar on phones (viewport-fit=cover). */
body {
  padding: env(safe-area-inset-top) env(safe-area-inset-right) env(safe-area-inset-bottom) env(safe-area-inset-left);
}

/* ── Touch devices ───────────────────────────────────────────────
   On a touch screen there is no :hover, so any control revealed only on
   hover is unreachable. Reveal them all, enlarge tap targets (WCAG 2.2
   SC 2.5.8), and lift inputs to 16px so iOS doesn't zoom on focus.
   Placed last so it wins over the desktop hover rules. */
@media (hover: none), (pointer: coarse) {
  /* iOS zooms the page when a focused field is < 16px; force 16px on every
     text-entry control (overrides the denser desktop ID/class sizes). */
  input:not([type="checkbox"]):not([type="radio"]), select, textarea, .tag-input {
    font-size: 16px !important;
  }

  /* The composer replaces typing into xterm on touch (predictive-text duplication).
     Shown together with the key bar, behind the ⌨ toggle (#nav-keys → .open). */
  #term-composer.open { display: flex; }

  /* Group the input tools at the BOTTOM, by the soft keyboard: terminal output on
     top, then the tmux nav keys (Esc/Tab/Ctrl/arrows), then the composer. The key
     bar is before the host in the DOM, so reorder it here — otherwise the arrows
     sit far above the keyboard and read as "gone". */
  #terminal-view { display: flex; flex-direction: column; }
  #terminal-host { order: 1; }
  #term-keybar { order: 2; border-bottom: none; border-top: 1px solid var(--border); }
  #term-composer { order: 3; }

  /* When the bottom dock (key bar + composer) is open, raise the floating
     scroll-nav column above it so it no longer overlaps the → / "Enviar". */
  .msg-nav.dock-open { bottom: 124px; }

  /* hover-only controls → always visible */
  .item-actions { display: flex; }
  .list .item-actions { position: static; margin-top: 8px; background: none; padding: 0; }
  li[data-id] .proj-move { display: flex; }
  /* The copy-path / open-in-editor button is a desktop-only affordance (copying a
     path or opening VSCode is meaningless on a phone/tablet) — keep it hidden. */
  li[data-id] .proj-copy { display: none !important; }
  .list li .proj-star { opacity: 1; }
  /* Touch: the ★/▦ buttons grow to 32-44px, so the desktop 28px spacing makes
     them overlap. Re-space their right offsets and reserve room on both text
     lines so titles/badges never run under the cluster. */
  /* Mover-para-grupo sai do táctil, pelo MESMO argumento que já tirou daqui a
     .proj-copy: arrumar projetos em grupos é trabalho de secretária, não um
     gesto de telemóvel — e continua a existir no desktop e no menu ⋯. O que o
     tira não é a estética: são quatro alvos de 44px numa linha de 380px.
     Ver a medição no comentário abaixo. */
  li[data-id] .proj-move { display: none !important; }
  /* Indexar é uma ação real em táctil (ao contrário de copiar-caminho/abrir no
     editor, que ali não fazem sentido e ficam escondidos) — logo tem de estar
     sempre visível, nunca só em :hover. Ocupa o slot que a .proj-copy liberta. */
  li[data-id] .proj-index { display: flex; }
  li[data-id]:not(.project-empty) .proj-index { right: 52px; }
  /* O ⋯ herdou o slot exterior que era do arquivar (que agora vive lá dentro).
     Sem hover não há revelação: em táctil está SEMPRE visível, senão o menu —
     e com ele arquivar, localização e mudar-a-pasta — simplesmente não existe
     no telemóvel.
     ─────────────────────────────────────────────────────────────────────────
     O ESPAÇAMENTO AQUI É UMA CORRECÇÃO DE SEGURANÇA, não afinação estética.
     Com os slots antigos (móver 64 · indexar 118 · arquivar 172, ~32px cada), o
     botão mais exterior ocupava, numa linha de 380px (iPhone de 390), x∈[196,218]
     — e o centro da linha é 200. `document.elementFromPoint` devolvia mesmo o
     `.proj-arch`: um toque no MEIO de qualquer linha de projeto arquivava-a em
     vez de a abrir. Medido em todos os projetos da lista, não só num.
     A condição geral é: há sobreposição sempre que  largura ≤ 2 × (offset + 32).
     Com o slot exterior a 172 isso dava 408px — ou seja, todos os telemóveis.
     Com 44px de espaçamento e três botões (8 · 52 · 96) o limite cai para 256px,
     abaixo de qualquer ecrã real. Os 44 são também o alvo mínimo táctil, por
     isso as áreas de toque ficam encostadas sem se sobreporem.
     Se voltares a acrescentar um botão a esta faixa, refaz a conta. */
  li[data-id] .proj-more { display: flex; }
  li[data-id]:not(.project-empty) .proj-more { right: 96px; }
  li[data-id] .proj-more[hidden] { display: none; }
  /* Reserva de texto = alcance do botão exterior (96+32) + folga. */
  .list li:not(.project-empty) .item-title,
  .list li:not(.project-empty) .item-sub { padding-right: 136px; }
  .list li.project-empty .item-title { padding-right: 8px; }
  .group-head .gmore { opacity: 0.7; }
  .msg.assistant .mem-btn,
  .msg.user .fork-btn { opacity: 1; }

  /* Target size: standalone controls get 44×44 (room to spare) */
  .modal-close, .view-tab, #session-tabs .tab,
  .proj-star, .mem-btn, .fork-btn, .tp-power, .tp-url-copy, .msg-nav-btn {
    min-width: 44px; min-height: 44px;
  }
  /* Toolbar buttons are square 44×44 on touch so every icon button reads the
     same size (was 28-wide × 44-tall, which looked uneven). Toolbars wrap so a
     full row of squares never overflows the pane. */
  .icon-btn { min-width: 44px; min-height: 44px; }
  .header-actions, .shead-tools, .phead-title { flex-wrap: wrap; row-gap: 6px; }
  /* iOS zooms the page when focusing an input < 16px — pin text inputs to 16px
     on touch so focusing a field never triggers that jump. */
  input:not([type="checkbox"]):not([type="radio"]), select, textarea, .tag-input {
    font-size: 16px;
  }
  /* Dense in-row / inline controls: ≥32px (≥24 min + gap spacing exception) */
  .row-btn, .proj-move, .proj-copy, .proj-index, .proj-arch, .label-forget, .label-rename,
  .tag-mode, .date-clear, .pal-swatch, .a2-ck, .gmore, .tf-btn, .tag-pop-more {
    min-width: 32px; min-height: 32px;
  }
  .item-actions { gap: 8px; }
  .proj-star, .proj-move, .proj-copy, .proj-index, .proj-arch, .row-btn, .mem-btn, .fork-btn, .gmore {
    display: inline-flex; align-items: center; justify-content: center;
  }
  /* O ⋯ é um controlo isolado: 44×44 cheios (WCAG 2.5.8), não os 32 dos densos.
     Depois das regras acima de propósito — .proj-arch/.proj-copy continuam a
     casar com os itens do menu e, com a mesma especificidade, ganha o último. */
  .proj-more { min-width: 44px; min-height: 44px; }
  .proj-menu { top: 56px; }
  .proj-menu-item { min-height: 44px; font-size: 14px; }
}

/* ─── Backlog panel v3 (lista única com secções + chips; base .bk2 mantida) ── */
.bk3-shell { height: 460px; max-height: calc(70vh / var(--ui-zoom, 1)); }
.bk3-toolbar {
  display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
  padding: 10px 13px; border-bottom: 1px solid var(--border);
}
.bk3-chip {
  font: inherit; font-size: 12px; font-weight: 600; padding: 4px 12px;
  border: 1px solid var(--border); border-radius: 999px;
  background: var(--input-surface, var(--bg)); color: var(--muted); cursor: pointer;
  transition: border-color 0.1s, color 0.1s, background 0.1s;
}
.bk3-chip:hover { border-color: var(--accent); color: var(--text); }
.bk3-chip.on { background: var(--accent-soft); border-color: var(--accent); color: var(--accent-strong); }
.bk3-score {
  margin-left: auto; font: 700 11px/1 ui-monospace, monospace;
  color: var(--success); border: 1px solid color-mix(in srgb, var(--success) 40%, var(--border));
  border-radius: 999px; padding: 5px 10px; white-space: nowrap;
}
.bk3-sec {
  display: flex; align-items: center; gap: 8px; margin: 10px 3px 2px;
  font: 700 10.5px/1 ui-monospace, monospace; text-transform: uppercase;
  letter-spacing: 0.08em; color: var(--muted);
}
.bk3-sec .bk3-ln { flex: 1; border-top: 1px solid var(--border); }
.bk3-layer, .bk3-src {
  font: 600 10px/1.6 inherit; text-transform: none; letter-spacing: 0;
  color: var(--muted); background: color-mix(in srgb, var(--text) 7%, transparent);
  border-radius: 8px; padding: 1px 8px; white-space: nowrap;
}
.bk3-dot { width: 9px; height: 9px; border-radius: 50%; flex: none; }
.bk3-dot.ok { background: var(--success); }
.bk3-dot.warn { background: var(--amber); }
.bk3-dot.bad { background: var(--danger); }
.bk3-dot.mut { background: var(--border); }
.bk3-act {
  position: relative;
  font: 600 11.5px/1 inherit; padding: 6px 12px; border-radius: 7px; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--accent) 45%, var(--border));
  background: var(--accent-soft); color: var(--accent-strong); white-space: nowrap;
}
.bk3-act:hover { border-color: var(--accent); }
.bk3-act.solid { background: var(--accent); border-color: var(--accent); color: var(--bg); }
.bk3-act.ghost { background: transparent; color: var(--muted); border-color: var(--border); }
.bk3-act.ghost:hover { color: var(--accent); border-color: var(--accent); }
.bk3-act:focus-visible, .bk3-live:focus-visible, .bk3-chip:focus-visible, .rc-lastrun:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.bk3-live {
  position: relative;
  font: 600 11px/1 inherit; padding: 6px 10px; border-radius: 7px; cursor: pointer;
  border: 1px solid color-mix(in srgb, var(--success) 45%, var(--border));
  background: color-mix(in srgb, var(--success) 12%, transparent); color: var(--success);
  white-space: nowrap;
}
/* Recontado com bordas: .bk3-act mede ~25.5px de altura (fonte 11.5px/1 +
   padding 6px+6px + borda 1px+1px) e .bk3-live ~25px (fonte 11px/1 + mesmo
   padding/borda) — ambos JÁ acima do mínimo de 24px (WCAG 2.5.8) antes deste
   ::before. O ::before fica por segurança/paridade com o .rc-lastrun::before
   logo abaixo (que esse sim precisa — ~18px sem ele), sem custo: alarga só a
   hit-area (invisível, position:absolute), o texto/fundo visíveis não mudam. */
.bk3-act::before, .bk3-live::before { content: ''; position: absolute; inset: -1px 0; }
/* Linha de plano arquivado: indentada sob o resumo que a revelou. */
.bk3-sub { padding-left: 22px; }
/* Mesmo visual do .bk3-src inerte (nunca corrido), mas clicável: abre a
   conversa de lastSid. Herda a base de .bk3-layer/.bk3-src acima. */
.rc-lastrun {
  position: relative;
  font: 600 10px/1.6 inherit; text-transform: none; letter-spacing: 0;
  color: var(--muted); background: color-mix(in srgb, var(--text) 7%, transparent);
  border: none; border-radius: 8px; padding: 1px 8px; white-space: nowrap; cursor: pointer;
}
.rc-lastrun:hover { color: var(--accent); background: var(--accent-soft); }
/* A pílula em si fica com ~18px de altura (font 10px/1.6 + padding 1px) —
   abaixo do alvo mínimo de 24px (WCAG 2.5.8). Em vez de esticar o padding
   visível (o que alargava a pílula), o ::before alarga só a hit-area,
   invisível, para os 24px — o texto e o fundo mantêm-se exactamente como
   estavam. Só verticalmente: horizontalmente já há folga de sobra. */
.rc-lastrun::before {
  content: ''; position: absolute; inset: -3px 0;
}
/* Um lançamento automático que rebentou é o sinal mais urgente da lista — sem
   isto, um item que não corre parece um item que ainda não chegou a hora. O
   texto já diz "falhou" (nunca só cor, WCAG 1.4.1); a cor + o peso mais forte
   são o reforço visual. Só `.bk3-src` — nunca `.rc-lastrun` (IMPORTANT 1 da
   revisão): uma falha nunca deixa a linha clicável, porque o `lastSid` que o
   botão abriria é sempre de uma corrida ANTERIOR bem-sucedida, nunca da que
   falhou. `font-weight: 700`, não 600: o `.bk3-src` base (acima) já fixa 600
   pelo shorthand `font:` — repetir 600 aqui era um no-op mascarado de reforço
   (achado MINOR 4 da revisão); 700 é o primeiro valor que de facto pesa mais
   que a base. */
.bk3-src.falhou { color: var(--danger); font-weight: 700; }
@media (hover: none), (pointer: coarse) {
  .bk3-act, .bk3-live, .bk3-chip, .rc-lastrun { min-height: 44px; }
  /* O .bk2-mini é 28×28 — acima do mínimo de 24 em rato, abaixo dos 44 exigidos
     em ecrã táctil, e o alvo tem de crescer nos DOIS eixos. Passou despercebido
     enquanto só o usavam os 🗑 do store antigo e das notas guardadas; com o
     duplicar/eliminar dos tópicos do registo, é o caminho principal que fica
     por baixo do alvo. O quadrado visual não muda (é o botão que cresce à volta
     do SVG de 15px). */
  .bk2-mini { min-height: 44px; min-width: 44px; }
  .ed-filter { min-height: 44px; }
  .ed-jump { min-height: 44px; min-width: 44px; }
}

/* ─── Painel "Ficheiros tocados" (reaproveita as classes bk2- e bk3-) ────── */
/* .modal-body tem white-space: pre-line (o confirmModal separa linhas com \n\n),
   por isso a INDENTAÇÃO do template literal vira quebras de linha a sério: sem
   isto cada linha media 101px em vez de 54. As duas linhas da linha são feitas
   com display:block, explicitamente, não com a indentação do código. */
.ed-row { white-space: normal; }
.ed-row .bk2-d { display: block; }
.ed-toolbar { flex-wrap: wrap; white-space: normal; }
.ed-filter {
  flex: 1 1 140px; min-width: 120px; padding: 5px 9px; border-radius: 7px;
  border: 1px solid var(--border); background: var(--bg); color: var(--fg);
  /* 16px: abaixo disto o iOS faz zoom ao focar o campo. */
  font-size: 16px;
}
@media (min-width: 640px) { .ed-filter { font-size: 12.5px; } }
.ed-path { display: block; overflow-wrap: anywhere; }
/* A pasta é contexto, o nome do ficheiro é a informação — daí o contraste. */
.ed-dir { color: var(--muted); font-weight: 400; }
.ed-base { color: var(--fg); font-weight: 600; }
.ed-when { white-space: nowrap; }
.ed-note { margin: 8px 2px 0; font-size: 11.5px; }

.bk2-act {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  padding: 8px 10px; border: 1px dashed var(--border); border-radius: 9px;
  background: transparent; color: var(--muted); cursor: pointer; font-size: 12.5px;
}
.bk2-act:hover { border-color: var(--accent); color: var(--accent); }
.bk2-act svg { width: 14px; height: 14px; flex: 0 0 auto; }

.bk2-main { flex: 1; display: flex; flex-direction: column; min-width: 0; }
/* .bk2-add nasceu para um único <input> + botão lado a lado (a caixa de
   criação do backlog); a toolbar "+ Novo recorrente" do painel de
   Recorrentes (S3.1, .rc-toolbar) reaproveita-a tal e qual, com um único
   botão lá dentro. A antiga caixa inline de criação (.rc-add — um
   <textarea> de 160px empilhado, porque em linha os três controlos ficavam
   esmagados) desapareceu com ela: criar passou a abrir o mesmo formulário
   de detalhe que editar. */
.bk2-add { display: flex; gap: 8px; padding: 11px 13px; border-bottom: 1px solid var(--border); }
.bk2-input {
  flex: 1; min-width: 0; background: var(--bg); border: 1px solid var(--border);
  border-radius: 8px; padding: 7px 10px; color: var(--text); outline: 0; font-size: 13px;
}
.bk2-input:focus { border-color: var(--accent); }
/* A 320px, .bk2-add mede ~234px: 26px de padding + 2 gaps de 8px + o clip fixo
   (34px) deixavam só a soma do campo com o botão "+ Tópico" (~158px), e o
   botão inteiro (ícone+texto+padding) já gastava a maior parte — o campo
   ficava a ~74px, a mostrar "new topi" cortado. Abaixo de 380px (largura onde
   o botão por extenso deixa de caber com folga) o botão perde o texto: só o
   ícone, com o nome acessível a sobreviver no `aria-label` (bkListHtml, porque
   o <span> era o único nome até aqui). Isso liberta ~65px de volta para o campo. */
@media (max-width: 380px) {
  #bk2-add-btn span { display: none; }
  #bk2-add-btn { padding: 7px 9px; }
}
/* O clip da caixa de criação de tópicos. Largura fixa porque .bk2-add é um flex
   e a 320px um terceiro controlo elástico esmagaria o campo de texto. */
.bk2-clip {
  width: 34px; height: 34px; flex: 0 0 auto; display: flex; align-items: center;
  justify-content: center; border: 1px solid var(--border); border-radius: 8px;
  background: var(--pane); color: var(--muted); cursor: pointer;
}
.bk2-clip:hover { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
.bk2-clip.on { border-color: var(--accent); color: var(--accent); background: var(--accent-soft); }
/* A tira de anexos em espera vive numa SEGUNDA linha: em linha com o campo, a
   320px, não sobrava largura para o texto do tópico. */
.bk2-att {
  display: flex; gap: 6px; flex-wrap: wrap; align-items: center;
  padding: 0 13px 11px; border-bottom: 1px solid var(--border);
}
.bk2-fchip {
  display: flex; align-items: center; gap: 6px; font-size: 11.5px; padding: 4px 4px 4px 9px;
  border-radius: 999px; border: 1px solid var(--accent); background: var(--accent-soft); color: var(--accent);
  max-width: 100%; min-width: 0;
}
.bk2-fname { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bk2-fsize { color: var(--muted); flex: 0 0 auto; }
/* .bk2-dfx (detalhe: anexos JÁ escritos na linha) partilha o visual de .bk2-fx
   (tira de anexos em espera) mas fica com classe própria de propósito — o
   handler genérico de .bk2-fx em wireBacklogPanel() assume bkPending/
   bkUploaded e um índice nessa base; se o botão do detalhe também respondesse
   a .bk2-fx, os dois handlers disparavam no mesmo clique e corrompiam as
   listas erradas. */
.bk2-fx, .bk2-dfx {
  position: relative;
  width: 24px; height: 24px; flex: 0 0 auto; display: flex; align-items: center; justify-content: center;
  border: 0; border-radius: 50%; background: transparent; color: var(--muted); cursor: pointer;
}
.bk2-fx:hover, .bk2-dfx:hover { color: var(--danger); }
.bk2-fbudget { font-size: 11px; color: var(--muted); margin-left: auto; }
/* Largar um ficheiro em cima do painel. A moldura é o feedback; o clip continua
   a ser a via principal (arrastar não existe em ecrã táctil). */
.bk-shell.drag-over { outline: 2px dashed var(--accent); outline-offset: -6px; background: var(--accent-soft); }
@media (hover: none), (pointer: coarse) {
  .bk2-clip { width: 44px; height: 44px; }
  /* .bk2-fx/.bk2-dfx ficam visualmente 24×24 dentro da pílula do chip — crescer
     o botão a sério para 44px (como esta regra fazia antes, só até 32px, já
     abaixo do mínimo) rebentava o chip. Em vez disso a hit-area invisível é
     que cresce, o mesmo padrão do .rc-lastrun::before/.bk3-act::before acima:
     -10px em cada lado de uma caixa de 24px dá exatamente 44×44 de alvo, sem
     alterar o visual. */
  .bk2-fx::before, .bk2-dfx::before { content: ''; position: absolute; inset: -10px; }
}
/* A secção "Anexos" da vista de detalhe: chips gravados + em espera + o clip. */
.bk2-datt { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-top: 4px; }
.bk2-datt .bk2-fname[href] { color: inherit; text-decoration: none; }
.bk2-datt .bk2-fname[href]:hover { text-decoration: underline; }
/* O subtítulo de anexos numa linha de tópico: ícone + contagem, alinhados. */
.bk2-d > svg { vertical-align: -2px; margin-right: 4px; }
.bk2-list { flex: 1; overflow: auto; padding: 8px 10px; display: flex; flex-direction: column; gap: 6px; }
.bk2-empty { padding: 18px 12px; text-align: center; }
.bk2-row {
  display: flex; align-items: center; gap: 10px; padding: 10px 11px;
  border: 1px solid var(--border); border-radius: 10px; background: var(--bg); cursor: pointer;
}
.bk2-row:hover { border-color: var(--accent); background: var(--accent-soft); }
.bk2-row.bk2-dim { cursor: default; opacity: 0.7; }
.bk2-row.bk2-dim:hover { border-color: var(--border); background: var(--bg); }
.bk2-ric {
  width: 30px; height: 30px; flex: 0 0 auto; border-radius: 8px;
  display: flex; align-items: center; justify-content: center; background: var(--pane);
}
.bk2-ric svg { width: 16px; height: 16px; }
.bk2-ric.man { color: var(--accent); }
.bk2-ric.mem { color: var(--amber); }
.bk2-ric.repo { color: var(--success); }
.bk2-ric.plan { color: var(--muted); }
.bk2-m { flex: 1; min-width: 0; }
.bk2-fn { font-size: 13.5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bk2-d { font-size: 11.5px; color: var(--muted); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* .bk2-side (painel de Recorrentes): "corrido há …" + "sem agenda" — sem
   regra nenhuma, um <label>/<span> é `inline` por omissão e os dois fluíam
   lado a lado na mesma linha. Coluna, alinhado à direita, gap mínimo (é o que
   o mockup do spec mostra). */
.bk2-side { display: flex; flex-direction: column; align-items: flex-end; gap: 3px; flex: 0 0 auto; }
/* .rc-targets/.rc-target (vista "Duplicar"): sem regra, os <label> das
   checkboxes ficavam `inline` por omissão e fluíam todos na mesma linha. */
.rc-targets { display: flex; flex-direction: column; gap: 8px; }
/* O <label> INTEIRO é o alvo clicável (activa a checkbox por semântica
   nativa) — o padding vertical sozinho já leva a caixa acima dos 24px do
   WCAG 2.5.8 sem alterar o texto. A checkbox em si nasce ~13px no browser
   (abaixo do mínimo); sobe para 18px — ainda discreta, mas visivelmente um
   alvo, não só a etiqueta ao lado a fazer o trabalho todo. */
.rc-target { display: flex; align-items: center; gap: 8px; font-size: 13px; padding: 5px 2px; cursor: pointer; }
.rc-target input[type="checkbox"] { width: 18px; height: 18px; flex: 0 0 auto; }
@media (hover: none), (pointer: coarse) { .rc-target { min-height: 44px; } }
.bk2-acts { display: flex; gap: 4px; opacity: 0; transition: opacity .12s; }
.bk2-row:hover .bk2-acts,
.bk2-row:focus-visible .bk2-acts,
.bk2-row:has(:focus-visible) .bk2-acts { opacity: 1; }
/* v3: a ação é a informação principal da linha — sempre visível (o redesign
   mata os controlos hover-only; ver rules/audit/ux.md sobre hover em touch). */
.bk3-row .bk2-acts { opacity: 1; }
.bk2-mini {
  width: 28px; height: 28px; border-radius: 7px; border: 1px solid transparent;
  background: transparent; color: var(--muted); cursor: pointer;
  display: flex; align-items: center; justify-content: center;
}
.bk2-mini:hover { background: var(--pane); color: var(--text); border-color: var(--border); }
.bk2-mini.danger:hover { color: var(--danger); border-color: var(--danger); }
.bk2-mini svg { width: 15px; height: 15px; }
/* v4: o toggle "Planos concluídos"/"voltar" perdeu o texto — usa o mesmo
   chevron ICON_CHEVRON_LEFT (aponta para a esquerda) rodado por CSS: -90°
   no estado recolhido lê-se como "para baixo" (expandir), +90° no estado
   expandido lê-se como "para cima" (recolher). A pista visual soma-se ao
   `aria-expanded` (que já não mudava a aparência sozinho). */
.bk3-plans-toggle svg { transition: transform .15s; transform: rotate(-90deg); }
.bk3-plans-toggle[aria-expanded="true"] svg { transform: rotate(90deg); }
.bk2-status { padding: 0 13px 8px; min-height: 14px; }

/* detail */
.bk2-detail { flex: 1; display: flex; flex-direction: column; min-width: 0; }
.bk2-dvhead { display: flex; align-items: center; gap: 10px; padding: 11px 13px; border-bottom: 1px solid var(--border); }
.bk2-back {
  border: 0; background: transparent; color: var(--muted); cursor: pointer;
  font-size: 13px; display: inline-flex; align-items: center; gap: 4px; padding: 4px 6px; border-radius: 7px;
}
.bk2-back:hover { color: var(--text); background: var(--accent-soft); }
.bk2-back svg { width: 15px; height: 15px; }
.bk2-pf { flex: 1; font-size: 13.5px; font-weight: 600; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bk2-pill { font-size: 11px; padding: 2px 9px; border-radius: 20px; background: var(--pane); color: var(--muted); border: 1px solid var(--border); flex: 0 0 auto; }
.bk2-pill.repo { color: var(--success); }
.bk2-pill.mem { color: var(--amber); }
.bk2-dbody { flex: 1; overflow: auto; padding: 14px 16px; }
/* display:block: a maioria dos usos é <p>, já block por omissão; desde o
   achado G1 dois usos passaram a <label for=…> (nome acessível para os
   campos do formulário de recorrentes) e o <label> nativo é inline — sem
   isto ficava colado ao campo seguinte, sem o espaçamento que o margin-bottom
   já pretendia dar. */
.bk2-label { display: block; font-size: 11px; letter-spacing: .05em; text-transform: uppercase; color: var(--muted); margin: 0 0 8px; }
.bk2-textarea {
  width: 100%; min-height: 160px; background: var(--bg); border: 1px solid var(--border);
  border-radius: 10px; color: var(--text); padding: 12px; outline: 0; resize: vertical;
  font: 13.5px/1.55 inherit;
}
.bk2-textarea:focus { border-color: var(--accent); }
/* Bloco de agenda (S3.2), dentro do formulário unificado de criar/editar um
   recorrente. `[data-cfg-hidden]` é o MESMO padrão do painel ⚙ (esconder sem
   desmontar, ver #cfg-panels em style.css) — trocar de frequência não perde
   o valor dos outros campos. */
.rc-sched { display: flex; flex-direction: column; gap: 10px; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.rc-sched-row { display: flex; align-items: center; gap: 10px; }
.rc-sched-row[data-cfg-hidden] { display: none; }
.rc-sched-row label { flex: 0 0 60px; font-size: 12px; color: var(--muted); }
.rc-sched-row select, .rc-sched-row input[type="time"] { flex: 1; min-width: 0; }
.rc-sch-preview { font-size: 12px; color: var(--muted); margin: 2px 0 0; min-height: 15px; }
/* Nota "hora da máquina" (achado G2) — mesmo padrão data-cfg-hidden da Hora
   que acompanha, escondida/mostrada em conjunto (ver syncScheduleVisibility). */
.rc-sch-hint { font-size: 11px; margin: -4px 0 0; }
.rc-sch-hint[data-cfg-hidden] { display: none; }
/* Campo com a agenda incompleta (achado G3) — a mesma cor que os toasts de
   erro usam, para o sinal ser reconhecível sem depender só do botão Guardar
   desativado, que não diz QUAL campo falta. */
.bk2-input[aria-invalid="true"] { border-color: var(--danger); }
/* .btn não tem regra `:disabled` nenhuma no resto do ficheiro (nem sequer
   `cursor` muda) — sem isto, desativar Guardar (achado G3) era invisível: o
   dono via o mesmo botão azul, com o mesmo cursor de "clicável", e não fazia
   ideia de que estava desativado. Fica escopado a ESTE botão (não um
   `.btn:disabled` geral) para não mudar o comportamento visual dos outros
   ~26 sítios do app que também desativam botões — isso é um achado à parte,
   não desta revisão. */
.rc-edit-save:disabled { opacity: .5; cursor: default; }
.bk2-gap {
  font-size: 13.5px; line-height: 1.6; color: var(--text);
  background: var(--bg); border: 1px solid var(--border); border-radius: 10px; padding: 13px 15px;
  white-space: pre-wrap;
}
.bk2-md { font-size: 13.5px; line-height: 1.6; }
.bk2-md h1, .bk2-md h2, .bk2-md h3 { margin: 14px 0 8px; }
.bk2-md h1:first-child, .bk2-md h2:first-child { margin-top: 0; }
.bk2-md ul, .bk2-md ol { padding-left: 22px; }
.bk2-md li { margin: 3px 0; }
.bk2-md code { background: var(--pane); padding: 1px 5px; border-radius: 5px; font-size: 12.5px; }
.bk2-md pre { background: var(--pane); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; overflow: auto; }
.bk2-dfoot { display: flex; align-items: center; gap: 8px; padding: 12px 16px; border-top: 1px solid var(--border); justify-content: flex-end; }
.bk2-note { flex: 1; font-size: 11.5px; color: var(--muted); }

@media (max-width: 720px) {
  .bk3-shell { height: auto; }
}

/* repo launch actions live under the repo list (contextual, not in the rail) */
.bk2-foot-acts { display: flex; gap: 8px; padding: 4px 12px 8px; flex-wrap: wrap; }
.bk2-foot-acts .bk2-act { width: auto; flex: 1 1 auto; justify-content: center; }
.bk2-dvhead .bk2-mini { opacity: 1; }

/* header · quick-lock button (only shown when local auth is active) */
.lock-btn { color: var(--accent); border-color: var(--accent-soft); background: var(--accent-soft); }
.lock-btn:hover { background: var(--accent); color: var(--bg); border-color: var(--accent); }

/* projects-header overflow menu: group headings + separators */
.overflow-menu .ov-group { font-size: 10px; letter-spacing: .08em; text-transform: uppercase; color: var(--muted); padding: 7px 10px 3px; }
.overflow-menu .ov-sep { border-top: 1px solid var(--border); margin: 5px 2px; }

/* The projects pane is the left-most, narrow (260px) column. Anchor its overflow
   menu to the header box (not the ⋯ button, which sits mid-row) so it opens
   aligned inside the column instead of off the left edge of the screen. */
#projects > header { position: relative; }
#projects .overflow-wrap { position: static; }
#projects-overflow { left: 12px; right: 12px; min-width: 0; }

/* "Acesso e segurança" panel */
.acc-panel { display: flex; flex-direction: column; }
.acc-status { display: flex; align-items: center; gap: 10px; padding: 11px 13px; border: 1px solid var(--border); border-radius: 11px; background: var(--bg); }
.acc-status .acc-dot { width: 32px; height: 32px; border-radius: 9px; display: grid; place-items: center; flex: 0 0 auto; }
.acc-status.on .acc-dot { background: var(--accent-soft); color: var(--accent); }
.acc-status.off .acc-dot { background: var(--pane); color: var(--muted); }
.acc-status .acc-t { flex: 1; min-width: 0; }
.acc-status .acc-t b { display: block; font-size: 13.5px; }
.acc-status .acc-t span { font-size: 12px; color: var(--muted); }
.acc-lockbig { width: 100%; justify-content: center; background: var(--accent-soft); border-color: var(--accent); color: var(--accent); font-weight: 700; margin-top: 12px; }
.acc-lockbig:hover { background: var(--accent); color: var(--bg); }
.acc-acts { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.acc-panel .btn.danger { border-color: var(--danger); color: var(--danger); background: transparent; }
.acc-panel .btn.danger:hover { background: var(--danger-soft); }
/* collapsible "levels" */
.acc-lvl { margin-top: 12px; border: 1px solid var(--border); border-radius: 11px; overflow: hidden; }
.acc-lvl > summary { list-style: none; cursor: pointer; display: flex; align-items: center; gap: 9px; padding: 11px 13px; background: var(--bg); font-size: 13px; font-weight: 600; }
.acc-lvl > summary::-webkit-details-marker { display: none; }
.acc-lvl > summary svg { width: 16px; height: 16px; color: var(--muted); }
.acc-lvl > summary .acc-chev { margin-left: auto; transition: transform .15s; }
.acc-lvl[open] > summary .acc-chev { transform: rotate(90deg); }
.acc-lvl.danger > summary { color: var(--danger); }
.acc-lvl.danger > summary svg { color: var(--danger); }
.acc-inner { padding: 12px 13px; }

/* Analytics dashboard (full-page overlay, opened via #analytics-btn). */
.an-view { position: fixed; inset: 0; z-index: 60; background: var(--bg, #f4f4f6); overflow-y: auto; padding: 18px 22px 40px; }
.an-view.hidden { display: none; }
.an-head { display: flex; align-items: flex-end; justify-content: space-between; gap: 16px; flex-wrap: wrap; max-width: 1180px; margin: 0 auto 18px; }
.an-title { font-size: 22px; margin: 0; }
.an-sub { color: var(--muted); font-size: 13px; }
.an-head-r { display: flex; align-items: center; gap: 12px; }
.an-range { display: inline-flex; background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 3px; gap: 2px; }
.an-range button { border: 0; background: none; color: var(--muted); font: inherit; font-size: 13px; padding: 6px 14px; border-radius: 7px; cursor: pointer; }
.an-range button.on { background: var(--accent); color: #fff; font-weight: 600; }
.an-body { max-width: 1180px; margin: 0 auto; }
.an-kpis { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; margin-bottom: 16px; }
.an-kpi { background: var(--panel); border: 1px solid var(--border); border-radius: 14px; padding: 16px 18px; }
.an-k-label { font-size: 11px; text-transform: uppercase; letter-spacing: .08em; color: var(--muted); }
.an-k-val { font-size: 26px; font-weight: 700; margin-top: 6px; font-variant-numeric: tabular-nums; display: flex; align-items: center; gap: 8px; }
.an-k-sub { font-size: 12px; color: var(--muted); margin-top: 2px; }
.an-k-sub-note:empty { display: none; }
.an-dot { width: 10px; height: 10px; border-radius: 50%; display: inline-block; }
/* §D4: KPI parcial + barra segmentada por host. Cores por token → theme-aware. */
.an-k-val.part { color: var(--amber); }
/* Uso sem tarifa: o aviso vive DENTRO do KPI, por baixo do número que desmente.
   :empty é o que o mantém invisível nos dias normais — o markup está sempre lá,
   e assim nenhum ramo condicional novo entra no template. */
.an-k-miss { font-size: 11px; line-height: 1.35; color: var(--amber-text); margin-top: 6px; }
.an-k-miss:empty { display: none; }
.an-leg.miss .an-amt,
.an-leg.miss .an-pct { color: var(--amber-text); }
/* "sem tarifa" não cabe nos 62px reservados a um valor monetário. */
.an-leg.miss .an-amt { width: auto; font-size: 11px; font-weight: 600; white-space: nowrap; }
/* O "+?" não cabe nos 70px da coluna de custo e quebrava para uma segunda
   linha, desalinhando a linha inteira do ranking. */
.an-rk-cost.miss { color: var(--amber-text); white-space: nowrap; font-size: 12px; }
.an-seg { display: flex; height: 7px; border-radius: 99px; overflow: hidden; margin-top: 8px; background: var(--border); }
.an-seg i { display: block; height: 100%; }
.an-s-on { background: var(--accent); }
.an-s-gap { background: repeating-linear-gradient(45deg, var(--muted), var(--muted) 3px, transparent 3px, transparent 6px); }
.an-hleg { display: flex; gap: 12px; font-size: 9.5px; color: var(--muted); margin-top: 7px; flex-wrap: wrap; font-variant-numeric: tabular-nums; }
.an-hleg b { display: inline-block; width: 7px; height: 7px; border-radius: 2px; margin-right: 4px; vertical-align: middle; }
.an-hleg .an-s-on { background: var(--accent); }
.an-hleg .an-s-gap { background: repeating-linear-gradient(45deg, var(--muted), var(--muted) 2px, transparent 2px, transparent 4px); }
.an-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; margin-bottom: 16px; }
.an-card { background: var(--panel); border: 1px solid var(--border); border-radius: 14px; padding: 18px; }
.an-card h3 { font-size: 13px; margin: 0 0 16px; display: flex; justify-content: space-between; align-items: center; }
.an-hint { font-size: 11px; color: var(--muted); font-weight: 400; }
.an-mix { display: flex; align-items: center; gap: 22px; }
.an-donut { position: relative; flex: 0 0 auto; }
.an-donut-c { position: absolute; inset: 0; display: grid; place-content: center; text-align: center; }
.an-donut-c b { font-size: 18px; font-variant-numeric: tabular-nums; }
.an-donut-c span { display: block; font-size: 10px; text-transform: uppercase; color: var(--muted); }
.an-legend { flex: 1; display: flex; flex-direction: column; gap: 9px; min-width: 0; }
.an-leg { display: grid; grid-template-columns: 12px 1fr auto auto; align-items: center; gap: 10px; font-variant-numeric: tabular-nums; }
.an-sw { width: 11px; height: 11px; border-radius: 3px; }
.an-nm { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 13px; }
.an-pct { font-size: 12px; color: var(--muted); width: 38px; text-align: right; }
.an-amt { font-weight: 600; width: 62px; text-align: right; }
.an-tokgrid { display: grid; grid-template-columns: 1fr 1fr; gap: 14px 20px; }
.an-tok-l { font-size: 12px; color: var(--muted); display: flex; justify-content: space-between; }
.an-bar { height: 8px; border-radius: 6px; background: var(--border-2, #eef0f3); overflow: hidden; margin-top: 6px; }
.an-bar > i { display: block; height: 100%; border-radius: 6px; }
.an-cachehit { grid-column: 1 / -1; display: flex; align-items: center; gap: 16px; border-top: 1px solid var(--border-2, #eef0f3); padding-top: 14px; margin-top: 4px; }
.an-ch-txt b { font-size: 22px; font-variant-numeric: tabular-nums; }
.an-ch-txt div { font-size: 12px; color: var(--muted); }
.an-rank { display: flex; flex-direction: column; gap: 3px; }
.an-rk { display: grid; grid-template-columns: 20px 1fr 70px 70px; align-items: center; gap: 12px; padding: 8px 6px; border-radius: 8px; background: none; border: 0; width: 100%; text-align: left; cursor: pointer; color: inherit; font: inherit; }
.an-rk:hover { background: var(--panel-2, #fafafb); }
.an-rk:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.an-pos { color: var(--muted); font-size: 12px; text-align: right; }
.an-rk-nm { display: flex; align-items: center; gap: 8px; overflow: hidden; }
.an-rk-nm b { font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.an-grp { font-size: 10px; padding: 1px 7px; border-radius: 999px; color: #fff; white-space: nowrap; }
/* Quanto da linha vem do arquivo. Contorno em vez de fundo cheio para não
   competir com o chip de grupo, que é o que identifica a linha.
   `--border`, não `--border-2`: este último NÃO existe (ver nota na .gsearch-mode)
   e um var() por resolver cai em currentColor — o contorno saía da cor do texto,
   mais forte do que se quer, e só no tema escuro é que isso se notava. */
.an-rk-arq { font-size: 10px; padding: 1px 6px; border-radius: 999px; border: 1px solid var(--border); color: var(--muted); white-space: nowrap; flex: none; }
.an-rk-cost { text-align: right; font-weight: 700; font-variant-numeric: tabular-nums; }
.an-rk-tks { text-align: right; font-size: 12px; color: var(--muted); font-variant-numeric: tabular-nums; }
.an-state { padding: 40px; text-align: center; color: var(--muted); }
@media (max-width: 820px) {
  .an-kpis { grid-template-columns: repeat(2, 1fr); }
  .an-grid { grid-template-columns: 1fr; }
  .an-rk { grid-template-columns: 18px 1fr 60px; }
  .an-rk-tks { display: none; }
}
