/* ============================================================
   ELVEN OBSERVABILITY — CHANGELOG · CONTROLES DE FORMULÁRIO
   Acabamento dos elementos nativos (select, input, textarea,
   checkbox, radio, botão) para eles pertencerem à mesma UI que
   o app.css já define. Não redesenha nada: reaproveita os
   mesmos tokens, raios, pesos e transições.

   ORDEM DE CARGA: depois de app.css.
     <link rel="stylesheet" href="/assets/app.css">
     <link rel="stylesheet" href="/assets/controls.css">

   ESTRATÉGIA DE CASCATA (por que os seletores são o que são):
   - O grosso da base vai dentro de :where(), que tem
     especificidade ZERO. Assim tudo que o app.css já opinou
     (.fld input, .install select, .ainput, .dta, .radios input…)
     continua vencendo e nenhuma métrica aprovada muda.
   - Só sobe de especificidade o que PRECISA vencer o app.css
     para o controle não ficar quebrado (ex.: o padding à direita
     do select, senão o texto passa por baixo da seta).
   - Zero !important. Zero JS. Nenhum token redefinido — as
     variáveis criadas aqui usam o prefixo --ctl- e são locais
     de componente, derivadas dos tokens existentes.
   ============================================================ */

/* ------------------------------------------------------------
   1. VARIÁVEIS LOCAIS DE COMPONENTE (não são tokens de marca)
   Só duas precisam de variante por tema: a seta do select
   (data-URI não enxerga currentColor) e a cor sobre o accent.
   As demais saem de color-mix() sobre tokens e já acompanham
   o tema sozinhas.
   ------------------------------------------------------------ */
:root {
  --ctl-h: 2.25rem;                 /* altura mínima de controle em .campo */
  --ctl-pad-y: .5rem;
  --ctl-pad-x: .625rem;
  --ctl-fs: .9375rem;
  --ctl-mark: 1.125rem;             /* lado do checkbox/radio desenhado */

  /* hairlines: discretos, mas presentes. O border puro some em
     superfície elevada; o mix dá o mínimo de definição. */
  --ctl-border-hover: color-mix(in srgb, var(--text-3) 45%, var(--border));
  --ctl-border-forte: color-mix(in srgb, var(--text-3) 55%, var(--border));

  --ctl-on-accent: #FFFFFF;         /* texto/marca sobre --accent */
  --ctl-chevron: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%2364748B' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.75 6 6.25 11 1.75'/%3E%3C/svg%3E");
}

/* No tema escuro o accent (#00BFA5) é claro demais para texto
   branco (≈1.9:1). Marca e rótulo sobre o accent viram tinta. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) {
    --ctl-on-accent: #0F1923;
    --ctl-chevron: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%238B98A5' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.75 6 6.25 11 1.75'/%3E%3C/svg%3E");
  }
}
:root[data-theme="dark"] {
  --ctl-on-accent: #0F1923;
  --ctl-chevron: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none' stroke='%238B98A5' stroke-width='1.6' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M1 1.75 6 6.25 11 1.75'/%3E%3C/svg%3E");
}

/* ------------------------------------------------------------
   2. BASE COMPARTILHADA
   range/color/file ficam nativos de propósito: o app não usa
   nenhum dos três, e meio-estilizado é pior que nativo.
   Nunca uso o atalho `background` aqui — ele zeraria o
   background-image da seta do select. Só longhands.
   ------------------------------------------------------------ */
:where(
  input:not([type="checkbox"]):not([type="radio"]):not([type="range"]):not([type="color"]):not([type="file"]):not([type="hidden"]),
  select,
  textarea
) {
  background-color: var(--bg-raised);
  border: 1px solid var(--border);
  border-radius: var(--r-md);
  padding: var(--ctl-pad-y) var(--ctl-pad-x);
  font-family: var(--font-sans);
  font-size: var(--ctl-fs);
  line-height: 1.25rem;
  caret-color: var(--accent);
  accent-color: var(--accent);      /* pinta o que sobrou de nativo */
  transition: border-color var(--t-fast), background-color var(--t-fast), color var(--t-fast);
}

