/* SEELE — a tela de operação (`#tela-sessao`).

   A composição do comp **v3**, tela `principal`
   (`.superpowers/sdd/comp-inventario-v3.md` §6): cabeçalho, quatro colunas,
   rodapé de telemetria. Os pontos de quebra que decidem a ordem em que essas
   colunas cedem estão no rodapé desta folha, e o porquê da ordem está lá. Carrega depois de `base.css`, e depende disso:
   `.compor input`, `.busca input`, `.convite-link` e `.volume` refinam o
   `input` de lá; `.busca .botao-fantasma` e `.botao-falar[data-ativo]` refinam
   botões de lá; e `.canais-cabeca .nota`, `.operador-frase` e `.compor-nota`
   refinam a `.nota` de lá — **refinam**, nunca reivindicam, que é o que
   `the_note_beside_a_control_is_defined_once_and_never_by_a_screen` cobra.

   Nenhuma cor literal aqui — nem em regra nem em comentário. `tests/tokens.rs`
   cobra isso lendo o arquivo inteiro, e é por isso que os hexadecimais do comp
   estão citados por **nome de token** e nunca por valor.

   ---- a medida é a do comp, e não a da célula ----

   As colunas são `60px 268px minmax(0,1fr) 328px` e o cabeçalho tem 56px, que é
   o que o comp v3 escreve. Nada aqui é arredondado para a célula de 8×16 dos
   tokens.

   Houve uma versão desta folha que arredondava — o `316` do v2 virava 320, o
   `56` virava 48 — com o argumento de que a célula é a régua do produto e os
   dois clientes deviam bater em pixel. O argumento está errado por duas razões.
   A primeira é que o inventário do v2 §13 mediu o comp inteiro e concluiu que a
   afirmação da célula **descreve a TUI**: o comp obedece a 4px com exceções,
   lista `56px` entre o que respeita, e usa a célula como orientação e não como
   restrição. A segunda é que arredondar é redesenhar — 316 e 320 não são o
   mesmo desenho, e quem decidiu 316 foi quem desenhou. O v3 decidiu 328, e vale
   o mesmo.

   A regra, para não refazerem a conta: **o app replica o comp; a TUI segue a
   célula do terminal; não se tenta alinhar os dois em pixel.** Os dois clientes
   combinam no que se lê — os mesmos nomes, a mesma ordem, as mesmas barras de
   blocos —, e não na largura de uma coluna que o terminal não tem.

   ---- o que o v3 omite, e por que omitir é a regra nova ----

   O v2 mandava desenhar a moldura e deixar o valor visivelmente não medido: um
   travessão com `title`, nunca um número plausível. O v3 **inverte isso nesta
   tela**, de propósito. Numa tela que existe para ser simples, meia dúzia de
   travessões explicados é ruído — cada um pede uma leitura e nenhum entrega
   informação. O que não tem dado sai da tela em vez de virar um campo vazio.

   Saíram por essa regra: a descrição por Linha (`linhaDesc` — o protocolo não
   tem o campo), a população do servidor (`412 operadores` — não existe), a
   contagem de pendências por Linha (`Line` é `{id, name, open}`), o atraso por
   pessoa no roster (`rtt_ms` é **nosso**, um número só) e o subsistema por
   pessoa (`PERMISSIONS·01` — o protocolo não diz qual atende quem).

   A convenção do travessão **continua valendo** onde a moldura é a informação:
   a média sem plug inserido, a barra da bateria, as três células do alerta. A
   diferença é que ali a ausência é a resposta a uma pergunta que a tela acabou
   de fazer; aqui era uma pergunta que ninguém fez.

   ---- peso 900 ----

   A Saira Condensed do comp é 900 em 48 lugares, e `--seele-cartela` é 700: os
   tokens não nomeiam o peso mais usado do desenho (inventário §11.5). Ele
   aparece aqui escrito, e não inventado como token novo — nomear um token de
   tipografia é decisão da folha de tokens, não desta. */

/* ---------- a cor do texto se resolve aqui, onde o tema chega ----------

   O `body` resolve `color: var(--seele-osso)` com o valor da raiz (`base.css`),
   e o tema de um MOD redefine o token em `#tela-sessao`, abaixo dele. Quem não
   declara cor herda a cor **já resolvida**, e não o token: medido em
   09/10/2026, com um tema claro que a validação aceita, o corpo das mensagens
   saía a 1,09:1 e o SAIR DA SALA a 1,04:1 — na 0.15.1 publicada também
   (`bancada/tema-claro-legivel.cjs`). Declarada aqui, a cor se resolve com o
   token do tema. Sem tema, o token é o mesmo da raiz, e o desenho não muda. */

#tela-sessao {
  color: var(--seele-osso);
  /* Os apelidos de `tokens.css` também se resolvem na raiz: a sincronia ficava
     com o fósforo, o laranja e o vermelho do produto sob qualquer tema. Aqui
     eles seguem as cores que o tema e a derivação escrevem em `#tela-sessao`. */
  --seele-sync-nominal: var(--seele-fosforo);
  --seele-sync-degradado: var(--seele-laranja-nerv);
  --seele-sync-critico: var(--seele-vermelho-alerta);
}

/* ---------- topo ----------

   56px e blocos divididos por borda, como o comp: cada bloco tem a altura toda
   e uma divisória de 1px, e não é o `gap` que os separa. É o que faz o
   cabeçalho parecer um painel de instrumentos em vez de uma barra de ícones. */

.topo {
  display: flex;
  align-items: stretch;
  height: 56px;
  border-bottom: 1px solid var(--seele-linha);
  background: var(--seele-negro-painel);
  flex: 0 0 auto;
}

/* O quadrado de 56×56 onde o comp põe o `MG`. */
.topo-marca {
  width: 56px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border-right: 1px solid var(--seele-linha);
}

.marca {
  font-family: var(--seele-display);
  font-weight: 900;
  font-size: 16px;
  letter-spacing: 0.06em;
  color: var(--seele-laranja-nerv);
}

/* Um bloco qualquer do cabeçalho: altura toda, divisória à direita. */
.topo-bloco {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 0 20px;
  border-right: 1px solid var(--seele-linha);
  flex: 0 0 auto;
}

/* O último bloco tem a divisória do outro lado — é o que fecha a fileira. */
.topo-bloco-fim {
  gap: 20px;
  border-right: none;
  border-left: 1px solid var(--seele-linha);
}

/* `TÓQUIO-3` sobre `SERVER CENTRAL · 7743`: duas linhas, empilhadas e centradas
   na altura do cabeçalho. */
.topo-server {
  display: flex;
  flex-direction: column;
  justify-content: center;
  gap: 2px;
  padding: 0 20px;
  border-right: 1px solid var(--seele-linha);
  min-width: 0;
  flex: 0 1 auto;
}

/* `topo-` no nome, e não `.server-nome`: o Terminal Server é uma tela inteira com
   folha própria, e `no_two_screens_claim_the_same_class_name` decide empates
   entre duas folhas de tela pela ordem de carga, em silêncio. Este é o nome do
   Server **no cabeçalho da sessão**, e o prefixo diz de quem ele é. */
