/* ═══════════════════════════════════════════════════════════════════════════
   Tuff: a base. O que vale para a página inteira, antes de qualquer componente.

   Reset mínimo, tipografia, scrollbar, foco e seleção. Os componentes moram em
   `tuff.css`; os valores, em `tuff-tokens.css`, que precisa vir antes deste.

   ─── De onde vem ────────────────────────────────────────────────────────────

   Este arquivo nasceu na biblioteca de componentes do toolkit de apps
   (`vssh-app-toolkit/lib/web/tuff/`), como a página de um app a recebe: um
   `<link>` injetado pelo backend, depois do CSS do próprio app. Ele é servido
   daqui a um app como `_sdk/tuff/tuff-base.css`.

   O shell não o carrega. As folhas dele foram escritas contra os padrões do
   navegador (caixa `content-box`, margem de `<p>`, sem anel de foco global), e
   um reset entrando por baixo delas mudaria a medida de todo elemento que não
   declara a própria caixa. Ele entra no shell quando as folhas migrarem para o
   Tuff, uma de cada vez, com alguém olhando a tela.

   ─── Por que é um arquivo separado ──────────────────────────────────────────

   O `tuff: true` do `vssh.web` o injeta junto dos tokens e dos componentes,
   porque o app novo é quem mais escreve HTML sem classe. Nem todo consumidor
   quer: um bundle grande e antigo (o Logseq é o caso real) traz o CSS dele
   inteiro, e um `box-sizing: border-box` global é estrago silencioso, em que
   nada quebra alto e as caixas só passam a medir outra coisa. Separado, quem
   não quer passa a lista sem ele (`tuff=['tuff-tokens.css', 'tuff.css',
   'tuff.js']`).

   ─── O HTML sem classe ──────────────────────────────────────────────────────

   Um `<button>`, um `<input>`, um `<select>` ou uma `<table>` sem classe saem
   com o desenho da peça equivalente (`.tuff-btn`, `.tuff-campo`, `.tuff-select`,
   `.tuff-tabela`), e não com o do navegador: um botão cinza-claro sobre o
   grafite e uma lista de opções branca. A régua do Tuff diz "controle nativo:
   nunca", e uma régua que depende de lembrar a classe falha onde alguém
   esqueceu.

   Os seletores são `:where(...:not([class]))`: especificidade zero, e só o
   elemento sem classe nenhuma. Um botão com a classe do próprio app fica de
   fora inteiro, e por isso uma borda que o app não declarou nunca aparece
   nele. Caixa de marcar, opção e faixa continuam nativas, com a cor de
   destaque: no Android, o controle nativo é o certo para o toque, e o escuro
   vem do `color-scheme`.

   ─── @layer, a decisão mais importante deste arquivo ────────────────────────

   Num app, a folha injetada entra antes de `</head>`, ou seja depois do CSS do
   próprio app. Com especificidade igual, quem vem depois ganha, e a biblioteca
   venceria o app em todo empate. Regra dentro de camada perde para regra fora de
   camada, seja qual for a ordem no arquivo, e envolver tudo em `@layer vssh`
   inverte a hierarquia para a certa:

       padrão da biblioteca  <  escolha do app  <  escolha do ambiente
       (@layer vssh)            (CSS sem camada)   (inline, que ganha de tudo)

   Isso é invisível a teste de texto: o seletor está lá, a regra está lá, e o
   que muda é só quem ganha. Cascata se mede num Chrome de verdade.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vssh {

  /* ── O ambiente é escuro ────────────────────────────────────────────────────
     Uma linha, e ela conserta o que não é nosso: os controles nativos que
     sobrarem, a scrollbar de subframes, o fundo dos campos antes do CSS
     carregar. Sem ela, um `<input type="date">` abre um calendário branco no
     meio de uma janela escura.

     Não há `light dark` aqui. O Tuff é escuro, e prometer um modo claro que não
     existe seria pior que não prometer nada. */
  :root {
    color-scheme: dark;
  }

  *, *::before, *::after { box-sizing: border-box; }

  body {
    margin: 0;
    background: var(--ds-bg);
    color: var(--ds-text);
    /* 13px é a medida do ambiente. Nos componentes do shell, 12px domina, com 11
       e 13 ao lado; o conteúdo de um app respira um pouco mais que a barra de
       tarefas, então a base é o `md` e as peças densas descem para `sm`. */
    font: var(--tuff-weight-n) var(--tuff-size-md)/1.5 var(--ds-font);
    -webkit-font-smoothing: antialiased;
  }

  /* ── Hierarquia ─────────────────────────────────────────────────────────────
     Rótulo e valor não podem ter o mesmo peso: sem hierarquia, uma coluna de
     opções vira uma pilha de pares iguais. Os títulos sobem por peso e cor antes
     de subir por tamanho; uma página de app não é um artigo, e escalas grandes
     aqui só empurram o conteúdo para baixo. */
  h1, h2, h3, h4 {
    margin: 0;
    font-weight: var(--tuff-weight-b);
    color: var(--ds-text-mid);
    line-height: 1.3;
  }
  h1 { font-size: var(--tuff-size-2xl); }
  h2 { font-size: var(--tuff-size-xl); }
  h3 { font-size: var(--tuff-size-lg); }
  h4 { font-size: var(--tuff-size-md); }

  p { margin: 0; }

  a {
    color: var(--ds-accent-texto);
    text-decoration: none;
  }
  a:hover { text-decoration: underline; }

  code, kbd, samp, pre {
    font-family: var(--ds-font-mono);
    font-size: var(--tuff-size-sm);
  }

  /* ── Seleção ────────────────────────────────────────────────────────────────
     A do ambiente, e não a azul do sistema. `--ds-sel` é uma das quatro
     variáveis que acompanham a cor escolhida pela pessoa, então selecionar
     texto num app fica da cor do ambiente dela. */
  ::selection {
    background: var(--ds-sel);
    color: var(--ds-sel-text);
  }

  /* ── Foco ───────────────────────────────────────────────────────────────────
     `:focus-visible`, e não `:focus`: o anel aparece para quem navega por
     teclado e não para quem acabou de clicar. Um anel em todo clique é o motivo
     pelo qual tanta gente desliga o foco por completo, e aí a navegação por
     teclado morre em silêncio.

     `outline` em vez de `box-shadow`: outline não participa do layout, então o
     anel não empurra nada e sobrevive a `overflow: hidden`. */
  :focus-visible {
    outline: 2px solid var(--ds-border-focus);
    outline-offset: 1px;
    border-radius: var(--ds-radius-sm);
  }

  /* ── A scrollbar do tema ────────────────────────────────────────────────────
     6px, track transparente, polegar em `--ds-border`. Uma barra cinza do
     sistema no meio de uma janela escura é o que denuncia uma página web dentro
     do ambiente.

     As duas sintaxes não somam: a padrão mata a do WebKit. Declarar
     `scrollbar-width` com qualquer valor diferente de `auto` faz o Chrome
     ignorar `::-webkit-scrollbar-*` por completo, e a barra volta a ser a do
     sistema; medido, com as duas juntas a barra ocupa 10px em vez de 6, e
     nenhum teste de texto acusa, porque as duas regras estão escritas e
     corretas.

     Por isso a padrão entra dentro de um `@supports not selector(...)`: ela
     vale onde o pseudo-elemento do WebKit não existe, que é o Firefox. É a
     pergunta certa, feita sobre a capacidade que decide, e não detecção de
     navegador disfarçada. No Firefox fica o limite: `thin` é o mais fino que a
     plataforma oferece, e não há como pedir 6px. */
  ::-webkit-scrollbar { width: var(--tuff-sb-w); height: var(--tuff-sb-w); }
  ::-webkit-scrollbar-track { background: transparent; }
  ::-webkit-scrollbar-thumb {
    background: var(--ds-border);
    border-radius: var(--ds-radius-lg);
  }
  ::-webkit-scrollbar-thumb:hover { background: var(--tuff-sb-thumb-h); }
  ::-webkit-scrollbar-corner { background: transparent; }

  @supports not selector(::-webkit-scrollbar) {
    * {
      scrollbar-width: thin;
      scrollbar-color: var(--ds-border) transparent;
    }
  }

  /* ── O HTML sem classe ──────────────────────────────────────────────────────
     O desenho de cada peça do `tuff.css`, para quem escreveu a tag sem a classe.
     As medidas de caixa (`width: 100%` do campo) ficam de fora: elas são do
     layout do app, e o elemento sem classe mantém a largura que tinha. */

  :where(button:not([class]),
         input:not([class]):is([type="button"], [type="submit"], [type="reset"])) {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: var(--ds-gap-sm);
    padding: 5px var(--ds-gap-lg);
    min-height: var(--ds-alvo);
    font: var(--tuff-weight-n) var(--tuff-size-sm) var(--ds-font);
    color: var(--ds-text);
    background: var(--ds-bg3);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-md);
    cursor: pointer;
    white-space: nowrap;
    transition: background var(--ds-dur-fast) var(--ds-ease),
                border-color var(--ds-dur-fast) var(--ds-ease);
  }
  :where(button:not([class]), input:not([class]):is([type="button"], [type="submit"], [type="reset"])):hover:not(:disabled) {
    background: var(--ds-bg-hover);
  }
  :where(button:not([class]), input:not([class]):is([type="button"], [type="submit"], [type="reset"])):disabled {
    opacity: .45;
    cursor: default;
  }

  :where(input:not([class]):not([type="checkbox"], [type="radio"], [type="range"], [type="file"],
                                 [type="color"], [type="button"], [type="submit"], [type="reset"],
                                 [type="image"], [type="hidden"]),
         textarea:not([class])) {
    padding: 5px var(--ds-gap-md);
    min-height: var(--ds-alvo);
    font: var(--tuff-weight-n) var(--tuff-size-sm) var(--ds-font);
    color: var(--ds-text);
    background: var(--ds-bg-input);
    border: 1px solid var(--ds-border-input);
    border-radius: var(--ds-radius-md);
  }
  :where(input:not([class]), textarea:not([class]))::placeholder { color: var(--ds-text-dim); }
  :where(input:not([class]), textarea:not([class])):focus { border-color: var(--ds-border-focus); }
  :where(input:not([class]), textarea:not([class])):disabled { opacity: .5; }
  :where(textarea:not([class])) { min-height: 5rem; resize: vertical; line-height: 1.5; }

  /* A seta é a mesma do `.tuff-select`, com a mesma cor escrita no SVG. */
  :where(select:not([class])) {
    padding: 5px 28px 5px var(--ds-gap-md);
    min-height: var(--ds-alvo);
    font: var(--tuff-weight-n) var(--tuff-size-sm) var(--ds-font);
    color: var(--ds-text);
    background-color: var(--ds-bg-input);
    border: 1px solid var(--ds-border-input);
    border-radius: var(--ds-radius-md);
    appearance: none;
    background-repeat: no-repeat;
    background-position: right var(--ds-gap-md) center;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6' viewBox='0 0 10 6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23a0a0a0' stroke-width='1.5' stroke-linecap='round'/%3E%3C/svg%3E");
    cursor: pointer;
  }
  :where(select:not([class])):focus { border-color: var(--ds-border-focus); }
  /* A lista aberta, que em Windows herda o fundo do sistema e sai branca. */
  :where(select:not([class]) option) { background: var(--ds-bg3); color: var(--ds-text); }

  :where(input[type="checkbox"], input[type="radio"], input[type="range"], progress) {
    accent-color: var(--ds-accent);
  }
  /* A caixa de marcar é pequena, e o alvo é o rótulo dela, com a altura de um controle. O
     controle deslizante e o resumo de um `<details>` ganham a mesma altura pela caixa: o desenho
     nativo continua o mesmo, centrado nela. */
  :where(label:has(> input[type="checkbox"], > input[type="radio"])) {
    display: inline-flex;
    align-items: center;
    gap: var(--ds-gap-sm);
    min-height: var(--ds-alvo);
  }
  :where(input[type="range"]) { min-height: var(--ds-alvo); }
  :where(details:not([class]) > summary) {
    padding-block: max(0px, calc((var(--ds-alvo) - 1lh) / 2));
    cursor: pointer;
  }

  :where(table:not([class])) { border-collapse: collapse; font-size: var(--tuff-size-sm); color: var(--ds-text); }
  :where(table:not([class]) th) {
    height: 26px;
    padding: 0 var(--ds-gap-md);
    text-align: left;
    font-size: var(--tuff-size-xs);
    font-weight: var(--tuff-weight-b);
    color: var(--ds-text-dim);
    white-space: nowrap;
    background: var(--ds-bg2);
    border-bottom: 1px solid var(--ds-border);
  }
  :where(table:not([class]) td) { padding: 4px var(--ds-gap-md); border-bottom: 1px solid var(--ds-border); }

  :where(hr:not([class])) {
    height: 1px;
    margin: var(--ds-gap-sm) 0;
    background: var(--ds-border);
    border: 0;
  }

  :where(details:not([class]) > summary) { cursor: pointer; color: var(--ds-text-mid); }

  /* A barra do `.tuff-progresso`: o trilho no fundo de campo e o preenchimento no destaque. */
  :where(progress:not([class])) {
    appearance: none;
    height: 4px;
    border: 0;
    border-radius: 999px;
    background: var(--ds-bg-input);
    overflow: hidden;
  }
  :where(progress:not([class]))::-webkit-progress-bar { background: var(--ds-bg-input); }
  :where(progress:not([class]))::-webkit-progress-value { background: var(--ds-accent); }
  :where(progress:not([class]))::-moz-progress-bar { background: var(--ds-accent); }

  /* O medidor diz se o valor está bem, perto do limite ou passou, pelas cores de estado. */
  :where(meter:not([class])) { height: 6px; border-radius: 999px; background: var(--ds-bg-input); }
  :where(meter:not([class]))::-webkit-meter-bar { height: 6px; border: 0; border-radius: 999px; background: var(--ds-bg-input); }
  :where(meter:not([class]))::-webkit-meter-optimum-value { background: var(--ds-green); border-radius: 999px; }
  :where(meter:not([class]))::-webkit-meter-suboptimum-value { background: var(--ds-warn); border-radius: 999px; }
  :where(meter:not([class]))::-webkit-meter-even-less-good-value { background: var(--ds-danger); border-radius: 999px; }

  :where(fieldset:not([class])) {
    margin: 0;
    padding: var(--ds-gap-md) var(--ds-gap-lg) var(--ds-gap-lg);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-lg);
  }
  /* A legenda é o `.tuff-rotulo`. */
  :where(fieldset:not([class]) > legend) {
    padding: 0 var(--ds-gap-xs);
    font-size: var(--tuff-size-xs);
    font-weight: var(--tuff-weight-m);
    color: var(--ds-text-dim);
  }

  :where(blockquote:not([class])) {
    margin: 0;
    padding-left: var(--ds-gap-lg);
    border-left: 2px solid var(--ds-border);
    color: var(--ds-text-dim);
  }

  /* ── Movimento ──────────────────────────────────────────────────────────────
     Quem pediu menos movimento ao sistema pediu ao app também. Enxaqueca
     vestibular é o motivo pelo qual esta media query existe.

     `0.01ms` em vez de `0`: com duração zero o navegador não dispara
     `transitionend`, e código que espera esse evento para seguir fica
     pendurado, um modo de falha que só aparece para quem ligou a preferência. */
  @media (prefers-reduced-motion: reduce) {
    *, *::before, *::after {
      animation-duration: 0.01ms !important;
      animation-iteration-count: 1 !important;
      transition-duration: 0.01ms !important;
      scroll-behavior: auto !important;
    }
  }
}