/* `color` fica FORA do :where() acima de propósito: o app.css tem
   `input, select, textarea { color: inherit }` (0,0,1), que
   venceria a especificidade zero e faria o valor do campo herdar
   a cor do container (text-2 dentro de .esummary, por exemplo).
   Aqui a cor do valor é sempre a cor de texto primária. */
:is(input, select, textarea):not([type="checkbox"]):not([type="radio"]) { color: var(--text-1); }

/* Firefox baixa a opacidade do placeholder por conta própria. */
:where(input, textarea)::placeholder { color: var(--text-3); opacity: 1; }

/* color-scheme é o único jeito de fazer o navegador desenhar o
   POPUP do select, o painel do date picker, os spinners e as
   barras de rolagem internas no tema certo. Aplico no controle
   (e não no :root) para não mexer no resto da página. */
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) :where(input, select, textarea, .btn) { color-scheme: dark; }
}
:root[data-theme="dark"] :where(input, select, textarea, .btn) { color-scheme: dark; }
:root[data-theme="light"] :where(input, select, textarea, .btn) { color-scheme: light; }

/* Hover: sobe a especificidade porque .fld/.install/.ainput já
   declaram border-color e venceriam um :where(). */
:is(
  input:not([type="checkbox"]):not([type="radio"]),
  select,
  textarea
):hover:not(:disabled):not([readonly]) {
  border-color: var(--ctl-border-hover);
}

/* Disabled. -webkit-text-fill-color existe porque o Safari
   lava o texto de controles desabilitados ignorando `color`. */
:where(input, select, textarea):disabled {
  opacity: .55;
  cursor: not-allowed;
  -webkit-text-fill-color: currentColor;
  background-color: var(--surface);
  border-color: var(--border-soft);
}
:where(input, select, textarea)[readonly] {
  background-color: var(--surface);
  color: var(--text-2);
}

/* Inválido por validação nativa. :user-invalid só marca DEPOIS
   da interação — :invalid pintaria o formulário de vermelho
   antes de o usuário digitar a primeira letra. */
:where(input, select, textarea):user-invalid { border-color: var(--danger); }

/* ------------------------------------------------------------
   3. SELECT
   appearance:none porque a seta nativa (e, no macOS, o relevo
   e o cantinho azul do sistema) é o que mais destoa da UI.
   Trocada por um chevron fino em SVG data-URI.

   Por que data-URI e não mask+currentColor: <select> é elemento
   substituído — não aceita ::before/::after de forma confiável
   em nenhum motor, então não há onde pendurar a máscara. Sobra o
   background-image, e data-URI não enxerga currentColor. Daí as
   duas variantes trocadas por --ctl-chevron (§1). Já onde EXISTE
   pseudo-elemento (search cancel, date picker) uso mask +
   currentColor, que resolve os dois temas com um recurso só.

   [multiple] e [size] ficam de fora: são listbox, não dropdown,
   e não têm seta para substituir.
   ------------------------------------------------------------ */
select:not([multiple]):not([size]) {
  -webkit-appearance: none;
  appearance: none;
  background-image: var(--ctl-chevron);
  background-repeat: no-repeat;
  background-position: right .6875rem center;
  background-size: 12px 8px;
  /* 2.125rem = .6875 (respiro) + 12px (seta) + .625 (padding).
     Precisa vencer .fld/.install/.dsel, senão a opção longa
     passa por baixo da seta. */
  padding-inline-end: 2.125rem;
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;          /* Blink/WebKit respeitam no select fechado; Gecko ignora e apenas corta */
  cursor: pointer;
}
select:not([multiple]):not([size]):disabled { cursor: not-allowed; }

/* O QUE O NAVEGADOR NÃO DEIXA ESTILIZAR (documentado de propósito):
   - macOS Safari e Chrome desenham o popup do select pelo sistema:
     background/color de <option> são ignorados. No Chromium de
     Windows/Linux e no Firefox eles valem — por isso a regra abaixo.
   - Em nenhum motor dá para estilizar padding, altura de linha,
     hover, o check da opção selecionada, o raio ou a animação do
     popup, nem colocar HTML dentro de <option>.
   - `appearance: base-select` (Chromium recente) abriria o popup
     inteiro para CSS, mas muda o rendering do controle e ainda não
     tem suporte cruzado: fica registrado como caminho futuro.
   - O que de fato uniformiza o popup nos dois temas é o
     color-scheme aplicado em §2. */