.topo-server-nome {
  font-family: var(--seele-display);
  font-weight: 900;
  font-size: 20px;
  line-height: 1;
  letter-spacing: 0.06em;
  color: var(--seele-osso);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topo-server-sub {
  font-size: var(--seele-t-micro);
  letter-spacing: 0.2em;
  color: var(--seele-osso-apagado);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.topo-kanji { color: var(--seele-osso-apagado); font-size: 12px; }

/* A régua do comp, que lá é um par cercando `CODE : 0447 · EX_MODE : ON`. Com
   a ficção fora, sobra uma, e ela é o vão entre os blocos e o relógio. */
.regua-larga {
  flex: 1;
  align-self: center;
  height: 1px;
  margin: 0 20px;
  background: var(--seele-linha-forte);
  min-width: 16px;
}

.topo .botao-fantasma { align-self: center; margin-right: 16px; }

.relogio {
  font-size: var(--seele-t-dado);
  letter-spacing: 0.08em;
  color: var(--seele-osso);
  font-variant-numeric: tabular-nums;
}

/* ---------- banner ---------- */

.banner {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  background: var(--seele-vermelho-fraco);
  border-bottom: 1px solid var(--seele-vermelho-alerta);
  color: var(--seele-vermelho-alerta);
  flex: 0 0 auto;
}

.banner[data-severidade="Info"] {
  background: var(--seele-laranja-fraco);
  border-bottom-color: var(--seele-laranja-nerv);
  color: var(--seele-laranja-nerv);
}

/* O veredito de identidade. Mesma geometria do banner e a cor institucional:
   ressalva, não queda. O vermelho é do `.banner`, e o token de vermelho está
   marcado "EXCLUSIVO alerta e queda" na folha de tokens.

   `pre-line` porque a impressão digital vai em linha própria: são 64
   caracteres para comparar com outro canal, e reflui-los no meio de uma frase
   é a única forma em que ninguém consegue compará-los. */
/* O veredito é do núcleo: as cores são `--nucleo-*` (tarefa 9). */
.veredito {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 8px 16px;
  background: var(--nucleo-acento-fraco);
  border-bottom: 1px solid var(--nucleo-acento);
  color: var(--nucleo-acento);
  flex: 0 0 auto;
}

.veredito-texto {
  font-family: var(--seele-mono);
  font-size: var(--seele-t-dado);
  white-space: pre-line;
  overflow-wrap: anywhere;
}

/* ---------- as colunas, e a célula que é disputada ----------

   Quatro colunas do produto e uma quinta que só existe quando um MOD pede um
   painel. Elas são **nomeadas** desde a API 4, e a razão é a célula do meio:
   uma página de MOD ocupa a mesma que a conversa, por cima dela, e é isso que
   permite «abrir a mesa» sem empurrar a conversa de lugar nem desconectar a
   voz. Com colunas numeradas, um quinto filho cairia numa segunda linha —
   a mesma armadilha que fez a faixa dos MODs nascer fora de `<main>`. */

.paineis {
  flex: 1 1 auto;
  display: grid;
  grid-template-columns: 60px 268px minmax(0, 1fr) 328px auto;
  grid-template-areas: "trilha canais linha pessoas painelmod";
  min-height: 0;
}

.paineis > .trilha { grid-area: trilha; }
.paineis > .painel-canais { grid-area: canais; }
.paineis > .painel-linha { grid-area: linha; }
.paineis > .painel-pessoas { grid-area: pessoas; }
.paineis > .palco-de-paineis { grid-area: painelmod; }

/* ---------- coluna 1: a trilha de servidores ----------
   60px de largura no v3, contra os 56px do v2, e o mesmo botão de 56px de
   altura por servidor. A marca de 2px na borda esquerda é a mesma técnica de
   estado que as salas de voz usam logo ao lado, e é de propósito que seja a mesma:
   são duas listas de coisas em que se está ou não se está. */
.trilha {
  display: flex;
  flex-direction: column;
  border-right: 1px solid var(--seele-linha);
  background: var(--seele-negro-painel);
  overflow-y: auto;
}

/* `REDES`, a faixa de 24px que o v3 põe no alto da trilha. Ela é o que diz que
   esta coluna de siglas é uma lista de servidores e não um menu — sem ela, `T3`
   sozinho num quadrado não é o nome de nada. */
.trilha-titulo {
  margin: 0;
  height: 24px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border-bottom: 1px solid var(--seele-linha);
  font-family: var(--seele-mono);
  font-size: 8px;
  font-weight: normal;
  letter-spacing: 0.16em;
  color: var(--seele-rotulo-painel);
}

.trilha-item {
  height: 56px;
  flex: 0 0 auto;
  border: 0;
  border-bottom: 1px solid var(--seele-linha);
  border-left: 3px solid transparent;
  background: none;
  font-family: var(--seele-display);
  font-weight: 900;
  font-size: 15px;
  letter-spacing: 0.06em;
  color: var(--seele-osso-apagado);
  cursor: pointer;
}

/* O servidor em que se está. Desabilitado porque apertá-lo seria entrar onde já se
   está — mas ele não é decoração: é o rótulo da coluna, e `aria-current` diz
   isso a quem escuta a tela. Sem o esmaecimento de um botão desabilitado
   qualquer; um item apagado seria o contrário do que ele significa. */
.trilha-atual {
  border-left-color: var(--seele-laranja-nerv);
  background: var(--seele-laranja-fraco);
  color: var(--seele-laranja-nerv);
  cursor: default;
}

/* Os servidores do histórico. Uma lista de verdade — é o que eles são, e é o
   que faz um leitor de tela anunciar quantos há antes de a pessoa percorrer a
   coluna. Sem marcador e sem recuo: o marcador de `<li>` numa coluna de 60px
   come a metade dela. */
.trilha-outros {
  margin: 0;
  padding: 0;
  flex: 0 0 auto;
  list-style: none;
}

/* O botão dentro do `<li>` não é mais filho direto da coluna, então o `stretch`
   que dava 60px a ele parou de valer: um `<button>` nasce `inline-block` e sai
   do tamanho do que tem dentro — uma sigla de duas letras num quadrado de duas
   letras, encostada à esquerda de uma coluna de 60px. */
.trilha-outro {
  display: block;
  width: 100%;
}

/* Entrar noutro servidor é sair deste, e a coluna diz isso pelo mesmo canal que
   as salas de voz ao lado: a marca de 2px na borda esquerda, que aqui acende sob o
   ponteiro e sob o foco em vez de ficar acesa. O que está aceso é onde se está;
   o que acende ao ser apontado é para onde se pode ir. */
.trilha-outro:hover,
.trilha-outro:focus-visible {
  border-left-color: var(--seele-linha-forte);
  color: var(--seele-osso);
}

/* O VOLTAR AO MEU SERVIDOR (tarefa 20 da F7): o mesmo item dos outros
   servidores, com a marca de 2px da borda esquerda acesa em laranja, porque é o
   único que esta janela sabe que está no ar. */
.trilha-voltar {
  border-left-color: var(--seele-laranja-nerv);
}

/* A imagem do servidor, quando ele tem uma, no lugar da sigla. `contain` e não
   `cover` pela mesma razão que no cabeçalho: recortar a imagem de alguém para
   caber num quadrado é decidir por quem a escolheu qual metade dela importa. */
.trilha-icone {
  display: block;
  width: 100%;
  height: 100%;
  padding: 8px;
  object-fit: contain;
}

/* **As regiões da API 6 são caixas fechadas** (rascunho §3, tarefa 5) — esta, a
   da barra do operador, a do compositor, a da barra de baixo e o miolo do
   cabeçalho do canal. Um MOD desloca o próprio desenho até ±512 px
   (`estilo.mover`), e é assim que ele o poria por cima do que é do produto ao
   lado. **O `overflow` e o `contain: paint` cortam esse desenho, cada um
   sozinho**; sem os dois, ele cobria o servidor atual, o microfone, o fone e o
   nome do canal (medido em `regioes-do-produto.cjs`, mirando o centro de cada
   um). As duas travas ficam: o `overflow` existe também pelo teto de altura, e
   o `contain` segura se ele um dia mudar.

   A região `redes`: o que os MODs acrescentam à trilha, com teto de altura de
   três itens da trilha — sem ele, um conteúdo alto empurrava o `+` para fora
   da vista (medido). O que passar rola por dentro. */
.trilha-mods {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  max-height: 168px;
  border-bottom: 1px solid var(--seele-linha);
  overflow-x: hidden;
  overflow-y: auto;
  contain: paint;
}

/* O `+` de 48px, que hoje faz alguma coisa: leva à entrada para conectar a um
   servidor que ainda não está nesta coluna. Enquanto ele não fazia, era pintado
   em `linha-forte` e com `cursor: default`, porque era moldura; um controle vivo
   com a cor de uma borda é um controle que ninguém tenta apertar. */
.trilha-mais {
  height: 48px;
  flex: 0 0 auto;
  border: 0;
  border-bottom: 1px solid var(--seele-linha);
  border-left: 3px solid transparent;
  background: none;
  font: inherit;
  font-size: 18px;
  color: var(--seele-osso-apagado);
  cursor: pointer;
}

.trilha-mais:hover,
.trilha-mais:focus-visible {
  border-left-color: var(--seele-linha-forte);
  color: var(--seele-osso);
}

.painel {
  display: flex;
  flex-direction: column;
  min-height: 0;
  border-right: 1px solid var(--seele-linha);
  background: var(--seele-negro-painel);
}

.painel-pessoas { border-right: none; }

/* O título de painel.

   A cor sai de `--seele-rotulo-painel` e nunca de `osso-apagado` direto.
   `docs/tokens-achados.md` mede aquela cor em 4,11:1 — que passa só como texto
   grande — e a escala põe rótulo de painel em 9–10px, que não é texto grande;
   o achado deixa a correção pendente entre subir a cor e garantir que nada
   necessário dependa só dela. Enquanto ela não vem, o valor é o do comp, que
   pinta todos os 25 rótulos de `0.2em` desta tela em `osso-apagado`, e o nome
   semântico é o único lugar a mudar no dia da decisão. Quem quiser mais
   contraste já o recebe: `acessibilidade.css` sobe `.painel-titulo` para osso
   sob `prefers-contrast: more`. */
.painel-titulo {
  margin: 0;
  padding: 0;
  flex: 1;
  min-width: 0;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  font-family: var(--seele-mono);
  font-size: var(--seele-t-micro);
  font-weight: normal;
  letter-spacing: 0.2em;
  color: var(--seele-rotulo-painel);
  flex: 0 0 auto;
}

/* O cabeçalho de uma das duas listas: o título e a nota que o explica.

   A nota é `.nota`, de `base.css`, e esta folha só a **refina** — margem e
   recuo, nada que decida se ela aparece. Uma tela que reivindicasse `.nota`
   passaria a decidir sozinha se aquela frase existe, que é como a explicação de
   um controle some de uma tela só e de mais nenhuma
   (`the_note_beside_a_control_is_defined_once_and_never_by_a_screen`). */
/* A faixa de cabeçalho de painel, com os valores da comp: `padding:10px 12px 8px`,
   tudo **centrado** na vertical e `gap:8px`.
 *
 * O alinhamento morava em três lugares que discordavam: a faixa alinhava pelo
 * topo, o título trazia a própria folga de 12px, e o `?` compensava com uma
 * margem de 10px. O resultado era o `+` colado no alto e o `?` dez pixels
 * abaixo dele — «o botão + e o botão ? não estão alinhados». A folga é da faixa
 * agora, e o que estiver nela fica no meio dela sem ninguém compensar nada. */
.canais-cabeca {
  flex: 0 0 auto;
  border-bottom: 1px solid var(--seele-linha);
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 12px 8px;
}

.canais-cabeca .painel-titulo { flex: 1; padding-bottom: 2px; }

/* O `?` que abre a ajuda.
   Quadrado e do tamanho do texto ao lado: ele não compete com o título, e não
   deve — quem já sabe o que é uma sala de voz nunca mais precisa dele. Vira laranja no
   hover e no foco como todo alvo desta janela. */
/* O `?` é um `.painel-acao` como o `+`, e o que sobra aqui é só a face: a comp
   escreve o `+` na monoespaçada de 14 e o `?` na de 10, porque uma interrogação
   no corpo do sinal de mais fica maior que ele. Tamanho, moldura e alinhamento
   vêm do `+`, uma vez, para os dois nunca mais divergirem. */
.painel-ajuda {
  font-family: var(--seele-mono);
  font-size: 10px;
}


.canais-cabeca .nota { padding: 0 12px 8px; }

.canais-cabeca-linhas {
  border-top: 1px solid var(--seele-linha);
  margin-top: 16px;
}

/* Os códigos de painel — `B·03`, `C·05` — saíram na 0.9.0. Eram herança do
   comp v2, que numerava painéis como um console numera módulos; a comp da
   0.9.0 não os traz, e o espaço do cabeçalho vale mais para o que age.

   O `+` que os substitui: quadrado de 22px, do tamanho do `?` que já morava
   ali, porque são irmãos na mesma fileira. */
/* O sair da sala, na linha do nome.
 *
 * Quadrado de 22px como o apagar ao lado, e laranja porque é o único controle
 * daquela fileira que age sobre a **sua** presença — os outros agem sobre a
 * sala. Ao passar o mouse ele inverte, que é o que a comp faz com todo botão
 * de contorno laranja. */
/* `.voice_room-sair` saiu com o botão de desligar que ela pintava. Quem sai da
   sala agora é o `SAIR DA SALA` do rodapé do operador, escrito em palavras —
   ver `desenharORodapeDoOperador`, em `tela-chamada.js`. */

.painel-acao {
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  font: inherit;
  font-size: 14px;
  line-height: 1;
  color: var(--seele-osso-apagado);
  background: none;
  border: 1px solid var(--seele-linha-forte);
  cursor: pointer;
}

.painel-acao:hover {
  color: var(--seele-laranja-nerv);
  border-color: var(--seele-laranja-nerv);
}

.espaco { flex: 1; }

/* ---------- o valor que ninguém mediu ----------
   Metade do que o comp desenha não tem dado por trás. A moldura fica; o valor
   sai como travessão, e o `title` diz o que faltaria.

   A cor é `.ausente`, de `base.css`, e não uma classe própria desta tela: são o
   mesmo conceito, e a cópia local ficava de fora do realce que
   `acessibilidade.css` dá a `.ausente` sob `prefers-contrast: more` — quem pede
   mais contraste perdia justamente o travessão que diz "isto não existe".

   O que fica aqui é só o cursor, e preso a `[title]`: cursor de ajuda sem
   tooltip promete uma explicação que não vem. */
.ausente[title] { cursor: help; }

/* ---------- coluna 2: VoiceRooms e Linhas ---------- */

/* O que rola nesta coluna, e o que não rola.

   Dentro: os dois cabeçalhos, as duas listas e os dois formulários de criar.
   Fora: a tira do operador, que fica presa no pé.

   O `min-height: 0` é a metade difícil disto em flexbox e ela já estava, no
   `.painel` genérico logo acima; a outra metade é este `overflow-y`, e sem ela
   `min-height: 0` não faz nada visível — o conteúdo passa da borda do painel e
   `body { overflow: hidden }` de `base.css` garante que nem a janela role atrás
   dele. Com dois VoiceRooms ninguém percebe. Com trinta, os de baixo não existem, e
   foi assim que o defeito foi encontrado: por alguém usando.

   As duas listas dividem **um** contêiner em vez de rolarem cada uma no seu.
   Duas áreas de rolagem numa coluna de 268px dão duas janelinhas de meia dúzia
   de linhas cada, e a pessoa que procura um canal passa a ter de descobrir
   qual das duas rola. Aqui o cabeçalho dos canais sobe junto com o conteúdo, que
   é o que uma coluna de canais faz em qualquer cliente de chat.

   Coluna flex, e não bloco: é o que mantém o `flex: 0 0 auto` dos filhos
   dizendo o que já dizia — não encolha para caber. Dentro de um contêiner que
   rola, isso é exatamente a instrução certa. */
.canais-rolagem {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
  display: flex;
  flex-direction: column;
}

.canais {
  list-style: none;
  margin: 0;
  padding: 0;
  flex: 0 0 auto;
}

/* A marca de 3px à esquerda é o estado, e é a mesma técnica do comp v3: uma
   barra de cor na borda em vez de um fundo inteiro, para que a lista continue
   legível com metade das linhas marcadas. Ela vai no elemento que **é** o item
   — o `<li>` da sala de voz, o `<button>` da Linha — e não num ancestral comum: a
   Linha virou botão para ganhar teclado, e um marcador no `<li>` em volta dela
   ficaria de fora do foco que ele acompanha. */
.voice_room, .linha { border-left: 3px solid transparent; }
.voice_room.aberto, .linha.aberto { border-left-color: var(--seele-laranja-nerv); }

/* O VoiceRoom tem quatro faixas de conteúdo agora — nome, ocupação, quem está
   dentro, e o botão — e ganha `10px 12px` mais um `border-top` que o separa do
   anterior, como no comp v3. */
.voice_room {
  display: flex;
  flex-direction: column;
  gap: 8px;
  border-top: 1px solid var(--seele-linha);
  padding: 10px 12px;
}

.voice_room.aberto { background: var(--seele-laranja-fraco); }

.canal-cabeca { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }

.voice_room-nome {
  font-family: var(--seele-display);
  font-weight: var(--seele-cartela);
  font-size: 15px;
  letter-spacing: 0.06em;
  color: var(--seele-osso);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.voice_room.aberto .voice_room-nome { color: var(--seele-laranja-nerv); }

/* **Na sala aberta, a ocupação e quem está dentro no acento**, como o nome
   dela: o preenchimento da sala é derivado do acento, e a derivação garante ao
   acento 4,5:1 sobre ele (tema.derivados-pelo-produto). O apagado, que não
   acompanha o tema, saía a 2,85:1 sob o papel — um par que o produto desenha e
   que a tabela do tema não tem (8:78 da auditoria do v6; D-api-10). O «(você)»
   e a marca de quem fala têm cor própria. */
.voice_room.aberto .voice_room-ocupacao,
.voice_room.aberto .voice_room-pessoa { color: var(--seele-laranja-nerv); }

/* O nome como botão de renomear, para quem administra as salas.
 *
 * Sem moldura e sem fundo: ele **é** o nome, e desenhá-lo como botão faria a
 * lista parecer uma barra de ferramentas. O que diz que ele responde é o
 * cursor e o sublinhado ao passar — e o `title` e o `aria-label`, que dizem o
 * verbo por extenso para quem não tem mouse nem enxerga. */
button.voice_room-nome {
  font: inherit;
  text-align: left;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}

button.voice_room-nome:hover { text-decoration: underline; }

/* `4/8` — a ocupação em número, e sozinha: a barra de blocos que a acompanhava
   saiu junto com o v2. A lista de nomes logo abaixo diz o mesmo com os nomes
   dentro, e duas leituras da mesma medida numa coluna de 268px é uma a mais. */
.voice_room-ocupacao {
  font-size: var(--seele-t-rotulo);
  color: var(--seele-osso-apagado);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}

/* ---- quem está dentro ----
   O ganho maior do v3, e ele não custa protocolo: `VoiceRoom.people` já vinha
   populado para todo VoiceRoom, e o app o gastava numa barra de blocos.

   A palavra ao lado do nome é o que separa isto de um enfeite: o glifo dá a
   varredura de relance, e `fala`/`mudo` é a mesma informação sem depender de
   enxergar forma nem cor (`specs/06-clientes-gui.md`). */
.voice_room-dentro {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 3px;
}

.voice_room-pessoa {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 11px;
  color: var(--seele-osso-apagado);
}

.voice_room-pessoa-nome {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.voice_room-pessoa-eu { color: var(--seele-padrao-azul); flex: 0 0 auto; }

.voice_room-pessoa-marca {
  margin-left: auto;
  flex: 0 0 auto;
  letter-spacing: 0.08em;
  color: var(--seele-laranja-nerv);
}

.voice_room-vazio { color: var(--seele-linha-forte); font-size: 11px; }

/* O botão de 38px, com rótulo. Contorno enquanto se está de fora, sólido
   enquanto se está dentro — a mesma ênfase invertida do resto da interface,
   dizendo qual dos dois é o estado corrente. */
.voice_room-entrar {
  width: 100%;
  min-height: 38px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--seele-display);
  font-weight: 900;
  font-size: 14px;
  letter-spacing: 0.14em;
  color: var(--seele-laranja-nerv);
  background: none;
  border: 1px solid var(--seele-laranja-nerv);
  cursor: pointer;
}

.voice_room-entrar:hover {
  color: var(--seele-negro-absoluto);
  background: var(--seele-laranja-nerv);
}

/* **Dentro da sala o botão continua, e muda de ofício: `VER A CHAMADA`.**

   Ele já foi `ENTRAR NA SALA` só do lado de fora, com um botão de desligar no
   lugar dele do lado de dentro. O desligar saiu — sair da sala é o `SAIR DA
   SALA` do rodapé, escrito —, e o que sobrou aqui é navegação: voltar para a
   grade depois de ter ido ler um canal.

   Preenchido em vez de vazado, e é a única diferença de desenho: o vazado
   convida a entrar, e num lugar onde já se está o convite seria uma promessa
   falsa. Preenchido ele lê como «é aqui», que é o que a borda laranja do
   `<li>` já diz uma linha acima. */
.voice_room-entrar.aqui {
  color: var(--seele-negro-absoluto);
  background: var(--seele-laranja-nerv);
}

.voice_room-entrar.aqui:hover { opacity: 0.85; }

/* ---- destruir uma sala ----

   Os dois botões da sala de voz numa fileira. `ENTRAR NA SALA` fica com o espaço, e
   `APAGAR` toma o que o rótulo dele precisa: o que se faz nesta lista o dia
   inteiro é entrar e sair, e apagar é o que se faz uma vez.

   Sem vermelho. `tokens.css` marca essa cor «EXCLUSIVO alerta e queda», e
   destruir uma sala é a coisa mais grave desta janela sem ser uma queda de
   enlace — o vermelho gasto aqui é o vermelho que ninguém lê no dia em que a
   bateria interna acender. O que separa este botão do outro é a moldura fraca
   e o texto apagado, e não uma cor de alarme. */
.voice_room-botoes { display: flex; gap: 6px; }
.voice_room-botoes .voice_room-entrar { flex: 1; width: auto; }

/* O `×` de apagar: 22×22, com os valores da comp.
 *
 * Ele foi `APAGAR` escrito por extenso durante uma versão, e a palavra tinha 38
 * pixels de altura e uma moldura em toda linha de sala e de canal — uma coluna
 * de 268px em que o verbo mais raro era o elemento mais pesado. O nome do que
 * ele apaga continua dito por extenso, no `aria-label` e no `title`, que é onde
 * ele é lido por quem procura o verbo em vez de esbarrar nele. */
.voice_room-apagar, .linha-apagar, .linha-renomear {
  width: 22px;
  height: 22px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 0;
  font: inherit;
  font-size: 13px;
  line-height: 1;
  color: var(--seele-osso-apagado);
  background: none;
  border: 1px solid var(--seele-linha-forte);
  cursor: pointer;
}

.voice_room-apagar:hover, .linha-apagar:hover, .linha-renomear:hover {
  color: var(--seele-negro-absoluto);
  background: var(--seele-osso-apagado);
  border-color: var(--seele-osso-apagado);
}

/* O último VoiceRoom, que não se apaga: desligado. **Ele não é legível** — o
   `--seele-linha-forte` dá 1,32:1 sobre o preenchimento da sala aberta, e a
   1,1:1 sob um tema escuro —, e não precisa ser: a WCAG 1.4.3 dispensa de
   contraste o controle inativo. O porquê está no `title` (o
   `botaoDeApagarVoiceRoom`), e não na cor (medido na auditoria do v6, 8:78;
   D-api-10). */
.voice_room-apagar:disabled {
  color: var(--seele-linha-forte);
  border-color: var(--seele-linha);
  cursor: not-allowed;
}

.voice_room-apagar:disabled:hover { color: var(--seele-linha-forte); background: none; }

/* A fileira da Linha, quando ela tem controles ao lado. Sem eles o `<li>`
   continua sendo o que era: um único botão de largura cheia. */
.linha-fileira { display: flex; align-items: stretch; }
.linha-fileira .linha { min-width: 0; flex: 1; }

/* Os dois controles do canal, **empilhados e grudados**: o `×` em cima e o
   lápis embaixo, numa coluna de 22px.

   Lado a lado eles custariam 44px da coluna de 268px onde o nome do canal mora,
   e o nome é o que se lê. A fileira tem 40px de altura, então duas caixas de 20
   cabem sem que nada cresça — a de 22px vira 20 aqui dentro, e o `-1px` de
   margem funde as duas bordas numa linha só, que é o que «grudado» quer dizer.

   Quem tem só uma das duas permissões vê um botão de 20px numa fileira de 40, e
   isso está certo: o vazio ao lado é a permissão que essa pessoa não tem. */
.linha-controles {
  display: flex;
  flex-direction: column;
  flex: 0 0 auto;
  justify-content: center;
}

.linha-controles > button { height: 20px; }
.linha-controles > button + button { margin-top: -1px; }

/* O lápis é desenho e não caractere, então a caixa do glifo manda — sem o
   `font-size` de 13px que o `×` precisa por ser texto. */
.linha-renomear { font-size: inherit; }

.barra {
  display: block;
  font-size: var(--seele-t-rotulo);
  letter-spacing: 0.04em;
  line-height: 16px;
  color: var(--seele-linha-forte);
  white-space: nowrap;
  overflow: hidden;
}

.voice_room.aberto .barra { color: var(--seele-laranja-nerv); }

/* A Linha, agora um botão de 40px que ocupa a fileira inteira. `text-align`
   explícito porque um `<button>` centra por omissão, e uma lista de nomes
   centrados não é uma lista. */
.linha {
  width: 100%;
  min-height: 40px;
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 12px;
  font: inherit;
  font-size: var(--seele-t-dado);
  text-align: left;
  color: var(--seele-osso-apagado);
  background: none;
  border-top: 0;
  border-right: 0;
  border-bottom: 0;
  cursor: pointer;
}

.linha:hover { background: var(--seele-linha); }
.linha.aberto { background: var(--seele-linha); color: var(--seele-osso); }

/* O `#` do comp: fraco enquanto a Linha está fechada, laranja na aberta. Ele é
   decoração ao lado do nome — quem escuta a tela recebe o nome, e "sinal de
   número" antes de cada item seria ruído em toda linha da lista.

   `linha-forte` e não a placa de marca com que o comp o pinta: as seis placas
   são do ícone do app, e `tokens.css` escreve na cara delas que nenhuma
   superfície do produto se pinta com elas. */
.linha-cerquilha { color: var(--seele-linha-forte); flex: 0 0 auto; }
.linha.aberto .linha-cerquilha { color: var(--seele-laranja-nerv); }

.linha-rotulo {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A tira do operador. */
.operador {
  border-top: 1px solid var(--seele-linha);
  padding: 10px 12px;
  display: flex;
  flex-direction: column;
  gap: 6px;
  flex: 0 0 auto;
  background: var(--seele-negro-painel);
}

/* **A capa de uma pessoa** (`pessoa.capa`; rascunho da API 6, §4; tarefa 6):
   a primeira camada de quem a veste — o cartão da chamada, a barra do
   operador —, atrás de todo o conteúdo. O véu e o halo de quem fala moram no
   `::after` da camada, por cima da imagem e por baixo do texto: um `box-shadow`
   inset do próprio cartão seria pintado com o fundo dele, **abaixo** da capa.
   Seletores de atributo, e não de classe: quem veste é de outra tela. */
[data-com-capa] {
  position: relative;
  isolation: isolate;
}

.capa-camada {
  position: absolute;
  inset: 0;
  z-index: -1;
  background-size: cover;
  background-position: center;
}

.capa-camada::after {
  content: "";
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--seele-negro-painel) 55%, transparent);
}

[data-fala="sim"] > .capa-camada::after {
  background: color-mix(in srgb, var(--seele-laranja-carga) 75%, transparent);
  box-shadow: inset 0 0 0 var(--seele-b-forte) var(--seele-laranja-nerv);
}

/* A região `operador.barra` (rascunho da API 6, §3): fechada como as outras, e
   com teto de altura — o que passar rola por dentro, e o SAIR e o microfone,
   embaixo dela, continuam onde estão. */
.operador-mods {
  max-height: 96px;
  overflow-x: hidden;
  overflow-y: auto;
  contain: paint;
}

/* **As placas da barra do operador sobre a capa** (rascunho da API 6, §4 e
   §10; tarefa 6). A capa de quem usa pinta o fundo da barra inteira, e cada
   texto e cada controle fica sobre uma placa opaca da cor do fundo nativo da
   barra, o negro do painel: o contraste é o de sem capa, medido em `capa.cjs`
   com a cor e a borda, contra uma capa branca, uma preta e uma da cor da
   tinta. O MUDO, o fone, o «no ar» e o SAIR, que são do núcleo, não estão
   aqui: a placa deles é a do núcleo, sempre (abaixo, tarefa 9). */
.operador[data-com-capa] :where(
  .operador-inicial, .operador-rotulo, .operador-nome, #botao-server-sessao, #operador-vista
) {
  background-color: var(--seele-negro-painel);
}

/* **O núcleo de confiança, nas cores do produto** (rascunho da API 6, §10;
   tarefa 9 da F3). O tema do servidor escreve os `--seele-*` em
   `#tela-sessao`; os `--nucleo-*`, declarados na raiz a partir dos tokens,
   chegam a todo filho já resolvidos, e o tema não os alcança. O MUDO, o fone,
   o «no ar», o SAIR e o veredito os leem, sobre uma placa opaca do núcleo:
   com e sem tema eles têm as mesmas cores — inclusive sob um tema de acento
   igual ao fundo, que a API 6 recusa pelos pares (medido em
   `bancada/nucleo.cjs`). Os nomes são os da casca do celular
   (`celular-base.css`), que declara os mesmos sob a marca dela. */
:root {
  --nucleo-fundo: var(--seele-negro-absoluto);
  --nucleo-painel: var(--seele-negro-painel);
  --nucleo-texto: var(--seele-osso);
  --nucleo-apagado: var(--seele-osso-apagado);
  --nucleo-acento: var(--seele-laranja-nerv);
  --nucleo-acento-fraco: var(--seele-laranja-fraco);
  --nucleo-borda: var(--seele-linha-forte);
  /* O rótulo miúdo do núcleo (6,82:1 sobre o painel): o nome do MOD por cima de
     cada seção do «⋯». O apagado ficava a 4,11:1, abaixo dos 4,5 de uma letra de
     10 px (medido pela F8 na folha do celular, 16/10). */
  --nucleo-rotulo: var(--seele-rotulo-claro);
}

#voz-estado {
  color: var(--nucleo-apagado);
  background-color: var(--nucleo-painel);
}

/* **O selo da prévia do tema** (rascunho da API 6, §9, B4; tarefa 17g): «prévia
   não publicada», do produto, nas cores do núcleo — o tema em prévia não o
   alcança —, no alto e acima do véu de um modal (z 60), sem pegar clique. */
.selo-da-previa {
  position: fixed;
  top: calc(var(--seele-barra-janela, 32px) + 8px);
  left: 50%;
  transform: translateX(-50%);
  z-index: 70;
  padding: 4px 12px;
  font-family: var(--seele-mono);
  font-size: var(--seele-t-dado);
  letter-spacing: 0.12em;
  color: var(--nucleo-fundo);
  background: var(--nucleo-acento);
  pointer-events: none;
}

.selo-da-previa[hidden] { display: none; }

#operador-sair {
  color: var(--nucleo-texto);
  background-color: var(--nucleo-painel);
}

