/* ═══════════════════════════════════════════════════════════════════════════
   Tuff: as peças de mídia.

   Opt-in: um app de formulário não deve pagar o download nem o parse disto. O
   comportamento mora em `tuff-midia.js`, que também é opt-in. Nasceu na
   biblioteca de componentes do toolkit de apps
   (`vssh-app-toolkit/lib/web/tuff/`); a fonte é esta, e um app o recebe como
   `_sdk/tuff/tuff-midia.css`. O shell não carrega esta folha: a central de
   mídia dele tem as próprias peças.

   ─── Onde o Tuff acrescenta ─────────────────────────────────────────────────

   O shell não tem player, grade de fotos nem visor com zoom; os únicos parentes
   são o slider dos diálogos e as linhas do mixer de volume. Aqui a biblioteca
   deixa de compilar o que o shell desenha e passa a acrescentar, e a régua é a
   mesma do resto.

   ─── A tese ─────────────────────────────────────────────────────────────────

   Todo player de vídeo na web tem a mesma cara: uma barra de vidro sobre um
   degradê escuro, flutuando por cima da imagem. Este não. O sistema declara
   `--ds-blur: none`, decisão de produto registrada no arquivo de tokens, então
   o chrome aqui é o material do ambiente: `--ds-bg2` opaco, com uma divisa de
   1px em `--ds-border`, a mesma da barra de tarefas. O vídeo faz letterbox
   contra `--ds-bg`. A janela lê como aplicativo do ambiente.

   ─── A assinatura ───────────────────────────────────────────────────────────

   O destaque da pessoa é o playhead. `--ds-accent` é a única coisa que ela
   escolhe neste ambiente; ele vira a porção reproduzida da trilha e o anel de
   seleção da grade. Trocar a cor nas Configurações repinta o pixel que os olhos
   mais seguem.

   A trilha tem 3px em repouso e 6px sob o ponteiro. O 6 é `--tuff-sb-w`, a
   medida de trilho fino do ambiente, a mesma da scrollbar.
   ═══════════════════════════════════════════════════════════════════════════ */