select option,
select optgroup {
  background-color: var(--bg-raised);
  color: var(--text-1);
  font-family: var(--font-sans);
}
select optgroup {
  font-style: normal;
  font-weight: 590;
  color: var(--text-3);
}
select option:disabled { color: var(--text-3); }

/* ------------------------------------------------------------
   4. INPUTS — search / date / number / url
   ------------------------------------------------------------ */
input[type="search"] {
  -webkit-appearance: none;         /* Safari ainda entrega cápsula com relevo */
  appearance: none;
}

/* O X nativo do search é cinza-sistema e não segue o tema.
   Aqui ele é zerado e redesenhado com mask + currentColor:
   um recurso só, correto no claro e no escuro.
   Gecko não expõe esse pseudo-elemento e simplesmente não
   mostra botão de limpar — comportamento aceito, sem JS. */
input[type="search"]::-webkit-search-cancel-button {
  -webkit-appearance: none;
  appearance: none;
  inline-size: .875rem;
  block-size: .875rem;
  margin-inline-start: .375rem;
  cursor: pointer;
  opacity: .4;
  background-color: currentColor;
  -webkit-mask-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M3.6 3.6l6.8 6.8M10.4 3.6l-6.8 6.8' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 14 14'%3E%3Cpath d='M3.6 3.6l6.8 6.8M10.4 3.6l-6.8 6.8' fill='none' stroke='%23000' stroke-width='1.7' stroke-linecap='round'/%3E%3C/svg%3E");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  transition: opacity var(--t-fast);
}
input[type="search"]::-webkit-search-cancel-button:hover { opacity: .85; }
input[type="search"]::-webkit-search-decoration,
input[type="search"]::-webkit-search-results-button { -webkit-appearance: none; appearance: none; }

/* Date: o ícone nativo é um calendário cinza fixo. Mesma técnica
   de máscara — acompanha o texto, logo acompanha o tema. O painel
   do calendário em si é UI do navegador: só o color-scheme (§2)
   consegue escurecê-lo. */
input[type="date"],
input[type="datetime-local"],
input[type="month"],
input[type="time"] {
  font-variant-numeric: tabular-nums;
  min-block-size: var(--ctl-h);
}
input[type="date"]::-webkit-calendar-picker-indicator,
input[type="datetime-local"]::-webkit-calendar-picker-indicator,
input[type="month"]::-webkit-calendar-picker-indicator,
input[type="time"]::-webkit-calendar-picker-indicator {
  -webkit-appearance: none;
  appearance: none;
  inline-size: 1rem;
  block-size: 1rem;
  padding: 0;
  margin-inline-start: .25rem;
  cursor: pointer;
  opacity: .45;
  background-color: currentColor;
  -webkit-mask-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.25' y='3.25' width='11.5' height='10.5' rx='1.75'/%3E%3Cpath d='M2.25 6.5h11.5M5.5 1.9v2.2M10.5 1.9v2.2'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%23000' stroke-width='1.4' stroke-linecap='round' stroke-linejoin='round'%3E%3Crect x='2.25' y='3.25' width='11.5' height='10.5' rx='1.75'/%3E%3Cpath d='M2.25 6.5h11.5M5.5 1.9v2.2M10.5 1.9v2.2'/%3E%3C/svg%3E");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  transition: opacity var(--t-fast);
}
input[type="date"]:hover::-webkit-calendar-picker-indicator,
input[type="date"]:focus-visible::-webkit-calendar-picker-indicator { opacity: .9; }
/* Campos internos do date (dd/mm/aaaa) herdam a cor do input;
   o realce de segmento é do navegador e usa a Highlight do SO. */
input[type="date"]::-webkit-datetime-edit-fields-wrapper { padding: 0; }
input[type="date"]::-webkit-datetime-edit-text { color: var(--text-3); padding: 0 .0625rem; }