.operador-topo { display: flex; align-items: center; gap: 8px; }

.operador-rotulo {
  font-size: var(--seele-t-micro);
  letter-spacing: 0.2em;
  color: var(--seele-rotulo-painel);
}

.operador-nome {
  font-size: var(--seele-t-dado);
  color: var(--seele-padrao-azul);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* O `⚙` do comp, 34×34 e contornado — a mesma medida do microfone e do fone
   ao lado, que é o que os faz lerem como uma fileira só. O glifo é o conteúdo inteiro do botão,
   então o nome acessível é obrigatório e vive no `aria-label` do próprio botão
   — a nota ao lado é a metade que quem enxerga recebe. */
.operador-config {
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 15px;
  color: var(--seele-osso-apagado);
  background: none;
  border: 1px solid var(--seele-linha-forte);
  cursor: pointer;
}

.operador-config:hover {
  color: var(--seele-laranja-nerv);
  border-color: var(--seele-laranja-nerv);
}

/* Microfone e fone, com os valores da comp: 34×34, contornados, o glifo em
 * 1,15em do corpo de 13px.
 *
 * **Quadrados, e este é o defeito que eles tinham.** Eles herdavam um
 * `.operador .botao-estado { width: 100% }` que vencia por especificidade o
 * `width: 34px` daqui, e um `font-size` de rótulo que deixava o glifo em 12px.
 * O resultado em campo foram duas caixas largas e aparentemente vazias no meio
 * da coluna — «os botões estão completamente estranhos». */
/* O MUDO e o fone são do núcleo: as cores e a placa são `--nucleo-*` (acima). */
.operador-glifo {
  width: 34px;
  height: 34px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  font: inherit;
  color: var(--nucleo-apagado);
  background: var(--nucleo-painel);
  border: 1px solid var(--nucleo-borda);
  padding: 0;
  cursor: pointer;
}

.operador-glifo .glifo { width: 1.15em; height: 1.15em; }

.operador-glifo:hover {
  color: var(--nucleo-acento);
  border-color: var(--nucleo-acento);
}

/* Desligado é laranja cheio, como todo estado ligado desta casca. */
.operador-glifo[data-ativo="sim"] {
  color: var(--nucleo-fundo);
  background: var(--nucleo-acento);
  border-color: var(--nucleo-acento);
}

/* ---------- coluna 3: o canal aberto ---------- */

.painel-linha { background: var(--seele-negro-absoluto); }

/* 52px no v3, contra os 40px do v2: o botão `BUSCAR` tem 34px de altura e não
   cabia numa barra de 40. */
.linha-barra {
  display: flex;
  align-items: center;
  gap: 12px;
  min-height: 52px;
  padding: 6px 16px;
  border-bottom: 1px solid var(--seele-linha);
  flex: 0 0 auto;
}

/* O título de um grupo da navegação substituída (rascunho da API 6, §3): o texto
   é do MOD, e o desenho é o dos títulos do produto — miúdo, espaçado, apagado —,
   para ele ler como divisão da lista, e não como uma linha que se aperta. */
.canais-grupo {
  list-style: none;
  padding: 10px 16px 4px;
  font-size: var(--seele-t-micro);
  letter-spacing: 0.18em;
  color: var(--seele-rotulo-painel);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* `canal.cabecalho` substituído (rascunho da API 6, §3): o miolo da barra, no
   lugar do espaço, fechado — o nome do canal e a lupa, dos dois lados, são do
   produto, e nada do MOD passa por cima deles. **A base é zero**, como a do
   espaço: o miolo cresce no que sobra e nunca tira largura do nome. Com
   `auto`, a base era o conteúdo do MOD, e um conteúdo largo espremia o nome do
   canal de 43 px para 7 (medido em `regioes-do-produto.cjs`). **E com teto de
   altura**: sem ele, um miolo alto fazia a barra crescer até 1 149 px e
   empurrava a conversa para fora da janela (medido); o que passar rola por
   dentro. */
.canal-cabecalho-miolo {
  flex: 1 1 0;
  max-height: 96px;
  overflow-x: hidden;
  overflow-y: auto;
  contain: paint;
}

.linha-barra:has(> .canal-cabecalho-miolo:not([hidden])) > .espaco { display: none; }

.linha-barra-cerquilha {
  font-size: 16px;
  color: var(--seele-laranja-nerv);
  flex: 0 0 auto;
}

.linha-nome {
  font-family: var(--seele-display);
  font-weight: var(--seele-cartela);
  font-size: 17px;
  letter-spacing: 0.06em;
  color: var(--seele-osso);
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* O nome como botão de renomear, para quem administra — o mesmo desenho que
   `button.voice_room-nome`, e pela mesma razão: ele **é** o nome, e desenhá-lo
   como botão faria a barra parecer uma barra de ferramentas. O que diz que ele
   responde é o cursor e o sublinhado ao passar, mais o `title` e o `aria-label`,
   que dizem o verbo por extenso para quem não tem mouse nem enxerga. */
button.linha-nome {
  font: inherit;
  text-align: left;
  padding: 0;
  background: none;
  border: 0;
  cursor: pointer;
}

button.linha-nome:hover { text-decoration: underline; }

/* Sem canal aberto a frase é uma frase — em minúscula, na tipografia de dado,
   para não parecer o nome de um canal chamado "nenhum canal aberto". */
.linha-nome-vazio {
  font-family: var(--seele-mono);
  font-weight: normal;
  font-size: var(--seele-t-dado);
  letter-spacing: 0;
  color: var(--seele-osso-apagado);
}

/* A lupa: 34×34 quadrada, contornada, sem rótulo ao lado. Valores da comp.

   Ela teve `BUSCAR` escrito ao lado por uma versão. Cabeçalho e compositor são
   a mesma coluna, e as duas palavras — `BUSCAR` e `ARQUIVO` — empurravam o nome
   do canal e o campo de escrever para dentro. O que o rótulo pagava, o
   `aria-label` e o `title` pagam sem ocupar largura. */
.linha-buscar {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  padding: 0;
  font: inherit;
  color: var(--seele-osso-apagado);
  background: none;
  border: 1px solid var(--seele-linha-forte);
  cursor: pointer;
}

.linha-buscar:hover,
.linha-buscar[aria-expanded="true"] {
  color: var(--seele-laranja-nerv);
  border-color: var(--seele-laranja-nerv);
}

/* ---------- mensagens ---------- */

/* O vão de 14px que separava toda mensagem virou dois vãos: 4px dentro de uma
   rajada da mesma pessoa e 14px entre rajadas. O espaço passa a dizer o que o
   nome repetido dizia, e é o que faz o agrupamento se ler sem contar nomes. */
.mensagens {
  list-style: none;
  margin: 0;
  padding: 16px 16px 8px;
  overflow-y: auto;
  flex: 1 1 auto;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.mensagem:not(.mensagem-segue) { margin-top: 10px; }
.mensagens > li:first-child { margin-top: 0; }

/* A grade do comp v3: 34px de avatar, o resto para autor, hora e corpo, com uma
   marca de 2px à esquerda. A marca é onde o comp separa mensagem de sistema e
   de alerta; `Message` não tem tipo, então aqui ela só separa a própria das
   outras. */
.mensagem {
  display: grid;
  grid-template-columns: 34px minmax(0, 1fr);
  gap: 12px;
  border-left: 2px solid var(--seele-linha);
  padding-left: 12px;
}

.mensagem.propria { border-left-color: var(--seele-linha-forte); }

/* O avatar de iniciais. Contorno e não bloco cheio: cheio, uma conversa de seis
   pessoas vira seis quadrados sólidos numa coluna, e o que se lê passa a ser a
   coluna de quadrados. */
.mensagem-avatar {
  width: 34px;
  height: 34px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--seele-display);
  font-weight: 900;
  font-size: 14px;
  color: var(--seele-osso-apagado);
  border: 1px solid var(--seele-linha-forte);
}

.mensagem.propria .mensagem-avatar {
  color: var(--seele-padrao-azul);
  border-color: var(--seele-padrao-azul);
}

.mensagem-conteudo { display: flex; flex-direction: column; gap: 3px; min-width: 0; }

.mensagem-cabeca { display: flex; align-items: baseline; gap: 9px; flex-wrap: wrap; }

.mensagem-autor {
  font-size: var(--seele-t-dado);
  letter-spacing: 0.04em;
  color: var(--seele-osso-apagado);
}

.mensagem.propria .mensagem-autor { color: var(--seele-padrao-azul); }

/* O comp pinta a hora mais fraca ainda que o apagado. Aqui ela fica no apagado:
   `docs/tokens-achados.md` já mede aquele em 4,11:1 e o classifica como
   texto-grande-só, e descer mais é pôr um dado real abaixo do piso de 3:1 que
   `.varredura` foi calibrada para preservar. A hierarquia que o comp queria
   sai do tamanho — 10px contra os 13px do nome —, não de mais um degrau de
   cinza. */
.mensagem-hora {
  font-size: var(--seele-t-rotulo);
  color: var(--seele-osso-apagado);
  font-variant-numeric: tabular-nums;
}

/* ---- a continuação de uma rajada ----

   Mesma pessoa, mesmo dia, e perto no relógio: o avatar e o nome não se
   repetem, e a calha de 34px que o avatar ocupava recebe a hora. A hora
   **fica**, e é o ponto: a solução comum é escondê-la até alguém passar o
   ponteiro por cima, e um dado que só o ponteiro revela é um dado que não está
   na tela para quem usa teclado, para quem toca, e para quem nunca varreu
   aquela linha por acaso. */
.mensagem-hora-calha {
  width: 34px;
  display: flex;
  justify-content: flex-end;
  /* Alinhada com a primeira linha do corpo, e não com o topo da caixa: o corpo
     tem `line-height: 1.55`, então o topo dele não é onde o texto começa. */
  padding-top: 3px;
}

/* O divisor de dia. Um rótulo entre dois traços, que é a forma que não pede
   cor nenhuma para se ler como divisão.

   `HOJE` e `ONTEM` por extenso; ver `rotuloDoDia`. Os traços vêm de
   `::before`/`::after` porque são desenho: um `<span>` vazio de cada lado
   seria dois nós a mais por dia de conversa, e nenhum deles diz nada. */
.mensagens-dia {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 18px 0 4px;
  font-family: var(--seele-mono);
  font-size: var(--seele-t-micro);
  letter-spacing: 0.2em;
  color: var(--seele-rotulo-painel);
}

.mensagens-dia::before,
.mensagens-dia::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--seele-linha);
}

/* A primeira mensagem do dia já foi separada pelo divisor. */
.mensagens-dia + .mensagem { margin-top: 0; }

/* `pre-wrap`, e isto é contratual: a busca do outro lado da ponte indexou esta
   string com as quebras de linha dentro, e colapsá-las aqui deslocaria o realce
   em toda mensagem de mais de uma linha. */
.mensagem .corpo {
  font-size: var(--seele-t-corpo);
  line-height: 1.55;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
  text-wrap: pretty;
}

.mensagens .editada { color: var(--seele-osso-apagado); font-size: 11px; }

/* ---- o estado de um envio que ainda não foi gravado ----

   Só aparece nas mensagens que esta máquina escreveu e o servidor não confirmou.
   Uma mensagem gravada não ganha selo nenhum: um selo em toda linha seria ruído
   por cima da conversa inteira para dizer o que é verdade sobre quase tudo.

   Zero border-radius e zero sombra, como o resto. A cor é o que separa os três
   estados, e a frase é o que os diz — nunca só a cor. */
.mensagens .envio {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  font-size: 11px;
  letter-spacing: 0.04em;
}
.mensagens .envio-frase { color: var(--seele-osso-apagado); }
/* «enviando» é o estado de espera, e ele é discreto de propósito: quase toda
   mensagem passa por aqui por um instante, e piscar em laranja a cada linha
   escrita transformaria o normal em alarme. */
.mensagens .envio[data-envio="enviando"] .envio-frase { color: var(--seele-osso-apagado); }
/* Recusada é o único estado que não volta por si. Vermelho é exclusivo de alerta
   e queda por token, e uma mensagem que o servidor recusou é exatamente isso
   para quem a escreveu: ela não existe e não vai passar a existir sozinha. */
.mensagens .envio[data-envio="falhou"] .envio-frase { color: var(--seele-vermelho-alerta); }
/* Sem resposta não é falha: pode ter sido gravada. Laranja, que é o acento de
   «isto pede sua atenção» sem afirmar o que não se sabe. */
.mensagens .envio[data-envio="sem-resposta"] .envio-frase { color: var(--seele-laranja-nerv); }
.mensagens .envio-acao {
  background: transparent;
  border: 1px solid var(--seele-linha-forte);
  color: var(--seele-osso);
  font: inherit;
  font-size: 10px;
  letter-spacing: 0.08em;
  padding: 2px 6px;
  cursor: pointer;
}
.mensagens .envio-acao:hover,
.mensagens .envio-acao:focus-visible {
  border-color: var(--seele-laranja-nerv);
  color: var(--seele-laranja-nerv);
}

/* O aviso de tamanho do compositor, contado em bytes.

   Vermelho porque, passado o teto, mandar não é uma opção — e o número está na
   frase justamente para que «corte o texto» tenha quanto. */
.campo-tamanho {
  display: block;
  color: var(--seele-vermelho-alerta);
  font-size: 11px;
  letter-spacing: 0.04em;
  padding-top: 4px;
}
/* A moldura do campo acompanha, para quem lê a caixa antes de ler a frase. */
#campo-mensagem[data-grande="sim"] { border-color: var(--seele-vermelho-alerta); }

/* O compositor do v3: o campo numa moldura própria, e `ENVIAR` com rótulo ao
   lado dela. 44px de altura nos dois, que é o que o comp escreve. */
/* A região `compositor` (rascunho da API 6, §3): acima da fileira de escrever,
   fechada como as outras, e com teto de altura — a caixa de escrever não sai da
   vista por causa dela. */
.compositor-mods {
  flex: 0 0 auto;
  max-height: 96px;
  padding: 0 16px;
  overflow-x: hidden;
  overflow-y: auto;
  contain: paint;
}

.compor {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 12px 16px;
  border-top: 1px solid var(--seele-linha);
  flex: 0 0 auto;
}

.compor .prompt { color: var(--seele-laranja-nerv); font-size: var(--seele-t-corpo); flex: 0 0 auto; }

/* O campo de escrever.

   **`input` e `textarea`.** Ele virou `<textarea>` quando o Shift+Enter passou
   a quebrar parágrafo, e esta regra continuou selecionando só `input`: o campo
   ficou com a aparência padrão do WebKit — fundo branco, largura de umas
   poucas dezenas de pixels — dentro de uma moldura escura larga. A validação
   nativa de 20/09/2026 encontrou assim.

   `font: inherit` e não só `font-size`: um `textarea` não herda a família nem o
   peso do documento, e sem ele o que se digita sai numa fonte que não é a
   deste produto. */
.compor input,
.compor textarea {
  flex: 1;
  min-width: 0;
  width: 100%;
  border: none;
  background: none;
  color: inherit;
  font: inherit;
  font-size: var(--seele-t-corpo);
  padding: 8px 0;
}

/* A moldura em volta do `>` e do campo, para o botão sólido ao lado ter contra
   o que se destacar. `focus-within` e não `:focus` no campo: a borda é da
   caixa, e o que recebe o foco é o `<input>` dentro dela. */
.compor-caixa {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 0 12px;
  min-height: 44px;
  border: 1px solid var(--seele-linha-forte);
  background: var(--seele-negro-painel);
}

.compor-caixa:focus-within { border-color: var(--seele-laranja-nerv); }

/* O campo cresce; a moldura acompanha. Com o `<textarea>` de várias linhas, o
   `align-items: center` de `.compor-caixa` empurraria o `>` para o meio da
   altura e o botão de anexar junto. Eles ficam no alto, alinhados com a
   primeira linha do que se escreve. */
.compor-caixa:has(textarea) { align-items: flex-start; padding-top: 0; padding-bottom: 0; }
.compor-caixa:has(textarea) .prompt { padding-top: 12px; }

/* O `ENVIAR` saiu na 0.9.0 — ver `index.html`. A instrução que ele carregava
   mora agora no `placeholder`, que é onde se olha quando se precisa dela. */

/* O documento: 44×44, a altura do campo de escrever ao lado. Valores da comp.

   Não há `ENVIAR` para ele contrastar desde a 0.9.0 — Enter envia —, então o
   par que ele formava com um botão sólido deixou de existir, e com ele a razão
   de a palavra estar escrita. */
/* O botão ENVIAR — U17.

   Pequeno e secundário de propósito: a tecla continua sendo o caminho rápido,
   e o botão é o que torna o caminho **visível**. Quem nunca leu a ajuda nem o
   `placeholder` — que some na primeira letra digitada — ficava com uma barra em
   que nada parecia mandar, e para quem navega por toque não há tecla nenhuma. */
.compor-enviar {
  flex: 0 0 auto;
  align-self: end;
  min-height: 34px;
  padding: 0 12px;
  border: 1px solid var(--seele-linha-forte);
  background: none;
  color: var(--seele-osso);
  font-family: var(--seele-display);
  font-size: var(--seele-t-rotulo);
  letter-spacing: var(--seele-tracking-rotulo);
  cursor: pointer;
}

.compor-enviar:hover { border-color: var(--seele-laranja-nerv); color: var(--seele-laranja-nerv); }
.compor-enviar:focus-visible { outline: 2px solid var(--seele-laranja-nerv); outline-offset: 2px; }

/* O campo cresce com o que se escreve, até o teto que `cresceOCampo` aplica.
   `resize: none` porque a alça do navegador competiria com esse crescimento —
   duas coisas mexendo na mesma altura, e a última a mexer ganhando. */
#campo-mensagem {
  resize: none;
  overflow-y: auto;
  max-height: 160px;
  line-height: 1.45;
}