@layer vssh {

  /* ── O palco ────────────────────────────────────────────────────────────────
     Letterbox contra o fundo da janela, e não contra preto. Preto puro ao lado
     do cinza da página vira uma moldura que ninguém pediu; o mesmo fundo
     desaparece, e o que sobra é a imagem. */

  .tuff-palco {
    position: relative;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ds-bg);
    overflow: hidden;
    min-height: 0;
  }
  .tuff-palco > video,
  .tuff-palco > img {
    max-width: 100%;
    max-height: 100%;
    display: block;
  }
  /* Enquanto o chrome está oculto, o ponteiro some junto: um cursor parado
     sobre um filme é a única coisa em movimento na tela. */
  .tuff-palco--limpo { cursor: none; }

  /* ── O chrome ───────────────────────────────────────────────────────────────
     Opaco, com o material da barra de tarefas. Ele some sozinho; a regra de foco
     em `tuff-midia.js` é o que separa isto de uma armadilha para quem navega
     por teclado. */

  .tuff-chrome {
    display: flex;
    align-items: center;
    gap: var(--ds-gap-lg);
    padding: var(--ds-gap-md) var(--ds-gap-lg);
    background: var(--ds-bg2);
    border-top: 1px solid var(--ds-border);
    transition: opacity var(--ds-dur) var(--ds-ease),
                transform var(--ds-dur) var(--ds-ease);
  }
  /* Volta em `--ds-dur-fast` e sai em `--ds-dur`: chegar devagar parece
     quebrado, sair rápido parece nervoso. */
  .tuff-chrome:not(.tuff-chrome--oculto) { transition-duration: var(--ds-dur-fast); }
  .tuff-chrome--oculto {
    opacity: 0;
    transform: translateY(100%);
    pointer-events: none;
  }
  /* Sobreposto, para o app que quer o chrome por cima da imagem em vez de
     abaixo dela. Continua opaco: a diferença é onde ele mora, e não do que é
     feito. */
  .tuff-chrome--sobreposto {
    position: absolute;
    left: 0;
    right: 0;
    bottom: 0;
  }

  /* ── O transporte ───────────────────────────────────────────────────────────
     O chrome acima fica sobre o vídeo. Isto é a outra peça: a barra que não
     some, embaixo da janela inteira, enquanto a pessoa navega a biblioteca. O
     vídeo não parou de tocar só porque ela foi olhar outra coisa, e é essa
     barra que faz um app parecer um player de desktop em vez de uma página com
     vídeo.

     ─── Dois andares ─────────────────────────────────────────────────────────

     Nove controles em fila leem como barra de ferramentas: nada distingue
     comando de reprodução de ajuste. Dois andares separam duas perguntas:

       andar 1   onde estou no tempo    decorrido ─── trilha ─── total
       andar 2   o que eu faço          o que toca | transporte | ajustes

     O tempo ladeia a trilha, como no VLC, em vez de virar mais um item na
     fileira de botões: os dois números falam da mesma coisa que a trilha, e
     juntá-los deixa o andar de baixo só com comandos.

     ─── Por que `1fr auto 1fr` e não `flex` ──────────────────────────────────

     Para o aglomerado de transporte cair no centro óptico da janela, e
     continuar lá quando o nome do que está tocando for curto ou comprido. Com
     `flex` ele anda conforme o texto ao lado cresce, e o botão que a pessoa
     procura sem olhar muda de lugar a cada faixa. */

  .tuff-transporte {
    background: var(--ds-bg2);
    border-top: 1px solid var(--ds-border);
  }

  .tuff-transporte-tempo {
    display: flex;
    align-items: center;
    gap: var(--ds-gap-md);
    padding: var(--ds-gap-sm) var(--ds-gap-md) 0;
  }
  .tuff-transporte-tempo > .tuff-trilha[data-tuff-trilha] { flex: 1; }

  .tuff-transporte-controles {
    display: grid;
    grid-template-columns: 1fr auto 1fr;
    align-items: center;
    gap: var(--ds-gap-md);
    padding: 2px var(--ds-gap-md) var(--ds-gap-sm);
  }
  /* `min-width: 0` no início: sem ele, um nome comprido de faixa empurra o
     aglomerado do meio para fora do centro, que é o que a grade existe para
     impedir. O fim guarda a largura dos botões dele: numa barra estreita ele
     toma o espaço do início, e nunca fica por cima do meio. */
  .tuff-transporte-inicio,
  .tuff-transporte-fim { display: flex; align-items: center; }
  .tuff-transporte-inicio { gap: var(--ds-gap-sm); min-width: 0; }
  .tuff-transporte-fim { gap: 2px; justify-content: flex-end; }
  /* Sem espaço entre os botões de transporte: eles são um grupo. Separá-los
     faria cada um parecer um comando independente, e é assim que "parar" vira
     vizinho de "tela cheia". */
  .tuff-transporte-meio { display: flex; align-items: center; gap: 1px; }

  /* ── A trilha ───────────────────────────────────────────────────────────────
     Quatro camadas empilhadas no mesmo trilho: o fundo, o que já baixou, o que
     já tocou, e as marcas de capítulo. As marcas são falhas de 1px na cor do
     fundo, espaço negativo em vez de pontinhos somados por cima. */

  /* O seletor leva o `data-tuff-trilha`, que é por onde o `TuffMidia` acha a peça, porque a
     `.tuff-trilha` sozinha é a trilha do caminho, do `tuff.css`: com as duas folhas na página, a
     do caminho ficaria com 14 px de altura e sem rolagem pelo dedo. */
  .tuff-trilha[data-tuff-trilha] {
    position: relative;
    flex: 1;
    height: max(24px, var(--ds-alvo));   /* área de clique; o trilho pintado é menor */
    display: flex;
    align-items: center;
    cursor: pointer;
    touch-action: none;           /* o arraste é nosso, não do scroll da página */
    min-width: 4rem;
  }
  /* O trilho é `--ds-bg3` e o buffer é `--ds-text-dim`, escolhidos olhando a
     peça ampliada: `--ds-bg-input` e `--ds-border` ficam a 1,28:1 um do outro,
     e uma faixa bufferizada pintada com eles some, sendo que ela existe para se
     distinguir do que ainda não baixou. A hierarquia é a de um player: sulco escuro, buffer em cinza
     neutro, tocado na cor de destaque, que é saturada e domina mesmo sendo
     mais escura que o cinza. */
  .tuff-trilha-trilho {
    position: relative;
    width: 100%;
    height: 3px;
    background: var(--ds-bg3);
    border-radius: 999px;
    overflow: hidden;
    transition: height var(--ds-dur-fast) var(--ds-ease);
  }
  .tuff-trilha[data-tuff-trilha]:hover .tuff-trilha-trilho,
  .tuff-trilha[data-tuff-trilha]:focus-visible .tuff-trilha-trilho,
  .tuff-trilha--arrastando .tuff-trilha-trilho { height: var(--tuff-sb-w); }

  .tuff-trilha-buffer {
    position: absolute;
    inset: 0 auto 0 0;
    width: 0;
    background: var(--ds-text-dim);
  }
  .tuff-trilha-tocado {
    position: absolute;
    inset: 0 auto 0 0;
    width: 0;
    background: var(--ds-accent);
  }
  .tuff-trilha-marca {
    position: absolute;
    top: 0;
    bottom: 0;
    width: 1px;
    background: var(--ds-bg);
  }

  /* O polegar só aparece quando há a quem servir: sob o ponteiro, ou com foco
     de teclado. Um ponto permanente sobre uma linha de 3px é ruído em toda a
     duração do filme. */
  .tuff-trilha-polegar {
    position: absolute;
    top: 50%;
    left: 0;
    width: 11px;
    height: 11px;
    margin: -5.5px 0 0 -5.5px;
    border-radius: 50%;
    background: var(--ds-accent);
    opacity: 0;
    pointer-events: none;
    transition: opacity var(--ds-dur-fast) var(--ds-ease);
  }
  .tuff-trilha[data-tuff-trilha]:hover .tuff-trilha-polegar,
  .tuff-trilha[data-tuff-trilha]:focus-visible .tuff-trilha-polegar,
  .tuff-trilha--arrastando .tuff-trilha-polegar { opacity: 1; }

  /* A prévia do ponto sob o ponteiro: o timecode, e um quadro se o app tiver um. */
  .tuff-trilha-previa {
    position: absolute;
    bottom: calc(100% + 8px);
    transform: translateX(-50%);
    padding: 2px var(--ds-gap-md);
    background: var(--ds-bg3);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-sm);
    box-shadow: var(--ds-shadow-md);
    font: var(--tuff-weight-n) var(--tuff-size-xs)/1.4 var(--ds-font-mono);
    font-variant-numeric: tabular-nums;
    color: var(--ds-text);
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--ds-dur-fast) var(--ds-ease);
  }
  .tuff-trilha[data-tuff-trilha]:hover .tuff-trilha-previa,
  .tuff-trilha--arrastando .tuff-trilha-previa { opacity: 1; }

  /* ── A forma de onda ────────────────────────────────────────────────────────
     O canvas ocupa a caixa inteira, e a altura é do app (40px por padrão, a de
     uma linha de transporte, e o alvo sob o dedo). A prévia de tempo é a mesma peça da trilha, sobre
     o ponteiro. */

  .tuff-onda {
    position: relative;
    flex: 1;
    min-width: 4rem;
    height: max(40px, var(--ds-alvo));
    cursor: pointer;
    touch-action: none;           /* o arraste é da onda, não do scroll da página */
    border-radius: var(--ds-radius-sm);
    outline: none;
  }
  .tuff-onda canvas { display: block; width: 100%; height: 100%; }
  .tuff-onda:focus-visible { box-shadow: 0 0 0 1px var(--ds-border-focus); }
  .tuff-onda-previa {
    position: absolute;
    bottom: calc(100% + 6px);
    transform: translateX(-50%);
    padding: 2px var(--ds-gap-md);
    background: var(--ds-bg3);
    border: 1px solid var(--ds-border);
    border-radius: var(--ds-radius-sm);
    box-shadow: var(--ds-shadow-md);
    font: var(--tuff-weight-n) var(--tuff-size-xs)/1.4 var(--ds-font-mono);
    font-variant-numeric: tabular-nums;
    color: var(--ds-text);
    white-space: nowrap;
    pointer-events: none;
    opacity: 0;
    transition: opacity var(--ds-dur-fast) var(--ds-ease);
  }
  .tuff-onda:hover .tuff-onda-previa,
  .tuff-onda--arrastando .tuff-onda-previa { opacity: 1; }

  /* ── Timecode ───────────────────────────────────────────────────────────────
     `tabular-nums`. Numa fonte proporcional o "1" é mais estreito que o "8",
     então cada segundo que passa mexe a largura do número e o texto treme, no
     único elemento que o olho acompanha continuamente. */

  .tuff-tempo {
    flex: none;
    font: var(--tuff-weight-n) var(--tuff-size-sm) var(--ds-font-mono);
    font-variant-numeric: tabular-nums;
    color: var(--ds-text-dim);
    white-space: nowrap;
  }
  .tuff-tempo b { font-weight: var(--tuff-weight-n); color: var(--ds-text); }

  /* ── Volume ─────────────────────────────────────────────────────────────────
     Desce do slider dos diálogos e das linhas do mixer, que são os parentes
     reais desta peça no ambiente. Estreito de propósito: volume não é a
     informação principal de um player, e um trilho longo aqui compete com a
     trilha do tempo. */

  .tuff-volume {
    display: flex;
    align-items: center;
    gap: var(--ds-gap-md);
    flex: none;
  }
  .tuff-volume-trilha {
    position: relative;
    width: 64px;
    height: max(24px, var(--ds-alvo));
    display: flex;
    align-items: center;
    cursor: pointer;
    touch-action: none;
  }
  /* Mesmo sulco da trilha do tempo: as duas são o mesmo tipo de controle, e
     fundos diferentes fariam uma parecer ativa e a outra desligada. */
  .tuff-volume-trilho {
    width: 100%;
    height: 3px;
    background: var(--ds-bg3);
    border-radius: 999px;
    overflow: hidden;
  }
  .tuff-volume-nivel {
    height: 100%;
    width: 100%;
    background: var(--ds-text-dim);
  }
  /* Mudo é um estado diferente de volume zero, e tem de ser distinguível de
     "abaixei tudo". A barra fica, esmaecida, guardando o valor para o qual se
     volta. */
  .tuff-volume--mudo .tuff-volume-nivel { background: var(--ds-border); }

  /* ── A grade de miniaturas ──────────────────────────────────────────────────
     Virtualizada, e é o motivo de esta peça existir. Uma pasta de fotos tem
     dezenas de milhares de arquivos; montar um `<img>` para cada um trava a
     aba na abertura e nunca se recupera. `tuff-midia.js` mantém no DOM só o que
     está visível, e o fuso abaixo dá ao contêiner a altura que a rolagem
     precisa ter. */

  .tuff-grade {
    position: relative;
    overflow: auto;
    min-height: 0;
    background: var(--ds-bg);
  }
  /* Um bloco vazio com a altura do conteúdo inteiro. É ele que faz a barra de
     rolagem ter o tamanho certo enquanto existem 40 elementos no DOM. */
  .tuff-grade-fuso { position: relative; width: 100%; }

  .tuff-miniatura {
    position: absolute;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--ds-bg2);
    border-radius: var(--ds-radius-md);
    overflow: hidden;
    cursor: default;
    outline: 2px solid transparent;   /* reserva o espaço do anel: selecionar não pode mover nada */
    outline-offset: -2px;
    transition: outline-color var(--ds-dur-fast) var(--ds-ease);
  }
  .tuff-miniatura img {
    width: 100%;
    height: 100%;
    object-fit: cover;
    display: block;
  }
  .tuff-miniatura:hover { outline-color: var(--ds-border); }
  .tuff-miniatura[aria-selected="true"] { outline-color: var(--ds-accent); }
  .tuff-miniatura:focus-visible { outline-color: var(--ds-border-focus); }

  /* ── A tira ─────────────────────────────────────────────────────────────────
     A fita horizontal ao pé do visor. Mesma seleção da grade, para as duas
     peças não ensinarem coisas diferentes sobre "o que está aberto": a tira é
     um `role="listbox"` com nome, e cada item um `role="option"`, que é o papel
     que aceita o `aria-selected`. */

  .tuff-tira {
    display: flex;
    gap: var(--ds-gap-sm);
    overflow-x: auto;
    padding: var(--ds-gap-sm);
    background: var(--ds-bg2);
    border-top: 1px solid var(--ds-border);
    scroll-snap-type: x proximity;
  }
  .tuff-tira-item {
    flex: none;
    width: 64px;
    height: 44px;
    border-radius: var(--ds-radius-sm);
    overflow: hidden;
    background: var(--ds-bg3);
    scroll-snap-align: center;
    outline: 2px solid transparent;
    outline-offset: -2px;
    cursor: default;
  }
  .tuff-tira-item img { width: 100%; height: 100%; object-fit: cover; display: block; }
  .tuff-tira-item[aria-selected="true"] { outline-color: var(--ds-accent); }

  /* ── O visor ────────────────────────────────────────────────────────────────
     Zoom e arraste. O conteúdo é transformado, e não redimensionado:
     `transform` roda no compositor, e uma imagem de 60 megapixels reflowada a
     cada roda do mouse não acompanha o gesto. */

  .tuff-visor {
    position: relative;
    overflow: hidden;
    background: var(--ds-bg);
    touch-action: none;
    cursor: grab;
    min-height: 0;
  }
  .tuff-visor--arrastando { cursor: grabbing; }
  /* O anel de foco por dentro: o visor costuma encostar nas bordas da janela, e o anel de
     fora ficaria cortado por elas. */
  .tuff-visor:focus-visible { outline-offset: -2px; }
  .tuff-visor-conteudo {
    position: absolute;
    top: 0;
    left: 0;
    transform-origin: 0 0;
  }
  /* `will-change` só durante o gesto, que o script marca com `--movendo`. Fixo, ele faz o
     Chrome rasterizar a camada uma vez e depois esticar o bitmap, e a imagem ampliada fica
     borrada; ao fim do gesto a camada é rasterizada de novo na escala final. */
  .tuff-visor--movendo .tuff-visor-conteudo { will-change: transform; }
  .tuff-visor-conteudo img { display: block; max-width: none; -webkit-user-drag: none; user-select: none; }
  .tuff-visor-conteudo canvas { display: block; }

  /* Sem transição durante o gesto: interpolar enquanto o dedo se move produz um
     atraso constante entre o ponteiro e a imagem, que se lê como travamento. A
     transição só entra nos saltos (duplo-clique, "ajustar à janela"). */
  .tuff-visor--suave .tuff-visor-conteudo {
    transition: transform var(--ds-dur) var(--ds-ease-out);
  }
}