/* Number: setinhas escondidas em repouso (ruído visual constante
   num formulário de versões), reveladas no hover/foco. Gecko
   mantém as próprias — aceito, some espremer o campo. */
input[type="number"] { font-variant-numeric: tabular-nums; }
input[type="number"]::-webkit-inner-spin-button,
input[type="number"]::-webkit-outer-spin-button {
  opacity: 0;
  transition: opacity var(--t-fast);
}
input[type="number"]:hover::-webkit-inner-spin-button,
input[type="number"]:focus::-webkit-inner-spin-button { opacity: .55; }

input[type="url"],
input[type="email"] { text-overflow: ellipsis; }
:where(input.mono, input.mono-sm, input.mono-xs) { font-family: var(--font-mono); }

/* ------------------------------------------------------------
   5. TEXTAREA
   min-block-size só quando o autor NÃO declarou rows — os
   textarea do admin usam rows="2"/"4" de propósito e não podem
   ser inflados por uma altura mínima genérica.
   ------------------------------------------------------------ */
:where(textarea) {
  resize: vertical;
  scrollbar-width: thin;
  scrollbar-color: var(--ctl-border-forte) transparent;
}
textarea:not([rows]) { min-block-size: 5.5rem; }
textarea::-webkit-scrollbar { inline-size: 10px; block-size: 10px; }
textarea::-webkit-scrollbar-track { background: transparent; }
textarea::-webkit-scrollbar-thumb {
  background-color: var(--ctl-border-forte);
  border: 3px solid transparent;    /* afina o polegar sem mudar a pista */
  background-clip: content-box;
  border-radius: var(--r-pill);
}
textarea::-webkit-scrollbar-thumb:hover { background-color: var(--text-3); }

/* ------------------------------------------------------------
   6. CHECKBOX E RADIO
   Desenhados do zero: o controle nativo é a peça que mais grita
   "sistema operacional" dentro de uma UI escura.
   A caixa desenhada tem 18px para casar com a altura de linha do
   texto ao lado; ::before (transparente) estende a área de clique
   para 24px, acima do mínimo de 20px, sem alterar o desenho.
   Marca em mask+currentColor: um recurso, dois temas.
   Nota de cascata: .radios input do app.css continua vencendo
   (0,1,1 > 0) e aqueles radios seguem ocultos, como projetado.
   ------------------------------------------------------------ */
:where(input[type="checkbox"], input[type="radio"]) {
  -webkit-appearance: none;
  appearance: none;
  position: relative;
  display: inline-grid;
  place-content: center;
  inline-size: var(--ctl-mark);
  block-size: var(--ctl-mark);
  flex: none;
  margin: 0;
  padding: 0;
  vertical-align: -.1875rem;
  background-color: var(--bg-raised);
  border: 1px solid var(--ctl-border-forte);
  border-radius: var(--r-sm);
  cursor: pointer;
  transition: background-color var(--t-fast), border-color var(--t-fast);
}
:where(input[type="radio"]) { border-radius: 50%; }

:where(input[type="checkbox"], input[type="radio"])::before {
  content: "";
  position: absolute;
  inset: -3px;                       /* 18px + 3px de cada lado = 24px de alvo */
  border-radius: inherit;
}

:where(input[type="checkbox"], input[type="radio"]):hover:not(:disabled) {
  border-color: var(--accent);
}
/* Sem :where() aqui: `color` precisa vencer o
   `input { color: inherit }` do app.css (0,0,1), senão a marca
   assume a cor do rótulo ao lado — cinza sobre teal, invisível
   no tema claro. Esta é a cor que o ::after lê como currentColor. */
input[type="checkbox"]:checked,
input[type="radio"]:checked,
input[type="checkbox"]:indeterminate {
  background-color: var(--accent);
  border-color: var(--accent);
  color: var(--ctl-on-accent);
}

:where(input[type="checkbox"])::after {
  content: "";
  inline-size: .875rem;
  block-size: .875rem;
  opacity: 0;
  background-color: currentColor;
  -webkit-mask-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.4 6.4 11.3 12.5 4.8' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M3.5 8.4 6.4 11.3 12.5 4.8' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-size: contain;
  mask-size: contain;
  transition: opacity var(--t-fast);
}
:where(input[type="checkbox"]:checked)::after { opacity: 1; }