.compor-anexar {
  width: 44px;
  height: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  padding: 0;
  font: inherit;
  color: var(--seele-osso-apagado);
  background: none;
  border: 1px solid var(--seele-linha-forte);
  cursor: pointer;
}

.compor-anexar:hover {
  color: var(--seele-laranja-nerv);
  border-color: var(--seele-laranja-nerv);
}

/* ---------- anexos (ADR 0027) ---------- */

/* O arquivo escolhido, acima do campo e fora da conversa.

   Fora dela porque enquanto sobe **não há mensagem**: o servidor só publica quando
   os bytes chegam inteiros, então não existe linha na Linha em que pendurar
   isto. É a metade que o ADR paga de propósito, e desenhar uma mensagem
   fantasma aqui seria desfazer a escolha. */
.anexo-pendente {
  flex: 0 0 auto;
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 10px 16px;
  border-top: 1px solid var(--seele-linha);
  background: var(--seele-negro-painel);
}

.anexo-linha {
  display: flex;
  align-items: center;
  gap: 10px;
}

.anexo-nome {
  font-family: var(--seele-mono);
  font-size: var(--seele-t-dado);
  color: var(--seele-osso);
  overflow-wrap: anywhere;
  min-width: 0;
}

.anexo-tamanho {
  font-family: var(--seele-mono);
  font-size: var(--seele-t-rotulo);
  color: var(--seele-osso-apagado);
  flex: 0 0 auto;
}

