:root {
  --bg: #0b0e14; --surface: #131824; --surface-2: #1a2130; --border: #273044;
  --ink: #eaeef6; --muted: #939db3; --accent: #6ea8fe; --accent-2: #9b8cff;
  --ok: #4cc38a; --radius: 14px;
  /* O vermelho de erro do produto (cartão #281 fatia 1i). Havia DOIS para o mesmo significado —
     `#ef6a6a` no login/ecrã do código e `#ff8a8a` no wizard — e nenhum com o contraste medido.
     Fica um só token, aplicado ao texto de erro E à borda de campo inválido nos dois sítios.
     O valor é o mais claro dos dois porque o token serve texto pequeno (12.5–13.5px), onde a
     régua é 4.5:1 (WCAG 1.4.3) e não os 3:1 que bastariam a uma borda:
       #ff8a8a sobre --surface (#131824) = 7.82:1   |  sobre --bg (#0b0e14) = 8.51:1
       #ef6a6a sobre --surface           = 5.86:1   |  sobre --bg           = 6.38:1
     Ambos passam AA, mas unificar no mais escuro tirava contraste ao wizard; unificar no mais
     claro não tira a ninguém e dá o vermelho melhor ao ecrã onde alguém está trancado à porta.
     Medido por `tests/test_error_red_contrast.py`, que é a régua — não este comentário. */
  --danger: #ff8a8a;
}
* { margin: 0; padding: 0; box-sizing: border-box; }
body {
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Inter, sans-serif;
  background: radial-gradient(1200px 600px at 70% -10%, #14203a 0%, var(--bg) 55%);
  color: var(--ink); min-height: 100vh; line-height: 1.6; font-size: 15.5px;
}
a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* Ligação de salto (#647): é o PRIMEIRO elemento focável de cada vista do workspace, escrito
   como primeiro filho do `<body>` — quem navega por teclado salta o cabeçalho e a barra de
   secções do shell (dezenas de tabulações, iguais em todas as vistas) e cai no `<main>`.
   Em repouso está fora do ecrã por `position: absolute` (fora do fluxo: não desloca nada, e
   continua na ordem de tabulação, ao contrário de `display:none`); ao receber o foco volta ao
   canto superior esquerdo. O anel de foco em si é a fatia 3 do #647 — aqui só se trata de a
   ligação passar a ser visível quando é ela que tem o foco.
   O anel é a fatia 3: a borda de acento está SEMPRE lá (também sem foco), logo não distingue
   estado nenhum — o `outline` é a propriedade que só se desenha enquanto o foco é dela. */
.skip-link {
  position: absolute; top: 0; left: -9999px; z-index: 100;
  background: var(--surface-2); color: var(--ink); border: 1px solid var(--accent);
  border-radius: 0 0 var(--radius) 0; padding: 10px 16px; text-decoration: underline;
}
.skip-link:focus, .skip-link:focus-visible {
  left: 0; outline: 2px solid var(--accent); outline-offset: 2px;
}
.wrap { max-width: 1040px; margin: 0 auto; padding: 0 24px; }
nav {
  display: flex; align-items: center; gap: 18px; padding: 18px 0;
}
nav .brand { font-size: 18px; font-weight: 750; letter-spacing: .3px; color: var(--ink); }
nav .brand span { background: linear-gradient(90deg, var(--accent), var(--accent-2)); -webkit-background-clip: text; background-clip: text; color: transparent; }
nav .tag { font-size: 12px; color: var(--muted); border: 1px solid var(--border); border-radius: 999px; padding: 2px 10px; }
nav .spacer { flex: 1; }
.btn {
  display: inline-block; background: var(--accent); color: #0b1020; font-weight: 650;
  border: none; border-radius: 10px; padding: 10px 22px; cursor: pointer; font-size: 15px;
}
.btn:hover { filter: brightness(1.08); text-decoration: none; }
.btn.ghost { background: transparent; color: var(--ink); border: 1px solid var(--border); }
.btn.ghost:hover { border-color: var(--accent); }
.hero { padding: 72px 0 40px; max-width: 760px; }
.hero h1 { font-size: 44px; line-height: 1.15; letter-spacing: -0.02em; font-weight: 800; }
.hero h1 em { font-style: normal; background: linear-gradient(90deg, var(--accent), var(--accent-2)); -webkit-background-clip: text; background-clip: text; color: transparent; }
.hero p.lead { margin-top: 18px; font-size: 18px; color: var(--muted); max-width: 62ch; }
.hero .cta { margin-top: 30px; display: flex; gap: 12px; flex-wrap: wrap; }
.cards { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; padding: 34px 0 60px; }
@media (max-width: 860px) { .cards { grid-template-columns: 1fr; } .hero h1 { font-size: 32px; } }
.card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 22px 24px;
}
.card h3 { font-size: 16px; margin-bottom: 8px; }
.card h3 .ico { margin-right: 8px; }
.card p { color: var(--muted); font-size: 14px; }
.panel {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 34px 38px; max-width: 460px; margin: 9vh auto 0;
}
.panel h2 { font-size: 22px; margin-bottom: 6px; }
.panel .sub { color: var(--muted); font-size: 14px; margin-bottom: 22px; }
/* O prazo do desafio do 2.º passo (cartão #281 fatia 1e). Fica DENTRO do `.sub` — é descrição do
   campo (`aria-describedby`) e sair de lá custava o anúncio à chegada do foco —, mas não pode ser
   mais uma frase inline no fim de cinco linhas em `--muted`: quem varre o ecrã a meio de um login
   não a vê, que é exatamente o problema que o cartão existe para resolver. Linha própria + a cor
   do texto normal chegam para a destacar; nada de peso, caixa ou animação, que a transformariam
   num alerta e roubariam a atenção ao campo onde há para escrever. */
.panel .sub .deadline { display: block; margin-top: 8px; color: var(--ink); }
.panel label { display: block; font-size: 13px; color: var(--muted); margin: 14px 0 6px; }
/* `font-size: 16px` NÃO é gosto tipográfico: é o limiar do zoom automático do iOS (cartão #281
   fatia 1j). O Safari em iPhone amplia a página ao focar um campo cujo tamanho computado seja
   INFERIOR a 16px — e o `font: inherit` sozinho trazia os 15.5px do `body`, meio pixel abaixo da
   linha. O ecrã onde isso dói é o do 2.º passo: a pessoa tem o telemóvel na mão, o enquadramento
   salta a meio de um código que dura 30 segundos, e voltar atrás para o ler outra vez passa a
   exigir uma panorâmica horizontal que não existia antes de tocar no campo.
   O `font: inherit` fica (é ele que traz família e altura de linha do painel); o longhand a seguir
   sobrepõe-se só ao tamanho, como manda a ordem das declarações. Quem mede isto é
   `tests/test_login_mobile_viewport.py`, resolvendo a cascata — não a leitura deste comentário. */
.panel input {
  width: 100%; background: var(--bg); color: var(--ink); border: 1px solid var(--border);
  border-radius: 10px; padding: 11px 14px; font: inherit; font-size: 16px;
}
/* O foco NÃO pode viver na borda: a regra de inválido logo abaixo tem a mesma especificidade e
   vem depois, logo ganharia o `border-color` e o campo focado ficava sem indicador nenhum
   (WCAG 2.4.7). O `outline` é uma propriedade à parte — soma-se ao vermelho do erro em vez de
   competir com ele. Mesma convenção do `.field` do wizard (ver linha do `.field input:focus`). */
.panel input:focus, .panel input:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
}
/* Campo em erro: o sinal visível acompanha o `aria-invalid` (mesma regra que `.field` já usa no
   wizard), senão a invalidez só existe para quem ouve o leitor de ecrã. Só mexe na borda — o
   indicador de foco acima sobrevive. */