/* Indeterminate: mesma caixa, traço no lugar do check. Estado só
   existe via IDL property (elemento.indeterminate), não via HTML. */
:where(input[type="checkbox"]:indeterminate)::after {
  opacity: 1;
  -webkit-mask-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8h8' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");
  mask-image: url("data:image/svg+xml;charset=UTF-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M4 8h8' fill='none' stroke='%23000' stroke-width='2.2' stroke-linecap='round'/%3E%3C/svg%3E");
}

/* Radio: ponto opaco. Sem scale — só opacidade, conforme a marca. */
:where(input[type="radio"])::after {
  content: "";
  inline-size: .5rem;
  block-size: .5rem;
  border-radius: 50%;
  background-color: currentColor;
  opacity: 0;
  transition: opacity var(--t-fast);
}
:where(input[type="radio"]:checked)::after { opacity: 1; }
:where(input[type="checkbox"], input[type="radio"]):disabled { cursor: not-allowed; opacity: .5; }

/* Rótulo clicável: o alvo real de toque é o <label>. */
.campo-check {
  display: inline-flex;
  align-items: flex-start;
  gap: .5rem;
  padding: .1875rem 0;
  font-size: .875rem;
  line-height: 1.125rem;
  color: var(--text-2);
  cursor: pointer;
}
.campo-check input { margin-block-start: 0; }
.campo-check:has(input:disabled) { cursor: not-allowed; color: var(--text-3); }

/* ------------------------------------------------------------
   7. FOCO — sistema único
   O app.css tem `:focus-visible { outline: 2px solid var(--focus) }`
   e --focus é AZUL: é literalmente o anel que destoa. Aqui ele
   passa a ser o accent. `:root ` na frente sobe para (0,2,0) e
   vence o (0,1,0) do app.css independente da ordem de carga.

   O mesmo bloco do app.css declara `border-radius: inherit`, o
   que quadra o canto do controle enquanto ele está focado
   (o pai costuma ter raio 0). Reafirmo o raio de cada família —
   o outline segue o raio, então isso é o que mantém o anel redondo.

   Onde o app.css já entrega um foco em accent (.fld …:focus e
   .search input:focus, com box-shadow) eu não intervenho: dois
   anéis empilhados seriam pior que um. Sistema único = uma cor
   (accent) e uma regra por contexto, nunca duas sobrepostas.
   ------------------------------------------------------------ */
:root :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
:root :focus:not(:focus-visible) { outline: none; }
:root :is(select, textarea, input:not([type="checkbox"]):not([type="radio"])):focus-visible { border-radius: var(--r-md); }
:root input[type="checkbox"]:focus-visible { border-radius: var(--r-sm); }
:root input[type="radio"]:focus-visible { border-radius: 50%; }
/* Mesmo motivo, para os alvos focáveis que já têm raio próprio no
   app.css — cada família reafirma O SEU raio, não um raio único. */
:root :is(.btn, .vchip, .linkchip, .mini, .ainput):focus-visible { border-radius: var(--r-md); }
:root :is(.chip, .tag):focus-visible { border-radius: var(--r-pill); }
:root .seg button:focus-visible { border-radius: 4px; }
:root summary:focus-visible { outline-offset: 1px; }   /* summary é bloco largo: 2px encostaria na borda do details */

/* ------------------------------------------------------------
   8. GRUPOS — .campo / .campo-linha
   ------------------------------------------------------------ */
.campo {
  display: flex;
  flex-direction: column;
  gap: .375rem;
  margin-block-end: 1.25rem;
  min-inline-size: 0;
}
.campo > label,
.campo > .campo-lbl {
  font-size: .75rem;
  font-weight: 590;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--text-3);
}
.campo > label .req { color: var(--danger); margin-inline-start: .1875rem; }
.campo > :is(input, select, textarea) {
  inline-size: 100%;
  min-block-size: var(--ctl-h);
}
.campo > :is(input[type="checkbox"], input[type="radio"]) {
  inline-size: var(--ctl-mark);
  min-block-size: 0;
}
/* `textarea:not([rows])` (§5) é (0,1,1) — EMPATA com a regra acima
   e perderia por vir antes no arquivo. Reafirmado aqui em (0,2,1)
   para o textarea sem rows nascer com altura de textarea, e não
   com altura de input. */