/* A barra por bytes. `appearance: none` porque o desenho nativo do
   `<progress>` traz o azul do sistema, que não é cor deste produto. */
.anexo-barra {
  appearance: none;
  width: 100%;
  height: 4px;
  border: none;
  background: var(--seele-linha);
}

.anexo-barra::-webkit-progress-bar { background: var(--seele-linha); }
.anexo-barra::-webkit-progress-value { background: var(--seele-laranja-nerv); }
.anexo-barra::-moz-progress-bar { background: var(--seele-laranja-nerv); }

/* O estado, e **é aqui que a queda aparece**. Osso apagado, não vermelho: o
   vermelho é exclusivo de alerta e de queda de enlace neste produto, e uma
   transferência que caiu é um contratempo de um arquivo, não a sessão indo
   embora. Chamá-la de vermelha ensinaria a ignorar o vermelho. */
.anexo-estado {
  font-family: var(--seele-mono);
  font-size: var(--seele-t-rotulo);
  color: var(--seele-osso-apagado);
  white-space: pre-line;
}

/* O bloco embaixo de uma mensagem que tem arquivo. */
.anexo {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 6px;
  padding: 6px 10px;
  border: 1px solid var(--seele-linha-forte);
  background: var(--seele-negro-painel);
}

.anexo-arquivo {
  font-family: var(--seele-mono);
  font-size: var(--seele-t-dado);
  color: var(--seele-osso);
  overflow-wrap: anywhere;
  min-width: 0;
}

/* Expirado: a moldura tracejada é o que diz «houve um arquivo aqui e ele não
   está mais», sem gastar cor. O nome e o tamanho continuam legíveis, porque é
   exatamente isso que o servidor guardou a linha para poder dizer. */
.anexo-expirado {
  border-style: dashed;
  color: var(--seele-osso-apagado);
}

.anexo-expirado .anexo-arquivo { color: var(--seele-osso-apagado); }

/* SALVAR, e nunca ABRIR. O ADR 0027 não dá a cliente nenhum do SEELE um botão
   que abre arquivo, e esta é a única ação que este bloco oferece. */
.anexo-salvar {
  padding: 3px 10px;
  font-family: var(--seele-display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--seele-osso-apagado);
  background: none;
  border: 1px solid var(--seele-linha-forte);
  cursor: pointer;
  flex: 0 0 auto;
}

.anexo-salvar:hover {
  color: var(--seele-laranja-nerv);
  border-color: var(--seele-laranja-nerv);
}

/* PRÉVIA, ao lado de SALVAR e nunca no lugar dele. Prever não é abrir e não é
   salvar: os bytes vão para a memória desta janela, não para o disco. */
.anexo-previa {
  padding: 3px 10px;
  font-family: var(--seele-display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--seele-osso-apagado);
  background: none;
  border: 1px solid var(--seele-linha-forte);
  cursor: pointer;
  flex: 0 0 auto;
}

.anexo-previa:hover {
  color: var(--seele-laranja-nerv);
  border-color: var(--seele-laranja-nerv);
}

.anexo-previa[disabled] {
  cursor: progress;
  border-style: dashed;
}

/*
  O FECHAR de uma prévia aberta.

  Mesmo desenho do PRÉVIA — é o mesmo par de estados do mesmo controle —, mas
  posicionado sobre a figura, no canto de cima e à direita, para não empurrar a
  conversa para baixo com uma linha a mais por imagem aberta.

  `position: absolute` sobre um `.anexo-desenho` que ganha `position: relative`
  logo abaixo. Fundo opaco, e não translúcido: por cima de uma foto clara, um
  rótulo translúcido some, e `06-clientes-gui.md` proíbe informação que dependa
  de sorte para ser lida.
*/
.anexo-previa-fechar {
  position: absolute;
  top: 6px;
  right: 6px;
  padding: 3px 10px;
  font-family: var(--seele-display);
  font-weight: 700;
  font-size: 11px;
  letter-spacing: 0.14em;
  color: var(--seele-osso-apagado);
  background: var(--seele-negro-absoluto);
  border: 1px solid var(--seele-linha-forte);
  cursor: pointer;
}

.anexo-previa-fechar:hover {
  color: var(--seele-laranja-nerv);
  border-color: var(--seele-laranja-nerv);
}

/* Numa recusa não há figura sobre a qual flutuar: ele fica na linha. */
.anexo-recusa .anexo-previa-fechar {
  position: static;
  margin-left: 10px;
}

/* A figura ocupa a linha inteira do bloco: `flex-basis: 100%` é o que a tira de
   dentro da fileira do nome em vez de espremê-la ao lado dele. */
.anexo-desenho {
  /* A âncora do FECHAR, que flutua no canto dela. */
  position: relative;
  flex: 0 0 100%;
  display: block;
  margin-top: 6px;
  min-width: 0;
}

/* O teto de bytes da prévia limita o download; ele **não** limita os pixels que
   a decodificação produz. Este limite é o outro: o que a janela desenha cabe na
   conversa, venha a imagem com a dimensão que vier. */
.anexo-desenho img {
  display: block;
  max-width: 100%;
  max-height: 320px;
  width: auto;
  height: auto;
  object-fit: contain;
  border: 1px solid var(--seele-linha);
  background: var(--seele-negro-absoluto);
}

/* Por que não há figura, escrito onde a pessoa está olhando. Uma recusa contada
   só à caixa de um pixel do leitor de tela é, para quem enxerga, a mesma coisa
   que nada ter acontecido — o ADR 0027 já pagou uma vez por isso. */
.anexo-recusa {
  flex: 0 0 100%;
  margin-top: 6px;
  padding: 6px 8px;
  border-left: 2px solid var(--seele-laranja-nerv);
  font-family: var(--seele-mono);
  font-size: var(--seele-t-rotulo);
  line-height: 1.5;
  color: var(--seele-osso-apagado);
  white-space: pre-line;
}

/* ---------- coluna 4: as pessoas ----------

   Esta coluna era o sinal: a média da sala num bloco de 52px e uma lista de
   quem estava na sala ocupada. A avaliação de usabilidade achou nisso o pior
   erro de alocação da tela — a faixa permanente de 328px de uma ferramenta de
   comunicação ocupada por um dado de diagnóstico, com a lista de pessoas sem
   aparecer em tela nenhuma fora da coluna de 268px, e lá só para a sala em que
   se está.

   A troca: as pessoas ocupam a faixa, o sinal de cada uma continua sendo uma
   linha dentro do cartão dela, e a média — que não é de ninguém — desceu para o
   rodapé, ao lado das outras medidas. O cartão não mudou: as quatro faixas de
   informação dele já tinham acompanhante textual, e era isso que não se podia
   perder na mudança. */