.panel input[aria-invalid="true"] { border-color: var(--danger); }
/* Alvo de toque de 44×44 CSS px (WCAG 2.5.5 / 2.5.8, cartão #281 fatia 1j). Com `padding: 10px` e
   15px de texto, o `.btn` media ~38px de altura — abaixo da régua no único ecrã do produto que se
   usa de propósito com o telemóvel na mão. O `inline-flex` não é decoração: `min-height` num
   `inline-block` faz a caixa crescer para BAIXO e o rótulo ficaria descentrado (um `<button>`
   centra o conteúdo por omissão, mas um `<a class="btn">` — que é o que a landing usa — não). */
.panel .btn {
  width: 100%; margin-top: 22px; min-height: 44px;
  display: inline-flex; align-items: center; justify-content: center;
}
.panel .err { color: var(--danger); font-size: 13.5px; margin-top: 12px; min-height: 1em; }
.panel .foot { margin-top: 18px; font-size: 12.5px; color: var(--muted); text-align: center; }
/* Ação com aspeto de ligação: para os controlos que EXECUTAM algo em vez de navegar (o «Voltar»
   do 2.º passo do login, #281) e que por isso têm de ser `<button>` — anunciado como «botão»,
   ativável por `Espaço`, sem `href="#"` a aterrar no URL. O aspeto é o do `a` de um `.foot`:
   `font: inherit` porque um `<button>` NÃO herda a tipografia do pai (sem isto vinha com a fonte
   e o tamanho do sistema, destoando do rodapé em 12.5px), e o resto tira-lhe a pele de widget
   sem lhe tirar a semântica. `vertical-align: baseline` mantém-no na linha do texto que o rodeia,
   como estava a ligação. O foco visível segue a convenção do `.iface-chip` (WCAG 2.4.7): outline
   à parte, que não depende de cor de borda nenhuma.

   O sublinhado é EM REPOUSO, ao contrário do `a` global lá em cima: aqui o controlo está embebido
   numa frase corrida do `.foot`, e a única diferença entre o `--accent` dele e o `--muted` do texto
   à volta é 1.13:1 de contraste — longe do 3:1 que a técnica G183 (WCAG 1.4.1, Nível A) exige
   quando a cor é o único sinal. Sem sublinhado, quem tem deuteranopia/protanopia ou um ecrã fraco
   lê «Voltar e entrar com outra conta.» como frase, e este é o único caminho de saída do 2.º passo.
   Por isso o `:hover` NÃO tira o sublinhado — reforça-o (mais espesso + o `brightness` que o `.btn`
   já usa), senão o estado de repouso voltava a ser o problema. O `padding` com `margin` negativo
   simétrico aumenta o alvo de toque sem mexer na linha: a caixa de margem fica com a altura que
   tinha (~20px), pelo que a frase não se desloca — só a área sensível ao dedo cresce por fora.
   O par 12/14 dá ~68×44 (era 4/6, ~50×28) e põe o único caminho de saída do 2.º passo acima dos
   44×44 do WCAG 2.5.5/2.5.8, como o «Entrar» ao lado (#281 fatia 1j). Os 12px que sobram para cima
   cabem nos 18px de `margin-top` do `.foot`: não há aqui outro alvo com que sobrepor-se. */