.campo > textarea:not([rows]) { min-block-size: 5.5rem; }
.campo .dica {
  font-size: .75rem;
  line-height: 1.5;
  color: var(--text-3);
}
.campo .erro {
  font-size: .75rem;
  line-height: 1.5;
  color: var(--danger);
  display: none;                     /* só existe quando o grupo está inválido */
}

.campo.is-invalido .erro { display: block; }
.campo.is-invalido > label,
.campo.is-invalido > .campo-lbl { color: var(--danger); }
.campo.is-invalido :is(input, select, textarea) { border-color: var(--danger); }
.campo.is-invalido :is(input, select, textarea):hover:not(:disabled) {
  border-color: var(--danger);
}
/* (0,3,1) — precisa vencer o anel accent de §7 sem !important. */
.campo.is-invalido :is(input, select, textarea):focus-visible { outline-color: var(--danger); }
.campo.is-invalido :is(input[type="checkbox"], input[type="radio"]):checked {
  background-color: var(--danger);
  border-color: var(--danger);
}

/* Controles lado a lado. Cada filho continua sendo um .campo
   completo (rótulo + controle + dica + erro), só que em coluna
   flexível; abaixo de 480px volta a empilhar sozinho. */
.campo-linha {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  gap: .75rem;
  margin-block-end: 1.25rem;
}
.campo-linha > .campo {
  flex: 1 1 9rem;
  margin-block-end: 0;
}
.campo-linha > .campo.e-estreito { flex: 0 1 7rem; }
.campo-linha > .campo.e-largo { flex: 2 1 16rem; }

/* ------------------------------------------------------------
   9. BOTÕES — .btn
   Convive com o .ghost do app.css sem disputa: são dois blocos
   de classe única (0,1,0) e NENHUM seletor daqui menciona .ghost,
   então nada é sobrescrito. As métricas do .btn foram copiadas do
   .ghost de propósito (mesma altura, mesmo raio, mesmo peso 510),
   para os dois conviverem lado a lado na mesma barra sem degrau.
   Não combine .btn e .ghost no mesmo elemento.
   ------------------------------------------------------------ */
.btn {
  --ctl-spinner: var(--text-2);
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .4375rem;
  font-family: var(--font-sans);
  font-size: .8125rem;
  font-weight: 510;
  line-height: 1.125rem;
  padding: .375rem .75rem;
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background-color: transparent;
  color: var(--text-2);
  white-space: nowrap;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--t-fast), border-color var(--t-fast), color var(--t-fast);
}
.btn:hover { text-decoration: none; }

.btn--sm { font-size: .75rem; padding: .25rem .5625rem; }
.btn--md { font-size: .875rem; padding: .4375rem .875rem; }  /* = tamanho base, explicitado */

.btn--primario {
  --ctl-spinner: var(--ctl-on-accent);
  background-color: var(--accent);
  border-color: var(--accent);
  color: var(--ctl-on-accent);
}
/* :not(:disabled) em todo hover: sem isso o botão desabilitado
   continua reagindo ao mouse. */
.btn--primario:hover:not(:disabled) { filter: brightness(1.06); }
.btn--primario:active:not(:disabled) { filter: brightness(.96); }

.btn--secundario {
  background-color: var(--bg-raised);
  border-color: var(--border);
  color: var(--text-1);
}
.btn--secundario:hover:not(:disabled) {
  background-color: var(--surface-strong);
  border-color: var(--ctl-border-hover);
}

.btn--perigo {
  --ctl-spinner: var(--danger);
  border-color: var(--danger-border);
  color: var(--danger);
}
.btn--perigo:hover:not(:disabled) {
  background-color: var(--danger-soft);
  border-color: var(--danger);
}
.btn--perigo:focus-visible { outline-color: var(--danger); }