/* A cartela invertida. O inventário §12 endurece essa regra no v2: cabeçalho de
   painel é **bloco sólido de cor com texto no negro absoluto**, e nunca texto
   colorido sobre fundo escuro. É a ênfase invertida da TUI. */
.pessoas-cartela {
  margin: 0;
  padding: 8px 16px;
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  background: var(--seele-laranja-nerv);
  color: var(--seele-negro-absoluto);
  font-family: var(--seele-display);
  font-weight: 900;
  font-size: 15px;
  letter-spacing: 0.14em;
  flex: 0 0 auto;
}

/* A contagem, dentro da cartela e no mesmo negro: um segundo tom aqui seria
   texto apagado sobre laranja, que é a metade da regra do §12 que se perde
   primeiro. Peso normal e corpo pequeno bastam para ela não competir com o
   título. */
.pessoas-conta {
  font-family: var(--seele-mono);
  font-size: var(--seele-t-micro);
  font-weight: normal;
  letter-spacing: 0.14em;
  flex: 0 0 auto;
}

/* A nota que diz de quem esta lista é — e de quem ela não é. Ela é permanente
   e não um `title`: o que falta aqui não é um detalhe de quem reparou, é o
   alcance da lista inteira. */
.painel-pessoas .nota {
  padding: 8px 16px;
  border-bottom: 1px solid var(--seele-linha);
  flex: 0 0 auto;
}

.roster {
  list-style: none;
  margin: 0;
  padding: 0;
  overflow-y: auto;
  flex: 1 1 auto;
}

.roster-grupo { display: block; }

/* O cabeçalho de um grupo: o nome da sala e a ocupação, o mesmo par que a
   coluna de salas escreve. Grudento porque a lista rola e um nome de sala que
   sai da tela deixa os cartões de baixo sem dono. */
.roster-sala {
  margin: 0;
  position: sticky;
  top: 0;
  z-index: 1;
  display: flex;
  align-items: baseline;
  gap: 8px;
  padding: 8px 14px;
  background: var(--seele-negro-painel);
  border-bottom: 1px solid var(--seele-linha);
  font-family: var(--seele-mono);
  font-size: var(--seele-t-micro);
  font-weight: normal;
  letter-spacing: 0.16em;
  color: var(--seele-rotulo-painel);
}