.linkbtn {
  background: none; border: none; padding: 12px 14px; margin: -12px -14px; font: inherit;
  color: var(--accent); text-decoration: underline; cursor: pointer; vertical-align: baseline;
}
.linkbtn:hover { text-decoration: underline; text-decoration-thickness: 2px; filter: brightness(1.08); }
.linkbtn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
/* ECRÃ CURTO (cartão #281 fatia 1j) — o mesmo padrão responsivo que o resto do ficheiro já usa
   (`@media (max-width: …)` nas linhas do `.cards`, `.choice` e `.grid-2`), mas na outra dimensão:
   aqui o que falta é ALTURA. Dispara no telemóvel deitado (844×390, 812×375), numa janela pequena
   e no Android com o teclado aberto (que encolhe mesmo a viewport de layout). Os 620px são o corte
   entre isso e um telemóvel ao alto (780px+), que fica com o painel como estava.

   O que se corrige é a decoração a ocupar o lugar da conversa: `9vh` de margem + 34px de `padding`
   punham ~72px de nada por cima do título quando o ecrã inteiro tem 420px. Pior, o `vh` no iOS
   mede a viewport GRANDE — não encolhe com o teclado —, pelo que a margem que sobra é sempre a de
   um ecrã que já não se vê. Aqui é px fixo, medido contra o que a pessoa tem à frente.

   O `margin-bottom` é novo (o `.panel` tinha `9vh auto 0`): sem ele o último controlo do painel —
   o «Voltar», a única saída do 2.º passo — acaba encostado ao limite inferior, por baixo da barra
   do indicador de início do iOS.

   NÃO se mexe em tamanhos de texto, nem na `line-height`, nem se esconde nada: comprimir a prosa
   para caber é redesenhar o painel, que este cartão põe fora de âmbito. O que se garante — e está
   medido em `tests/test_login_mobile_viewport.py` — é que campo, mensagem de erro e «Entrar»
   cabem TODOS na mesma viewport curta, ou seja que quem chega ao campo já tem o erro e o botão à
   vista sem voltar a rolar. */