.btn--fantasma { color: var(--text-2); }
.btn--fantasma:hover:not(:disabled) { background-color: var(--surface); color: var(--text-1); }

.btn:disabled,
.btn[aria-disabled="true"] {
  opacity: .45;
  cursor: not-allowed;
  filter: none;
}

/* Carregando: o rótulo fica transparente (continua no DOM, então
   o leitor de tela segue lendo — use aria-busy="true") e o
   spinner ocupa o centro. Assim a largura do botão não muda e a
   barra de ações não dá salto. --ctl-spinner não é afetado por
   color:transparent, por isso o anel tem cor própria. */
.btn.is-carregando {
  color: transparent;
  pointer-events: none;
}
.btn.is-carregando::after {
  content: "";
  position: absolute;
  inset: 0;
  margin: auto;
  inline-size: .875rem;
  block-size: .875rem;
  border: 2px solid var(--ctl-spinner);
  border-block-start-color: transparent;
  border-radius: 50%;
  animation: ctl-girar .6s linear infinite;
}
@keyframes ctl-girar { to { transform: rotate(360deg); } }

/* ------------------------------------------------------------
   10. CONTRASTE EM SUPERFÍCIE ELEVADA (admin)
   Auditoria dos dois temas:
   - claro: controle #FFF sobre .panel (surface ~#F2F4F5) —
     separação vem do texto e do hairline, OK.
   - escuro: controle #142638 sobre .panel (~#18202B) são vizinhos
     demais, e o hairline #223244 quase some. O mix abaixo dá o
     mínimo de definição sem virar contorno desenhado.
   Texto: --text-1 sobre --bg-raised passa AAA nos dois temas;
   placeholder --text-3 fica em ~4.6:1 (AA), proposital.
   ------------------------------------------------------------ */
:is(.panel, .chgcard, .abar, .slideover, .warnbar) :is(
  input:not([type="checkbox"]):not([type="radio"]),
  select,
  textarea
):not(:disabled) {
  border-color: var(--ctl-border-hover);
}

/* ------------------------------------------------------------
   11. ALTO CONTRASTE FORÇADO (Windows)
   appearance:none apaga a semântica visual nativa; sem isto o
   checkbox marcado e o desmarcado ficam idênticos nesse modo.
   ------------------------------------------------------------ */
@media (forced-colors: active) {
  :where(input, select, textarea, .btn) {
    border-color: ButtonBorder;
    color: FieldText;
  }
  :where(input[type="checkbox"], input[type="radio"]) { border-color: ButtonBorder; }
  :where(input[type="checkbox"]:checked, input[type="radio"]:checked, input[type="checkbox"]:indeterminate) {
    background-color: Highlight;
    border-color: Highlight;
    color: HighlightText;
    forced-color-adjust: none;
  }
  :root :focus-visible { outline-color: Highlight; }
  .btn.is-carregando::after { border-color: ButtonText; border-block-start-color: transparent; }
}

/* ------------------------------------------------------------
   12. MOVIMENTO REDUZIDO
   A regra global do app.css já zera as durações, o que
   congelaria o spinner num anel qualquer. Aqui ele vira um anel
   estático assumido, com o gap ainda legível como "ocupado".
   ------------------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .btn.is-carregando::after {
    animation: none;
    opacity: .7;
  }
}

/* ------------------------------------------------------------
   13. IMPRESSÃO
   Controles viram registro em papel: sem seta, sem fundo,
   sem spinner. Só valor e um contorno leve.
   ------------------------------------------------------------ */
@media print {
  :where(input, select, textarea) {
    background-color: #fff;
    background-image: none;
    color: #000;
    border-color: #ccc;
    opacity: 1;
  }
  select:not([multiple]):not([size]) { background-image: none; padding-inline-end: var(--ctl-pad-x); }
  input[type="search"]::-webkit-search-cancel-button,
  input[type="date"]::-webkit-calendar-picker-indicator { display: none; }
  .btn { background-color: transparent; border-color: #ccc; color: #000; filter: none; }
  .btn.is-carregando { color: #000; }
  .btn.is-carregando::after { display: none; }
  .campo .erro { display: block; }
  textarea { resize: none; }
}