.roster-sala-nome {
  color: var(--seele-osso-apagado);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.roster-sala-conta { flex: 0 0 auto; }

/* A sala em que se está, marcada na borda **e** em palavra: a borda é cor, e
   `specs/06-clientes-gui.md` não aceita informação que só a cor carregue. */
.roster-grupo[data-nossa="sim"] .roster-sala {
  border-left: 2px solid var(--seele-laranja-nerv);
  color: var(--seele-laranja-nerv);
}

.roster-grupo[data-nossa="sim"] .roster-sala-nome { color: var(--seele-laranja-nerv); }

.roster-sala-aqui {
  margin-left: auto;
  flex: 0 0 auto;
  letter-spacing: 0.1em;
  text-transform: lowercase;
}

.roster-pessoas {
  list-style: none;
  margin: 0;
  padding: 0;
}

/* **FORA DO AR** (D-fio-4, P6): quem é membro e não está conectado. Apagado, e
   só o nome — nada ali foi medido e nada ali responde.

   Apagado com `--seele-rotulo-painel`, e não com `osso-apagado`: o nome é a
   única coisa que a linha diz, e aquele mede 4,11:1, que só vale para texto
   grande. Este mede 5,52:1 e continua abaixo do `osso` de quem está aqui, que
   é a diferença que o grupo precisa mostrar. */
.membro-fora-do-ar {
  border-bottom: 1px solid var(--seele-linha);
  padding: 6px 14px;
  font-size: var(--seele-t-corpo);
  letter-spacing: 0.02em;
  color: var(--seele-rotulo-painel);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* A região `pessoas.lista` (rascunho da API 6, §3): acima da lista, fechada e
   com teto de altura, como as outras regiões. */
.roster-mods {
  flex: 0 0 auto;
  max-height: 96px;
  padding: 0 14px;
  overflow-x: hidden;
  overflow-y: auto;
  contain: paint;
}

/* O corpo de uma mensagem desenhado por um MOD (`conversa.mensagem`, rascunho da
   API 6, §3): fechado como as regiões — a cabeça acima, com o apelido
   verificado, e o anexo e o selo abaixo são do produto. */
.mensagem-miolo {
  display: block;
  min-width: 0;
  overflow: hidden;
  contain: paint;
}

/* O subtítulo de um grupo do MOD **dentro** de uma sala: o texto é do MOD, e a
   forma é do produto — recuado e com um filete à esquerda, que o cabeçalho de
   uma sala não tem. Sem isso, um MOD que intitulasse um grupo «Sala principal
   2/8» imitaria uma seção, que é a verdade que as seções guardam: quem está na
   sala de quem ouve você. */
.roster-subgrupo {
  list-style: none;
  margin: 8px 14px 2px;
  padding: 0 0 0 8px;
  border-left: 1px solid var(--seele-linha-forte);
  font-size: var(--seele-t-micro);
  letter-spacing: 0.18em;
  color: var(--seele-rotulo-painel);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.roster .pessoa {
  border-bottom: 1px solid var(--seele-linha);
  border-left: 2px solid transparent;
  padding: 10px 14px;
  display: flex;
  flex-direction: column;
  gap: 6px;
}

/* Quem está falando ganha a marca e o fundo, como no comp. */
.roster .pessoa.falando {
  border-left-color: var(--seele-fosforo);
  background: var(--seele-fosforo-fraco);
}

.pessoa-cabeca { display: flex; justify-content: space-between; align-items: baseline; gap: 8px; }

.pessoa-identidade { display: flex; align-items: baseline; gap: 8px; min-width: 0; }

/* **O nome é a coisa maior da linha** — U15.
 *
 * «A identidade domina pouco a faixa de pessoas; um sinal 100 e "TRANSMITINDO"
 * dominam inclusive fora de sala de voz.» A medida estava aqui: o número do
 * sinal era 26px e peso 900, e isto era o corpo de dado. A linha dizia a
 * medida e sussurrava de quem ela era.
 *
 * O número desceu para o diagnóstico recolhível, e o nome passou a ter o
 * tamanho de quem é o assunto da lista. */
.pessoa-nome {
  font-size: var(--seele-t-corpo);
  font-weight: 600;
  letter-spacing: 0.02em;
  color: var(--seele-osso);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* O diagnóstico, recolhido — U15.
 *
 * Recolher não é tirar: quem precisa do número continua a um toque dele, e o
 * resumo já o carrega. `specs/06-clientes-gui.md` não perde nada — o estado
 * continua em palavra, dentro. */
.pessoa-diagnostico { margin-top: 4px; }

.pessoa-diagnostico-resumo {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 8px;
  cursor: pointer;
  list-style: none;
  font-size: var(--seele-t-micro);
  letter-spacing: 0.12em;
  color: var(--seele-osso-apagado);
}

.pessoa-diagnostico-resumo::-webkit-details-marker { display: none; }

.pessoa-diagnostico-resumo:focus-visible {
  outline: 2px solid var(--seele-laranja-nerv);
  outline-offset: 2px;
}

/* O rótulo do resumo: pequeno e apagado, porque ele nomeia o que está dentro e
   não o que a linha diz. O valor ao lado é o que sobrevive ao recolhimento. */
.pessoa-diagnostico-rotulo { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.pessoa-diagnostico-valor { font-variant-numeric: tabular-nums; }

/* A marca de bloco: a metade da faixa que sobrevive sem cor
   (`specs/06-clientes-gui.md`). Em face monoespaçada porque a Saira Condensed,
   que desenha os números ao lado dela, não tem `U+2588`. Vale no cartão e na
   célula do rodapé, que são os dois lugares em que uma faixa é escrita. */
.sync-marca {
  font-family: var(--seele-mono);
  font-size: 11px;
  line-height: 1;
}

.pessoa-sync { display: flex; align-items: baseline; gap: 6px; flex: 0 0 auto; }

/* O número, dentro do diagnóstico. De 26px para o corpo de dado: ele deixou de
   competir com o nome porque deixou de estar ao lado dele. */
.pessoa-sync-valor {
  font-family: var(--seele-display);
  font-weight: 700;
  font-size: var(--seele-t-dado);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* A faixa pinta a marca, o número e a barra — nunca o nome, que precisa ser
   legível em qualquer estado. O acompanhante não-cromático é a marca de bloco,
   que é diferente em cada uma das três faixas. */
.pessoa[data-faixa="Nominal"] .pessoa-sync,
.pessoa[data-faixa="Nominal"] .barra { color: var(--seele-sync-nominal); }
.pessoa[data-faixa="Degraded"] .pessoa-sync,
.pessoa[data-faixa="Degraded"] .barra { color: var(--seele-sync-degradado); }
.pessoa[data-faixa="Critical"] .pessoa-sync,
.pessoa[data-faixa="Critical"] .barra { color: var(--seele-sync-critico); }

.pessoa .barra { font-size: 11px; letter-spacing: 0.02em; }

/* `flex-end` e não `space-between`: o `ATRASO` que ocupava a esquerda saiu com
   a regra de omissão, e um `space-between` de um filho só o encosta à esquerda,
   que é onde as pastilhas não estavam. */
.pessoa-rodape {
  display: flex;
  justify-content: flex-end;
  align-items: center;
  gap: 8px;
  font-size: var(--seele-t-micro);
  letter-spacing: 0.12em;
}

.pessoa-estados { display: flex; gap: 6px; flex-wrap: wrap; justify-content: flex-end; }

/* A pastilha de estado: bloco sólido, texto no negro absoluto (inventário §12).

   `EM ESCUTA` não ganha bloco, e a ausência é do comp e não economia daqui: o
   `estadoFundo` dele é `transparent` e o `estadoTxt` é o apagado, contra o
   laranja e o fósforo dos outros dois. É o que separa os dois estados que
   mudam alguma coisa do repouso, que é onde toda linha está quase sempre —
   seis pastilhas sólidas empilhadas não distinguiriam nada. Sem contorno
   tampouco: uma moldura em volta do repouso é o mesmo peso por outro meio. */
.pastilha {
  padding: 2px 6px;
  font-weight: 600;
  white-space: nowrap;
  color: var(--seele-negro-absoluto);
}

.pastilha[data-estado="at"] { background: var(--seele-laranja-nerv); }
.pastilha[data-estado="fala"] { background: var(--seele-fosforo); }
.pastilha[data-estado="surdo"] { background: var(--seele-osso); }
.pastilha[data-estado="escuta"] {
  background: none;
  color: var(--seele-osso-apagado);
}

/* O volume por pessoa fica escondido até a linha ser apontada: `specs/03-audio.md`
   pede o controle, e um deslizante por pessoa sempre visível transformaria o
   roster numa mesa de som. */
.volume {
  width: 100%;
  accent-color: var(--seele-laranja-nerv);
  opacity: 0;
}

.roster .pessoa:hover .volume,
.roster .pessoa:focus-within .volume { opacity: 1; }

/* ---------- rodapé de telemetria ---------- */

.telemetria {
  display: flex;
  align-items: stretch;
  height: 40px;
  border-top: 1px solid var(--seele-linha-forte);
  background: var(--seele-negro-painel);
  font-variant-numeric: tabular-nums;
  flex: 0 0 auto;
}

/* A região `status.barra` (rascunho da API 6, §3), na altura da barra, que é
   fixa. **Ela encolhe primeiro**: num item flex, `overflow: hidden` zera a
   largura mínima automática, e as métricas, em `nowrap`, não encolhem abaixo do
   texto delas. Sem o `overflow`, um MOD largo empurrava o aviso de falha local
   para fora da janela (medido em `regioes-do-produto.cjs`). */
.telemetria-mods {
  display: flex;
  align-items: center;
  flex: 0 1 auto;
  padding: 0 12px;
  overflow: hidden;
  contain: paint;
}

.metrica {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 16px;
  border-right: 1px solid var(--seele-linha);
  white-space: nowrap;
}

.metrica .rotulo {
  font-size: var(--seele-t-micro);
  letter-spacing: 0.18em;
  font-weight: normal;
  color: var(--seele-osso-apagado);
}

.metrica span { font-size: 12px; color: var(--seele-osso); }

/* A média de sinal da sala, que era o bloco de 52px da quarta coluna. Aqui ela
   é uma célula como as outras: número e marca coloridos pela faixa, e nunca o
   bloco sólido invertido — a inversão do inventário §12 é para cabeçalho de
   painel, e um retângulo laranja de altura inteira nesta fileira gritaria mais
   alto que o alerta que fica logo acima dela.

   A cor pinta o valor e a marca, e nunca o rótulo: `SINAL DA SALA` tem de ser
   legível nas três faixas. */
.metrica-sinal { gap: 6px; }

.metrica-sinal .rotulo { margin-right: 2px; }

.metrica-sinal[data-faixa="Nominal"] span { color: var(--seele-sync-nominal); }
.metrica-sinal[data-faixa="Degraded"] span { color: var(--seele-sync-degradado); }
.metrica-sinal[data-faixa="Critical"] span { color: var(--seele-sync-critico); }

.falha-local {
  display: flex;
  align-items: center;
  padding: 0 16px;
  color: var(--seele-vermelho-alerta);
  font-size: 11px;
}

/* As três luzes. O quadrado é vazado porque não há leitura: o protocolo sabe
   dizer que **algo** mudou de subsistema (`NoticeReason::SubsystemChanged`) e
   não qual nem para qual estado. Três quadrados cheios em fósforo seriam três
   afirmações de saúde que ninguém mediu.

   `.luzes` e não `.magi`, que seria o nome óbvio: `tela-boot.css` já tem um
   `.magi`, que é o diagrama de polígonos daquela tela, e esta folha carrega
   depois dela. Duas telas com o mesmo nome de classe não é ambiguidade de
   leitura — é a última regra vencendo o empate, e o `.magi li` daqui alcançava
   os `<li>` do diagrama de lá com especificidade maior que a do `.magi-no` que
   deveria pintá-los. */
.luzes {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  align-items: stretch;
}

.luzes li {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 0 14px;
  border-left: 1px solid var(--seele-linha);
  font-size: var(--seele-t-micro);
  letter-spacing: 0.14em;
  color: var(--seele-osso-apagado);
}

.luz-marca {
  width: 6px;
  height: 6px;
  border: 1px solid var(--seele-osso-apagado);
}

/* ---- o link da porta, onde quer que ele apareça ----

   A faixa laranja que morava no topo da sessão saiu: a comp não a desenha, e o
   link já tinha dois lugares melhores — o diálogo que aparece uma vez para quem
   acaba de hospedar, e a seção `A PORTA` da configuração, onde ele mora depois
   disso. Uma faixa permanente sobre a sessão para um texto que se copia uma vez
   é o produto cobrando atenção todo dia por uma coisa que se faz num dia.

   O que sobra é o campo, que os dois lugares usam.

   O link inteiro, selecionável. Um campo que corta o fim do endereço produz
   convites que não funcionam e ninguém entende por quê. */
.convite-link {
  flex: 1;
  min-width: 0;
  font: inherit;
  color: var(--seele-osso);
  background: var(--seele-negro-absoluto);
  border: 1px solid var(--seele-linha-forte);
  padding: 4px 8px;
}


/* Até onde o link chega (ADR 0022).
   `pre-line` porque as três frases do `FRASES` são duas linhas — um título em
   caixa alta e a explicação —, e o `\n` entre elas é a divisão. */
.convite-alcance {
  margin: 6px 0 0;
  white-space: pre-line;
  color: var(--seele-osso);
}

/* Nenhum vermelho aqui, e é decisão e não descuido: `tokens.css` reserva o
   vermelho a alerta e queda, e um link que só alcança a rede de casa não é
   nenhum dos dois — ele funciona, e funciona menos longe do que a pessoa
   provavelmente quer.

   O laranja já é a cor deste bloco, então chamar atenção com ele não introduz
   vocabulário novo: é o mesmo bloco falando mais alto. */
/* O resumo, num nó próprio — U13. Ver `mostrarAlcance` em `tela-boot.js`: o
   nome acessível de um elemento concatena o texto dos filhos, e um nó de texto
   solto ao lado de três `<span>` era lido como uma frase só. */
.convite-alcance-resumo { display: block; }

.convite-alcance-curto { color: var(--seele-laranja-nerv); }
.convite-alcance-longe { color: var(--seele-fosforo); }

/* O que o roteador respondeu, quando respondeu. Secundário de propósito: é
   texto que o modelo do roteador inventou, útil para quem for investigar e
   ruído para quem só queria o link. */
.convite-alcance-detalhe {
  display: block;
  margin-top: 4px;
  font-size: var(--seele-t-rotulo);
  color: var(--seele-osso-apagado);
}

/* A parede: a regra de firewall desta máquina não cobre o programa que está
   escutando, então **ninguém** entra — nem quem está na mesma rede.

   Ela não é `-detalhe` apagado porque não explica um alcance menor: explica
   alcance nenhum. Mais clara que os detalhes e com uma barra à esquerda, que é
   como esta interface separa «isto é sobre o que você tentou» de «isto é sobre
   o que está impedindo». Sem vermelho: `tokens.css` reserva essa cor para
   alerta e queda, e uma porta fechada é nenhum dos dois. */
/* O botão que resolve a parede, logo abaixo dela.

   Vazado e não cheio: ele é o caminho e não o assunto — o assunto é a frase
   acima. E laranja porque é a única coisa nesta tela que pede um gesto. */
.convite-alcance-conserto {
  display: block;
  margin-top: 8px;
  margin-left: 8px;
  font: inherit;
  font-size: var(--seele-t-rotulo);
  letter-spacing: var(--seele-tracking-rotulo);
  text-transform: uppercase;
  color: var(--seele-laranja-nerv);
  background: none;
  border: 1px solid var(--seele-laranja-nerv);
  padding: 6px 12px;
  cursor: pointer;
}

.convite-alcance-conserto:hover:not(:disabled) {
  color: var(--seele-negro-absoluto);
  background: var(--seele-laranja-nerv);
}

.convite-alcance-conserto:disabled {
  color: var(--seele-linha-forte);
  border-color: var(--seele-linha);
  cursor: progress;
}

.convite-alcance-parede {
  display: block;
  margin-top: 8px;
  padding-left: 8px;
  border-left: 2px solid var(--seele-laranja-nerv);
  font-size: var(--seele-t-rotulo);
  color: var(--seele-osso);
}

/* ---- bateria interna ----
   `specs/07-estetica.md`: contagem em vermelho, interface esmaecida mas
   **ainda legível**. Esmaecer não é esconder: o histórico continua ali para
   leitura, e é isso que separa "segurando a sessão" de "caiu". */
.bateria {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding: 8px 16px;
  border-bottom: var(--seele-b) solid var(--seele-vermelho-alerta);
  background: var(--seele-vermelho-fraco);
  color: var(--seele-vermelho-alerta);
}

.bateria-titulo {
  font-size: var(--seele-t-rotulo);
  letter-spacing: var(--seele-tracking-rotulo);
}

/* A contagem é o que diz se vale esperar ou ir consertar o wifi, então é o
   maior elemento da faixa. */
.bateria-conta {
  font-size: var(--seele-t-secao);
  font-variant-numeric: tabular-nums;
}

.bateria-tentativas {
  font-size: var(--seele-t-rotulo);
  letter-spacing: var(--seele-tracking-rotulo);
  color: var(--seele-osso-apagado);
}

/* Esmaecido, e não desabilitado: dá para ler e rolar o que já foi dito. */
.na-bateria .paineis { opacity: 0.55; }
.na-bateria .compor { opacity: 0.4; }


/* ---- a busca no painel de mensagens ---- */
.busca {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 16px;
  border-bottom: 1px solid var(--seele-linha);
  flex: 0 0 auto;
}

.busca .prompt { color: var(--seele-laranja-nerv); }
.busca input { flex: 1; min-width: 0; border: none; background: none; padding: 0; }
.busca .botao-fantasma { margin-left: 0; }

/* O contador é o acompanhante textual do realce: `specs/06-clientes-gui.md`
   proíbe informação transmitida só por cor, e "[2/7]" é a mesma informação sem
   depender de enxergar o laranja.

   Osso, e não osso-apagado. O `tokens.css` anota o apagado como "só texto
   grande", e 10px não é texto grande — o mínimo que vale aqui é 4,5:1. Um
   rótulo secundário podia viver com isso; este elemento não, porque ele é
   precisamente a metade não-cromática de que o realce não pode abrir mão. */
.busca-contador {
  font-size: var(--seele-t-rotulo);
  color: var(--seele-osso);
  font-variant-numeric: tabular-nums;
}

/* O realce, e o realce onde o cursor está — duas coisas, e a diferença entre
   elas nunca é só a cor (`specs/06-clientes-gui.md:144`).

   O terminal resolveu isto com REVERSED contra o acento puro (`ui.rs`), e aqui
   é a mesma ideia com o que o navegador tem: o corrente inverte o bloco e
   engrossa o traço. Sem cor nenhuma continuam distintos — um é texto claro
   sobre bloco escuro, o outro é texto escuro sobre bloco claro e em negrito. */
/* ---------- a imagem de um link de imagem ----------

   Desenhada embaixo do link, e não no lugar dele: quem quiser a origem
   continua podendo abri-la, e uma imagem que não carrega não deixa a mensagem
   sem nada.

   O teto é de desenho, e não de bytes — o de bytes é do Rust, 4 MiB, o mesmo
   do anexo. Aqui o que se impede é uma imagem alta demais empurrar a conversa
   inteira para fora da tela: quem rola procurando o que foi dito não pode ter
   de atravessar um GIF de tela cheia por mensagem.

   `display: block` porque ela vem depois de texto corrido, e `object-fit`
   para que o teto não a deforme. */
.previa-de-link {
  display: block;
  margin-top: 0.375rem;
  max-width: min(100%, 420px);
  max-height: 320px;
  object-fit: contain;
  border: 1px solid var(--seele-osso-apagado);
}

/* ---------- o convite de prévia, no lugar da imagem ----------

   ADR 0053. Uma imagem cujo domínio não foi liberado não é buscada, e o que
   ocupa o lugar dela é isto: o nome do domínio e um botão. O nome está aqui de
   propósito — a escolha é sobre entregar o endereço desta máquina a um
   servidor, e um botão que dissesse só «mostrar imagem» esconderia a quem.

   Discreto, na moldura de sempre, e nunca em vermelho: não há nada de errado
   acontecendo. É uma pergunta. */
.previa-convite {
  display: flex;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
  margin-top: 0.375rem;
  padding: 6px 8px;
  border: 1px dashed var(--seele-linha-forte);
  max-width: min(100%, 420px);
}
.previa-convite-frase {
  color: var(--seele-osso-apagado);
  font-size: 11px;
  letter-spacing: 0.04em;
  overflow-wrap: anywhere;
  min-width: 0;
}
.previa-convite-acao {
  background: transparent;
  border: 1px solid var(--seele-linha-forte);
  color: var(--seele-osso);
  font: inherit;
  font-size: 10px;
  letter-spacing: 0.08em;
  padding: 2px 6px;
  cursor: pointer;
  flex: 0 0 auto;
}
.previa-convite-acao:hover,
.previa-convite-acao:focus-visible {
  border-color: var(--seele-laranja-nerv);
  color: var(--seele-laranja-nerv);
}

/* ---------- endereços dentro de uma mensagem ----------

   Sublinhado **e** cor, e não cor sozinha: a cor é a informação que some
   primeiro — em daltonismo, em tela ruim, no contraste alto do sistema —, e um
   endereço que só se distingue pela cor não se distingue. O sublinhado
   atravessa os três casos.

   O cursor é o de sempre para algo clicável, e o `title` do elemento diz o
   endereço inteiro: quem vai clicar num link escrito por outra pessoa lê para
   onde ele vai antes de ir.

   `word-break` porque um endereço longo não quebra sozinho, e um só deles
   esticaria a coluna da conversa inteira. */
.link-externo {
  color: var(--seele-laranja-nerv);
  text-decoration: underline;
  text-underline-offset: 2px;
  cursor: pointer;
  word-break: break-all;
}

.link-externo:hover,
.link-externo:focus-visible {
  background: var(--seele-laranja-fraco);
  color: var(--seele-osso);
}

/* Recusado pelo Rust — esquema que não sai desta janela, ou navegador que não
   abriu. O `title` já carrega o motivo; isto é o sinal de que houve um. */
.link-externo.link-recusado {
  color: var(--seele-osso);
  text-decoration-style: wavy;
  cursor: not-allowed;
}

.realce {
  background: var(--seele-laranja-fraco);
  color: var(--seele-osso);
  text-decoration: underline;
  text-decoration-color: var(--seele-laranja-nerv);
}

.realce-atual {
  background: var(--seele-laranja-nerv);
  color: var(--seele-negro-absoluto);
  font-weight: var(--seele-cartela);
  text-decoration-color: var(--seele-negro-absoluto);
}

/* ---------- criar sala ----------

   Debaixo de cada lista, e não num botão de barra: a ação pertence ao lugar que
   ela muda, e quem quer uma sala nova está olhando as que já existem.

   Aparece só para quem pode — `Snapshot.may_manage_voice_rooms`. Esconder não é o que
   protege: o servidor recusa `CreateVoiceRoom` de quem não tem `ManageVoiceRooms`, e há
   conformidade provando. Isto é não oferecer o que não ia funcionar. */
.criar {
  flex: 0 0 auto;
  display: grid;
  grid-template-columns: 1fr auto;
  gap: 8px;
  align-items: end;
  padding: 12px 16px 16px;
  border-bottom: 1px solid var(--seele-linha);
}

.criar-campo {
  display: flex;
  flex-direction: column;
  gap: 4px;
  min-width: 0;
}

/* Os lugares cabem em quatro dígitos; o nome fica com o resto. */
.criar-campo-numero { grid-column: 1; width: 88px; }
.criar-campo-numero + .botao-fantasma { grid-column: 2; }

.criar input {
  width: 100%;
  min-width: 0;
}

/* A nota ocupa a largura inteira, embaixo dos dois campos. */
.criar .nota { grid-column: 1 / -1; }

/* ---------- o que um MOD desenha, e onde — ADR 0049 ----------

   ---- o tema não está aqui, e isto é o registro de onde ele está ----

   Um MOD pede um tema, e o produto redefine quatro tokens — fundo, texto,
   acento e borda — **dentro de `#tela-sessao`**. Isso não é uma regra desta
   folha: `aplicarOTemaDoMod`, em `base.js`, escreve a propriedade no elemento
   pela CSSOM.

   Duas razões, e nenhuma é gosto. A CSP desta janela é `style-src 'self'`, que
   recusa um `<style>` montado em tempo de execução. E o valor vem de código de
   terceiro: como propriedade ele é um valor, e como texto de regra ele seria
   uma porta para escrever CSS arbitrário na página.

   Escopado na sessão, e não na raiz: a tela de entrada, o launcher e a bateria
   ficam com a cor do produto enquanto um MOD estiver de pé, e tirar a
   propriedade faz a sessão voltar sozinha — não há aqui, nem lá, uma cor
   padrão posta por cima da que `tokens.css` define.

   **O contraste medido em `tokens.css` é o do produto.** Um MOD que troque
   estes quatro troca também as razões que aquelas linhas afirmam; por isso
   `aplicarOTemaDoMod` recusa um tema cujo texto não alcance 4,5:1 sobre o
   fundo que ficaria em vigor, e recusa pelo nome.

   (A faixa das regiões, que morava aqui, saiu com a API 3.) */

/* O que um MOD declara numa contribuição — na lista de pessoas, num canal —
   ou numa superfície é desenhado por `mods-controles.css`. Ele mora lá e não
   aqui porque a mesma gramática é montada nos dois lugares, e uma régua repetida em
   duas folhas é a régua que discorda no dia em que só uma delas muda — que foi
   exatamente o que o guarda de classes duplicadas pegou. */

/* ---------- o que aperta primeiro, quando a janela aperta ----------

   As quatro colunas somam 656px de largura fixa antes de a conversa receber um
   pixel. Numa janela de mil e pouco isso deixa a conversa com menos da metade
   da tela, e é aí que a régua vira decisão: **qual coluna cede primeiro**.

   A ordem é esta, e ela não é a ordem de importância — é a ordem do que se
   pode ir buscar noutro lugar:

   1. A faixa de pessoas recolhe primeiro. Ninguém trabalha dentro dela, e o
      que ela mostra não desaparece com ela: a coluna do lado — que só cede no
      ponto seguinte — lista quem está em cada sala, com o estado de cada um em
      palavra, e a tela de chamada tem a sala ocupada em cartões, a um botão do
      cabeçalho. O que se perde entre 1240px e 900px é o sinal por pessoa; a
      média continua no rodapé e a de cada um volta assim que a janela volta.
   2. A coluna de salas e canais vira gaveta depois. Ela é navegação — serve
      para chegar a um canal, não para ficar aberta enquanto se lê o que está
      dentro dele —, e a porta dela é o `CANAIS` que `tela-sessao.js` põe na
      barra do canal aberto. Recolhê-la antes da faixa seria tirar as duas
      listas de gente ao mesmo tempo, que é o que a ordem acima evita.
   3. A conversa não cede nunca antes das outras duas. É o painel onde se
      passam horas e o único desta tela em que a largura muda o que cabe numa
      linha: espremê-la primeiro é encurtar a linha de texto para preservar
      duas colunas que ninguém está lendo.

   Os dois números são onde cada coisa deixa de caber, e não uma classe de
   dispositivo: 1240px é onde a conversa desce de 600px com a faixa de pessoas
   aberta, e 900px é onde ela desce disso outra vez sem ela. Não há um terceiro
   ponto porque não há uma quarta coluna para tirar. */

/* As portas das gavetas não existem fora da largura em que as gavetas existem.
   Este é o estado normal delas, e os `@media` de baixo são a exceção. */
.linha-canais,
.linha-pessoas { display: none; }

@media (max-width: 1240px) {
  /* `relative` porque a gaveta de pessoas se posiciona contra esta caixa. */
  .paineis {
    position: relative;
    grid-template-columns: 60px 268px minmax(0, 1fr) auto;
    grid-template-areas: "trilha canais linha painelmod";
  }

  /* **A faixa de pessoas recolhe, e ganha uma porta.**
   *
   * A ordem de recolher está escrita acima e continua valendo. O que faltava
   * era a porta: fechada sem controle nenhum, a faixa sumia e com ela as duas
   * coisas que só ela mostra — quem está no servidor **fora** das salas, e o
   * sinal por pessoa, que `specs/07` chama de elemento assinatura do produto.
   *
   * A coluna de salas lista quem está em cada sala, e é por isso que a faixa
   * pode recolher primeiro; ela não lista quem não está em sala nenhuma, e não
   * mostra sinal. A validação de janela pequena de 20/09/2026 encontrou as duas
   * ausências sem caminho de volta. */
  .painel-pessoas { display: none; }

  #tela-sessao[data-pessoas="abertas"] .painel-pessoas {
    display: flex;
    position: absolute;
    top: 0;
    bottom: 0;
    right: 0;
    width: min(320px, 100%);
    z-index: 3;
    /* A borda forte é o que separa a gaveta do que ela cobre. Sem sombra:
       nenhuma superfície deste produto tem uma. */
    border-left: 1px solid var(--seele-linha-forte);
  }

  .linha-pessoas {
    display: flex;
    align-items: center;
    min-height: 34px;
    padding: 0 12px;
    flex: 0 0 auto;
    font: inherit;
    font-size: 11px;
    letter-spacing: 0.1em;
    color: var(--seele-osso-apagado);
    background: none;
    border: 1px solid var(--seele-linha-forte);
    cursor: pointer;
  }

  .linha-pessoas:hover,
  .linha-pessoas[aria-expanded="true"] {
    color: var(--seele-laranja-nerv);
    border-color: var(--seele-laranja-nerv);
  }
  /* A conversa passa a ser a última coluna, e a divisória dela seria uma linha
     colada na borda da janela. */
  .painel-linha { border-right: none; }
  /* Oito métricas numa fileira de largura fixa não cabem, e o que não cabe
     some sem dizer que existia. Rolando, ele continua alcançável — e a régua
     de `body { overflow: hidden }` continua valendo para a página. */
  .telemetria { overflow-x: auto; }
}

@media (max-width: 900px) {
  /* `relative` porque a gaveta se posiciona contra esta caixa. */
  .paineis {
    position: relative;
    grid-template-columns: 60px minmax(0, 1fr);
    /* A gaveta de canais sai do fluxo (`position: absolute` logo abaixo), e o
       painel de MOD não cabe como coluna nesta largura: ele se empilha sobre a
       conversa, como a página. */
    grid-template-areas: "trilha linha";
  }

  .paineis > .painel-canais,
  .paineis > .palco-de-paineis { grid-area: linha; }

  /* Numa janela estreita, um painel de MOD ocupa a largura toda em vez de
     espremer a conversa a nada. U04 pede escala legível, e 200px de painel ao
     lado de 300px de conversa não é nenhuma das duas coisas. */
  .palco-de-paineis > .painel-de-mod { flex: 1 1 auto; border-left: none; }

  /* Fechada, ela não existe: `display: none` e não uma coluna empurrada para
     fora da tela. Fora da tela ela continua no caminho do teclado, e tabular
     por trinta botões invisíveis até o campo de mensagem é pior do que não ter
     a coluna. */
  .painel-canais { display: none; }

  #tela-sessao[data-canais="abertos"] .painel-canais {
    display: flex;
    position: absolute;
    top: 0;
    bottom: 0;
    left: 60px;
    width: 268px;
    z-index: 2;
    /* A borda forte é o que separa a gaveta do que ela cobre. Sem sombra:
       nenhuma superfície deste produto tem uma. */
    border-right: 1px solid var(--seele-linha-forte);
  }

  /* A porta da gaveta. Ela existe só aqui — acima deste ponto a coluna está na
     tela, e um botão que abre o que já está aberto é um botão que não faz
     nada. */
  .linha-canais {
    display: flex;
    align-items: center;
    min-height: 34px;
    padding: 0 12px;
    flex: 0 0 auto;
    font: inherit;
    font-size: 11px;
    letter-spacing: 0.1em;
    color: var(--seele-osso-apagado);
    background: none;
    border: 1px solid var(--seele-linha-forte);
    cursor: pointer;
  }

  .linha-canais:hover,
  .linha-canais[aria-expanded="true"] {
    color: var(--seele-laranja-nerv);
    border-color: var(--seele-laranja-nerv);
  }
}


/* A fileira do pé do operador: sair da sala, alternar a vista, compartilhar.
 *
 * `flex-wrap` porque a coluna tem 268px e os três rótulos não cabem numa
 * linha em toda largura de janela — e um rótulo cortado é pior que uma
 * segunda linha. */
.operador-fileira {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.operador-fileira > button { flex: 1 1 auto; }

/* A barra da vista da chamada, com os valores da comp da 0.9.0. */
.vista-chamada-barra {
  display: flex;
  align-items: center;
  gap: 16px;
  min-height: 48px;
  padding: 6px 16px;
  border-bottom: 1px solid var(--seele-linha);
  background: var(--seele-negro-painel);
  flex: 0 0 auto;
}

/* As duas vistas ocupam a coluna inteira, uma de cada vez. `min-height: 0` em
   ambas porque as duas têm filhos que rolam, e sem ele um filho que cresce
   empurra a coluna para fora da janela em vez de rolar dentro dela. */
#vista-chamada,
#vista-conversa {
  display: flex;
  flex-direction: column;
  min-height: 0;
  flex: 1 1 auto;
}

#vista-chamada[hidden],
#vista-conversa[hidden] { display: none; }



/* A linha que substituiu o botão de falar: estado, não instrução. Ela quebra
   em duas linhas nos 268px da coluna e não é para caber numa — é uma frase, e
   não um rótulo. */
.operador-frase {
  margin: 0 0 2px;
  font-size: 11px;
  letter-spacing: 0.04em;
  color: var(--seele-osso-apagado);
}


/* O bloco do operador virou botão na 0.9.0: ele abre o perfil. Sem moldura,
   porque continua sendo o cabeçalho do bloco e não um controle à parte — o que
   diz que responde é o cursor e a inicial acendendo ao passar. */
.operador-quem {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  flex: 1;
  min-width: 0;
  padding: 0;
  font: inherit;
  text-align: left;
  background: none;
  border: 0;
  cursor: pointer;
}

.operador-quem-texto {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}

/* A inicial, que é o que os outros veem de quem não pôs imagem. 32×32, que é o
   `estiloAvatarPequeno` da comp — a mesma medida aqui e no rodapé da entrada,
   porque é a mesma pessoa nos dois lugares. */
.operador-inicial {
  width: 32px;
  height: 32px;
  flex: 0 0 auto;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--seele-linha-forte);
  font-family: var(--seele-display);
  font-weight: 900;
  font-size: 13px;
  color: var(--seele-osso-apagado);
}

.operador-quem:hover .operador-inicial {
  color: var(--seele-laranja-nerv);
  border-color: var(--seele-laranja-nerv);
}

/* O nome de uma pessoa na faixa, quando ele é a porta da moderação.
 *
 * Sem moldura e sem cor própria: ele **é** o nome, e a única coisa que o
 * distingue é o cursor e o sublinhado ao passar. A comp desenha esta linha sem
 * controle nenhum, e a regra que ela segue é a do resto da janela — não
 * acrescentar pixel para um verbo que a pessoa só usa quando precisa. */
.pessoa-nome-porta {
  padding: 0;
  font: inherit;
  color: inherit;
  text-align: left;
  background: none;
  border: 0;
  cursor: pointer;
}

.pessoa-nome-porta:hover { color: var(--seele-laranja-nerv); }

/* Um quadrado de avatar com retrato dentro.
 *
 * `cover` e não `contain`: o quadrado tem 34px e a imagem chega com no máximo
 * 256, em proporção qualquer — deixar barras ao redor de um retrato desse
 * tamanho o transforma num selo. O fundo negro é o que aparece enquanto os
 * bytes não chegaram, e não um vazio claro piscando na coluna. */
[data-com-retrato="sim"] {
  background-color: var(--seele-negro-absoluto);
  background-repeat: no-repeat;
  background-position: center;
  background-size: cover;
}