@media (max-height: 620px) {
  .panel { margin: 12px auto 24px; padding: 20px 22px; }
  .panel h2 { font-size: 20px; }
  .panel .sub { margin-bottom: 14px; }
  .panel label { margin: 10px 0 4px; }
  .panel .btn { margin-top: 14px; }
  /* 16px e não 12: o alvo de toque do «Voltar» cresce 12px para CIMA por margem negativa (ver
     `.linkbtn`), e com 12px de folga ficava encostado ao «Entrar». Aqui sobram 4px entre os dois
     alvos — medido em `test_o_alvo_do_voltar_nao_invade_o_botao_de_entrar`. */
  .panel .foot { margin-top: 16px; }
}
.choice { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-top: 30px; }
@media (max-width: 700px) { .choice { grid-template-columns: 1fr; } }
.choice a.option {
  display: block; background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 30px 28px; color: var(--ink);
}
.choice a.option:hover { border-color: var(--accent); text-decoration: none; transform: translateY(-2px); transition: all .15s; }
.choice .ico { font-size: 30px; }
.choice h3 { margin-top: 12px; font-size: 19px; }
.choice p { color: var(--muted); font-size: 14px; margin-top: 6px; }
.crumbs { color: var(--muted); font-size: 13.5px; margin: 26px 0 8px; }
.section-title { font-size: 26px; font-weight: 750; margin: 6px 0 6px; }
.badge { display: inline-block; font-size: 12px; color: var(--accent-2); border: 1px solid var(--accent-2); border-radius: 999px; padding: 2px 10px; margin-left: 10px; vertical-align: middle; }
.kv { display: grid; grid-template-columns: 160px 1fr; gap: 8px 18px; margin-top: 18px; font-size: 14.5px; }
.kv dt { color: var(--muted); }
.note {
  background: var(--surface-2); border: 1px dashed var(--border); border-radius: 12px;
  padding: 16px 20px; color: var(--muted); font-size: 14px; margin-top: 26px;
}
footer { padding: 40px 0; color: var(--muted); font-size: 12.5px; text-align: center; }

/* ---- Wizard de setup do clone (super admin) ------------------------------- */
.setup-topbar { display: flex; align-items: flex-end; gap: 24px; flex-wrap: wrap; margin-top: 22px; }
.setup-topbar .field.inline { flex: 0 0 auto; }
.hint { color: var(--muted); font-size: 12.5px; margin-top: 6px; display: block; }
.chips { display: flex; gap: 10px; flex-wrap: wrap; margin-left: auto; }
.chip {
  font-size: 13px; border-radius: 999px; padding: 6px 14px; border: 1px solid var(--border);
  background: var(--surface); white-space: nowrap;
}
.chip.pending { color: var(--muted); }
.chip.ok { color: #0b1020; background: var(--ok); border-color: var(--ok); font-weight: 650; }

.steps { list-style: none; display: flex; gap: 10px; margin: 22px 0 10px; flex-wrap: wrap; }
.step-chip {
  display: flex; align-items: center; gap: 8px; font-size: 14px; color: var(--muted);
  border: 1px solid var(--border); border-radius: 999px; padding: 6px 16px; background: var(--surface);
}
.step-chip .n {
  display: inline-flex; align-items: center; justify-content: center; width: 22px; height: 22px;
  border-radius: 50%; background: var(--surface-2); font-size: 12.5px; font-weight: 700;
}
.step-chip.active { color: var(--ink); border-color: var(--accent); }
.step-chip.active .n { background: var(--accent); color: #0b1020; }

.wizard-panel {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 26px 28px; margin-top: 16px;
}
.wizard-panel h2 { font-size: 20px; margin-bottom: 4px; outline: none; }
.field { margin-top: 18px; }
.field label { display: block; font-size: 13.5px; color: var(--ink); margin-bottom: 6px; font-weight: 600; }
.field .req { color: var(--accent); }
.field input, .field textarea {
  width: 100%; background: var(--bg); color: var(--ink); border: 1px solid var(--border);
  border-radius: 10px; padding: 11px 14px; font: inherit; resize: vertical;
}
.field input:focus, .field textarea:focus, .field input:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 1px; border-color: var(--accent);
}
.field input[aria-invalid="true"], .field textarea[aria-invalid="true"] { border-color: var(--danger); }
.field .err { color: var(--danger); font-size: 12.5px; margin-top: 6px; display: block; min-height: 1em; }
.grid-2 { display: grid; grid-template-columns: 1fr 1fr; gap: 0 18px; }
@media (max-width: 640px) { .grid-2 { grid-template-columns: 1fr; } .chips { margin-left: 0; } }

.wizard-actions { display: flex; align-items: center; gap: 14px; margin-top: 26px; }
.wizard-actions .btn:last-child { margin-left: auto; }
.wizard-actions .save-state { color: var(--muted); font-size: 13px; }
.btn:disabled { opacity: .6; cursor: default; }
/* Botão inerte por `aria-disabled` (mantém-se focável e anunciado, ao contrário de `disabled` —
   ver kb_review_queue.js). O estado VISÍVEL tem de acompanhar o semântico, senão fica um botão
   com ar de clicável que não responde. */
.btn[aria-disabled="true"] { opacity: .6; cursor: default; }
.btn[aria-disabled="true"]:hover { filter: none; }

.banner { border-radius: 12px; padding: 13px 18px; font-size: 14px; margin-top: 18px; }
.banner.error { background: rgba(239,106,106,.12); border: 1px solid #ef6a6a; color: #ffb3b3; }
.banner.success { background: rgba(76,195,138,.12); border: 1px solid var(--ok); color: #9be3c0; }
/* Aviso (kind "warn"): usado por várias páginas (chat/documents/jira/meetings) para erros
   transitórios e validações não-bloqueantes. Mesma paleta de aviso de .summary-clone.warn. */
.banner.warn { background: rgba(239,176,76,.12); border: 1px solid #e8b04c; color: #f2d69a; }
/* Neutro (kind "info"): a variante de quem tem uma informação a dar e nenhum alarme a tocar.
   Existe porque `.banner` SEM variante não tem fundo nem moldura — texto solto a fingir de
   painel. Quem não tem variante para dar usa esta, não a ausência dela. */
.banner.info { background: rgba(110,168,254,.10); border: 1px solid var(--border); color: var(--ink); }
.banner a { color: inherit; text-decoration: underline; }

.summary-clone { border-radius: 12px; padding: 14px 18px; font-size: 15px; margin-bottom: 20px; }
.summary-clone.ok { background: rgba(76,195,138,.14); border: 1px solid var(--ok); color: #b6ecd1; }
.summary-clone.pending { background: var(--surface-2); border: 1px dashed var(--border); color: var(--muted); }
.summary-clone.warn { background: rgba(239,176,76,.12); border: 1px solid #e8b04c; color: #f2d69a; }
/* Fornecimento de AI · BYOK (YAI-042): a key aparece SEMPRE mascarada, nunca em claro. */
.ai-card { margin-top: 18px; }
code.mask { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 13px;
  padding: 1px 6px; border-radius: 6px; background: var(--surface-2); border: 1px solid var(--border); }
.summary-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; }
@media (max-width: 700px) { .summary-grid { grid-template-columns: 1fr; } }
.summary-card { background: var(--bg); border: 1px solid var(--border); border-radius: 12px; padding: 18px 20px; }
.summary-card h3 { font-size: 15px; margin-bottom: 6px; }
.summary-card .kv { grid-template-columns: 96px 1fr; gap: 6px 14px; margin-top: 10px; font-size: 13.5px; }
.summary-card .kv dd { color: var(--ink); word-break: break-word; }

/* Interface de trabalho dinâmica (sugerida do perfil, evoluível) */
.interface-card { margin-top: 18px; }
.iface-status { font-size: 12.5px; margin: 8px 0 12px; padding: 6px 12px; border-radius: 999px; display: inline-block; }
.iface-status.suggested { color: var(--accent-2); border: 1px solid var(--accent-2); background: rgba(155,140,255,.10); }
.iface-status.saved { color: var(--ok); border: 1px solid var(--ok); background: rgba(76,195,138,.12); }
.iface-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 10px; }
.iface-chip {
  text-align: left; cursor: pointer; background: var(--surface); color: var(--ink);
  border: 1px solid var(--border); border-radius: 12px; padding: 12px 14px;
  display: grid; grid-template-columns: 24px 1fr; grid-template-rows: auto auto; gap: 2px 10px;
  transition: border-color .15s, background .15s, transform .1s;
}
.iface-chip:hover { border-color: var(--accent); transform: translateY(-1px); }
.iface-chip:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
.iface-chip .ic { grid-row: 1 / span 2; font-size: 20px; align-self: start; }
.iface-chip .lb { font-size: 14px; font-weight: 600; }
.iface-chip .why { font-size: 12px; color: var(--muted); }
.iface-chip .why.muted { opacity: .8; }
.iface-chip.on { border-color: var(--ok); background: rgba(76,195,138,.10); }
.iface-chip.on .lb::after { content: " ✓"; color: var(--ok); }

/* ---- Conteúdo largo em ecrã estreito (cartão #648, fatia 4) -----------------
   UMA regra, um invólucro: `.wide-scroll`. Toda a tabela do workspace (as duas escritas à mão —
   `.users-table` das Definições e `.jira-issues` — e as que o `render.js` desenha a partir de
   Markdown/DBML) vive dentro de um `<div class="wide-scroll">`, e é ESSE div que desliza. Antes
   desta fatia havia uma regra por vista com valores diferentes (`.users-table-wrap` tinha
   `overflow-x: auto` a todas as larguras; a tabela do Jira não tinha invólucro nenhum e empurrava
   a página inteira para fora do ecrã).

   Porquê dentro de uma `@media` e com esta fronteira: `.wrap` tem `max-width: 1040px`, logo é aos
   1040px que a coluna de conteúdo deixa de poder crescer e passa a encolher com a janela — abaixo
   daí é que uma tabela larga tem de ceder. Acima, nada muda: em ecrã largo `.wide-scroll` é um
   `<div>` sem uma única declaração, e as tabelas ficam exatamente como estavam (não há caixa de
   deslize, não há contexto de scroll novo, não há `position: sticky` a mudar de referência).

   As três declarações, e nenhuma a mais:
     • `overflow-x: auto` — o deslize é DAQUI. Como efeito de spec, um contentor de deslize tem
       tamanho mínimo automático 0, e é isso que impede a largura da tabela de subir pela árvore
       de flex/grid acima e dar deslize horizontal ao corpo da página;
     • `max-width: 100%` + `min-width: 0` — a caixa nunca é mais larga do que a coluna que a
       recebe, esteja ela num `1fr` de grelha ou num item de flex (onde o mínimo automático é o
       conteúdo, e não zero);
     • `overflow-wrap: break-word` nas células — o texto QUEBRA em vez de ser cortado. As células
       que declaram `white-space: nowrap` (a chave e o responsável do Jira, a coluna de ações das
       Definições) não quebram de propósito: essas fazem o invólucro deslizar. Cortar — `overflow:
       hidden` + `text-overflow: ellipsis` — é que não é opção em lado nenhum: sem saída, o texto
       desaparecia e não havia gesto que o trouxesse de volta.

   A régua é `tests/test_workspace_conteudo_largo.py`, não este comentário. */
@media (max-width: 1040px) {
  .wide-scroll { max-width: 100%; min-width: 0; overflow-x: auto; }
  .wide-scroll th, .wide-scroll td { overflow-wrap: break-word; }
}
/* O invólucro é um ALVO DE TABULAÇÃO, e é por isso que esta regra está fora da `@media`.
   Motivo: uma caixa que desliza e não recebe foco é conteúdo escondido a quem só usa teclado —
   a coluna que sai do ecrã (o «Responsável» do Jira, que não quebra) não volta com gesto nenhum,
   porque as setas rolam o documento e não uma caixa que não está focada. É a regra
   `scrollable-region-focusable` do axe (WCAG 2.1.1), e o critério do cartão — «não é cortado nem
   truncado SEM SAÍDA» — não se cumpre sem ela. O markup dá-lhe `tabindex="0"`, `role="region"` e
   um `aria-label` que diz QUAL tabela é; sem o nome, o papel anunciava uma região anónima.

   O `tabindex` do markup vale a TODAS as larguras (torná-lo focável só quando transborda pedia
   JavaScript, que esta fatia não tem), logo o indicador também tem de valer a todas: um alvo de
   tabulação sem contorno visível é o defeito seguinte. Isto não contradiz o «em ecrã largo nada
   muda»: um `outline` não ocupa espaço no modelo de caixa e só se desenha enquanto o foco é
   visível — a caixa, essa, continua exatamente como estava. O token é o da casa (`.linkbtn`,
   `.iface-chip`).

   O deslocamento é NEGATIVO, e é a única diferença para o `.linkbtn`. Um `outline-offset` positivo
   desenha o anel FORA da caixa de bordo, e um invólucro pode ter um antepassado que recorta: o
   cartão DBML que o `render.js` desenha (`.dbml-table`, `border-radius` + `overflow: hidden`, sem
   `padding`) apagava-lhe três lados, e o quarto — a faixa de cima — caía sobre a barra de título
   `.dbml-tname`, que tem `background: var(--accent)`, ou seja a MESMA cor do contorno. Resultado:
   uma paragem de tabulação sem indicador nenhum. Com `-2px` o anel é desenhado DENTRO da caixa de
   bordo do próprio invólucro: nenhum antepassado o pode recortar, não se sobrepõe a irmão nenhum,
   e continua a não ocupar espaço. Nas outras quatro tabelas fica igualmente legível. */
.wide-scroll:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }

/* ---- Ecrã estreito: nenhum campo abaixo dos 16px (cartão #648, fatia 3) -----
   O Safari em iOS AMPLIA a página ao focar um campo cujo tamanho computado seja inferior a 16px —
   e depois não a devolve: quem escreve numa vista do workspace com o telemóvel na mão fica com o
   enquadramento deslocado e com metade do ecrã fora da vista. O #281 já o tinha pago no login
   (`.panel input`, com um longhand a seguir ao `font: inherit`), mas só ali: no workspace há
   dezenas de campos, e quase todos declaram `font: inherit` — que não traz 16px nenhum, traz o
   que o corpo da página tiver. O corpo tinha 15.5px, meio pixel abaixo da linha, e por isso TODOS
   ampliavam.

   A correção é no PONTO DE HERANÇA e não campo a campo, e é isso que a torna uma linha só: um
   `input { font-size: 16px }` na folha partilhada perdia para qualquer `.field input` de uma vista
   (especificidade maior, e a folha inline de cada vista vem depois desta no `<head>`), ao passo
   que uma regra `font: inherit` não SOBREPÕE o valor herdado — propaga-o. Levantar o corpo levanta
   com ele todos os campos que herdam, sem uma única disputa de cascata. Fica dentro da `@media`
   porque é só no telemóvel/tablet que o zoom automático existe: em ecrã largo a folha é a mesma
   de sempre, 15.5px incluídos.

   Os campos que declaram um tamanho PRÓPRIO abaixo de 16px não herdam nada e não são apanhados
   por aqui — esses estão corrigidos na folha da vista que os declara (chat, aprendizagens,
   definições) e o varrimento que o prova é `tests/test_shell_ecra_estreito.py`, não este
   comentário. A fronteira é a do menu colapsável do shell (`LARGURA_DE_COLAPSO = 640`): é a
   mesma largura a partir da qual a barra deixa de caber numa linha, e ter duas seria ter dois
   ecrãs estreitos diferentes. */
@media (max-width: 640px) {
  body { font-size: 16px; }
  /* O chão de quem não herda nada: um campo sem regra de tipografia nenhuma não usa o tamanho do
     corpo — usa o da folha do agente-utilizador (≈13.3px), que está abaixo da linha. Esta regra
     perde para qualquer seletor mais específico, e é isso que se quer: ela só existe para os
     campos que ninguém vestiu. */
  input, textarea, select { font-size: 16px; }
}

/* `hidden` fiável em toda a app servida por esta folha (cartão #1060, 3.ª ronda, devolvido pela
   revisão de Design). O `[hidden] { display: none }` vem da folha do agente-utilizador e perde
   para QUALQUER regra de autor que declare `display` — `.btn { display: inline-block }`,
   `.wizard-actions { display: flex }`, `.user-form { display: grid }`, `.hint { display: block }`.
   A armadilha já tinha sido caçada à mão três vezes (`#comTentar[hidden]` e `#promoverBar` em
   `notas.html`, `mostrarBotao` em `audio_recorder.js`) e voltou a abrir no wizard do setup:
   `renderAiStatus` punha `hidden` no `#ai-key-actions` (`.wizard-actions`) e o botão «Validar e
   guardar» ficava no ecrã, órfão, sem campo e sem feedback ao clique. Uma regra só, com
   `!important`, fecha a porta de vez: quem escreve `el.hidden = true` esconde mesmo, seja qual
   for a classe. A ordem nesta folha é irrelevante (o `!important` decide), por isso fica no fim
   e não desloca nenhuma linha citada. Guarda: `tests/test_byok_estado_do_fornecedor.py`. */
[hidden] { display: none !important; }
