/* App do lojista. Carrega DEPOIS de /style.css e reusa os tokens de cor dele — é o
   mesmo produto, e paleta duplicada divergiria no primeiro ajuste de marca.

   O QUE GUIA ESTE LAYOUT: o garagista não usa isto sentado numa mesa. Ele usa no
   pátio, no celular, com uma mão, olhando o carro. Então: alvos grandes, foto antes
   de texto, e a informação que ele procura ("está no ar?", "falta conferir algo?")
   visível sem abrir nada. */

:root {
  --raio: 12px;
  --raio-g: 16px;
  --atencao: #f0b354;
  --atencao-bg: #2a2115;
  /* ⚠️ O QUE FALTAVA NA CAIXA DE ATENÇÃO ERA A BORDA, e não a cor nem o texto. Medido:
     o texto dentro dela dá 13,6:1 sobre o próprio fundo (AAA folgado), mas o FUNDO dá
     1,02:1 contra o cartão — ou seja, o bloco não se distingue do que está atrás dele,
     e quem desenha o limite é a borda sozinha. Em #5c4626 ela ficava em 1,74:1, abaixo
     do piso de 3:1 que a WCAG pede para elemento de FORMA (não-texto). O aviso existia
     e não parecia uma caixa.
     #9a7838 dá 3,8:1 contra o cartão e contra o próprio fundo. A cor QUENTE fica: aqui
     não é erro do lojista, é trabalho que ainda falta — vermelho diria a coisa errada
     sobre uma ficha que só está incompleta. */
  --atencao-linha: #9a7838;
}

/* ⚠️ A CALHA DA BARRA DE ROLAGEM FICA RESERVADA SEMPRE, e isto é metade da correção
   do "salto" entre passos. Medido no harness a 1440x1100: o documento do assistente
   ia de 1002px (passo Fotos) a 1777px (passo Ficha). Ou seja, os passos 1, 2 e 5 eram
   os ÚNICOS que passavam da altura da janela — então a barra de rolagem NASCIA ao
   entrar neles e MORRIA ao sair, e a página inteira, que é centrada, escorregava uns
   15px para o lado a cada troca de passo. O relato foi "a tela toda dá um salto", e a
   metade horizontal do salto era esta.
   Só vale para o app: este arquivo não é carregado pela vitrine pública. */
html { scrollbar-gutter: stable; }

/* ⚠️ O CINZA APAGADO SOBE DE LUMINÂNCIA DENTRO DO APP, e o motivo não é contraste de
   norma: `#9aa8bd` sobre o cartão dá 6,42:1, acima do piso AA de 4,5:1. O relato foi
   "fica forçando a visão", e forçar a visão com 6,4:1 é o que acontece quando o texto
   é PEQUENO, dessaturado e repetido — 49 itens de veículo, rótulo de campo, legenda de
   card. Norma cobre legibilidade mínima de um trecho; não cobre varredura de lista
   longa no pátio, com o celular no sol.
   `#b6c2d4` leva a 8,6:1 e continua nitidamente abaixo do texto principal (13,3:1),
   que é o que mantém os dois níveis distinguíveis.

   Escopado em `.app-body`: `--txt-2` mora em `/style.css`, que a VITRINE PÚBLICA também
   carrega. Mexer no token lá dentro mudaria a página do anúncio, que ninguém pediu para
   mudar e que tem outro fundo. */
.app-body { --txt-2: #b6c2d4; }

.app-body { display: flex; flex-direction: column; min-height: 100vh; }
.app-main { flex: 1; width: min(1120px, 92vw); margin: 0 auto; padding: 32px 0 80px; }
.hidden { display: none !important; }

/* ====================================================== cabeçalho de página */
.topo-pagina { display: flex; gap: 20px; align-items: end; justify-content: space-between;
               flex-wrap: wrap; margin: 0 0 24px; }
.topo-pagina h1 { margin: 0 0 4px; font-size: clamp(24px, 3.4vw, 32px);
                  letter-spacing: -.025em; }
.topo-pagina .muted { margin: 0; font-size: 14px; }
.topo-acoes-pagina { display: flex; gap: 10px; flex-wrap: wrap; }
.topo-acoes-pagina .btn, .topo-acoes-pagina .btn-ghost { border: 0; cursor: pointer; font: inherit; }
.topo-acoes-pagina .btn-ghost { border: 1px solid var(--linha); }

/* ================================================================== resumo */
/* Três números, não um dashboard. O que o lojista precisa saber ao abrir é quantos
   tem, quantos estão rendendo, e se algo o espera.

   ⚠️ E AGORA ELES FILTRAM. Eram `<div>` inertes, e a barra lateral repetia a MESMA
   contagem em botões miúdos — dois lugares para o mesmo número, e o bonito era o que
   não fazia nada. Quem batia o olho em "2 a conferir" e queria ver aqueles dois carros
   não tinha onde tocar. São `<button>` com `aria-pressed`, e o estado marcado é a única
   fonte de verdade sobre qual recorte está na tela. */
.resumo { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 20px; }
.pil { display: flex; align-items: baseline; gap: 7px; padding: 10px 16px; cursor: pointer;
       border-radius: 999px; font: inherit; text-align: left;
       border: 1px solid var(--linha); background: var(--bg-2); color: var(--txt);
       transition: border-color .12s, background .12s; }
.pil:hover { border-color: var(--acento); }
.pil b { font-size: 18px; letter-spacing: -.02em; }
.pil span { font-size: 13px; color: var(--txt-2); }
/* Marcada = fundo de acento e borda cheia. É o mesmo par de sinais do `.filtro-op.ativo`
   da barra lateral, para as duas famílias de controle se reconhecerem como controle. */
.pil.ativa { border-color: var(--acento); background: rgba(55, 211, 155, .12); }
.pil.ativa span { color: var(--txt); }
.pil.alerta { border-color: var(--atencao-linha); background: var(--atencao-bg); }
.pil.alerta b, .pil.alerta span { color: var(--atencao); }
.pil.alerta:hover { border-color: var(--atencao); }
.pil.alerta.ativa { background: #3a2d1c; border-color: var(--atencao); }

/* ========================================================= card do veículo */
.grade-veiculos { display: grid; gap: 18px;
                  grid-template-columns: repeat(auto-fill, minmax(268px, 1fr)); }

.cv { display: flex; flex-direction: column; border-radius: var(--raio-g); overflow: hidden;
      border: 1px solid var(--linha); background: var(--bg-2); box-shadow: var(--sup);
      transition: border-color .15s, transform .15s; }
.cv:hover { border-color: #2e3c55; transform: translateY(-2px); }
.cv.vendido { opacity: .72; }

/* ⚠️ `flex: none` NÃO É DECORAÇÃO. Sem ele a capa é um item flexível de altura
   indefinida, e o `aspect-ratio` deixa de mandar quando a fileira do grid estica os
   cards para a altura do mais alto: medido no navegador, dois cards VIZINHOS da mesma
   fileira mostravam a foto com 202px e 359px de altura. A tela lê como torta sem que
   ninguém consiga apontar onde — e é sempre o card com menos selos que estufa.
   Quem absorve a sobra da fileira continua sendo o `.cv-corpo` (`flex: 1`), que é o
   que mantém a linha de botões alinhada no pé de todos os cards. */
.cv-capa { position: relative; flex: none; aspect-ratio: 4/3; background: var(--bg); }
/* ⚠️ FORA DO FLUXO, e isto NÃO é preferência de escrita. Com a foto no fluxo, a altura
   da capa saía do tamanho INTRÍNSECO dela e não do `aspect-ratio`: quem sobe foto em
   pé — que é o que a câmera do celular produz, e este produto vive de vídeo filmado em
   pé — ganhava um card com a foto 78% mais alta que a do vizinho. Medido: 359px contra
   202px na mesma fileira, com a mesma largura de 269px.
   Com `position: absolute` a imagem deixa de ter voz na altura do pai, e aí o
   `aspect-ratio` volta a mandar sozinho. O `flex: none` acima é a outra metade: sem
   ele, a fileira do grid ainda estica a capa do card mais curto. */
.cv-capa img, .cv-capa video { position: absolute; inset: 0;
                               width: 100%; height: 100%; object-fit: cover; display: block; }
.cv-sem-foto { display: grid; place-items: center; height: 100%;
               color: var(--txt-2); font-size: 13px; }
/* Capa que é um QUADRO DO VÍDEO, e não a foto escolhida: escurece um tom. O lojista
   precisa saber, sem clicar, que aquele carro ainda não tem foto no anúncio — a capa é
   o que vai para o WhatsApp, e um frame de vídeo passando por foto escolhida esconde
   trabalho que falta. Ver `previaDoItem` no app.js. */
.cv-capa.so-video video { opacity: .72; }
.cv-capa.so-video::after { content: "quadro do vídeo"; position: absolute; left: 10px;
                           top: 50%; right: 10px; transform: translateY(-50%);
                           text-align: center; font-size: 11px; letter-spacing: .04em;
                           text-transform: uppercase; color: #c3cddd;
                           text-shadow: 0 1px 6px rgba(0,0,0,.9); pointer-events: none; }

.cv-status { position: absolute; top: 10px; left: 10px; padding: 4px 10px;
             border-radius: 999px; font-size: 11px; font-weight: 700;
             letter-spacing: .03em; text-transform: uppercase;
             background: rgba(5,8,14,.78); color: #c3cddd; backdrop-filter: blur(6px); }
.cv-status.ativo { background: var(--acento); color: #05130d; }
.cv-status.vendido { background: #3a4152; color: #dfe6f2; }

.cv-selos { position: absolute; bottom: 10px; left: 10px; display: flex; gap: 5px;
            flex-wrap: wrap; }
.cv-selo { padding: 3px 8px; border-radius: 6px; font-size: 11px;
           background: rgba(5,8,14,.78); color: #c3cddd; backdrop-filter: blur(6px); }
/* O único selo que é PRODUTO e não insumo: o vídeo pronto para mandar no WhatsApp. Os
   outros contam o que existe guardado; este conta o que dá para usar hoje. */
.cv-selo.pronto { background: rgba(55, 211, 155, .92); color: #04140e; font-weight: 600; }

.cv-corpo { padding: 15px 16px 12px; flex: 1; }
.cv-corpo h3 { margin: 0 0 7px; font-size: 16px; line-height: 1.3; letter-spacing: -.01em; }
.cv-linha { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
            margin: 0; font-size: 13px; }
.cv-preco { font-size: 17px; color: var(--acento); letter-spacing: -.01em; }
.cv-nota { margin: 8px 0 0; font-size: 12px; }
/* A única cor de atenção do card: é a única coisa que EXIGE ação antes de o anúncio
   poder ir ao ar. */
.cv-aviso { margin: 9px 0 0; padding: 6px 10px; border-radius: 8px; font-size: 12px;
            background: var(--atencao-bg); border: 1px solid var(--atencao-linha);
            color: var(--atencao); }

.cv-acoes { display: flex; gap: 8px; padding: 0 16px 15px; align-items: center;
            flex-wrap: wrap; }
.cv-acoes .btn, .cv-acoes .btn-ghost { border: 0; cursor: pointer; font: inherit;
                                       text-decoration: none; }
.cv-acoes .btn-ghost { border: 1px solid var(--linha); }

/* Ações de ÍCONE, na ponta direita: QR e lixeira. Com "QR" em botão de rótulo, as
   quatro ações não cabiam nos 268px do card e quebravam em duas linhas — e a segunda
   linha juntava "QR" (inofensivo) com a lixeira (definitiva) na mesma altura, que é
   como o polegar erra. Mesma silhueta quadrada, cores diferentes: a lixeira é a única
   que fica vermelha ao encostar. */
.cv-icone { display: grid; place-items: center; height: 36px; min-width: 36px; padding: 0 9px;
            flex: none; border-radius: 9px; cursor: pointer; font: inherit; font-size: 12px;
            font-weight: 600; letter-spacing: .04em; border: 1px solid var(--linha);
            background: none; color: var(--txt-2); }
.cv-icone:hover { border-color: var(--acento); color: var(--txt); }

/* ------------------------------------------- convite no fim da grade */
/* Com um veículo só, a grade deixava um card de 268px sozinho ao lado de mais de mil
   pixels de nada — e vazio de layout parece defeito, não espaço livre. Aqui o vazio
   passa a dizer o que fazer com ele. Tracejado e sem fundo porque NÃO é mercadoria:
   os cards de verdade são caixas cheias e elevadas; este é um contorno. */
.cv-novo { display: flex; flex-direction: column; align-items: center; justify-content: center;
           gap: 6px; min-height: 220px; padding: 24px 20px; cursor: pointer; font: inherit;
           text-align: center; border-radius: var(--raio-g); color: var(--txt-2);
           border: 1px dashed var(--linha); background: none;
           transition: border-color .15s, color .15s, background .15s; }
.cv-novo:hover { border-color: var(--acento); color: var(--txt);
                 background: rgba(55, 211, 155, .05); }
.cv-novo b { font-size: 15px; color: var(--txt); }
.cv-novo .muted { font-size: 12.5px; line-height: 1.45; max-width: 24ch; }
.cv-novo-mais { display: grid; place-items: center; width: 40px; height: 40px;
                border-radius: 50%; font-size: 22px; line-height: 1;
                background: var(--linha); color: var(--txt-2); }
.cv-novo:hover .cv-novo-mais { background: var(--acento); color: #04140e; }

.vazio { text-align: center; padding: 54px 28px; }
.vazio h2 { margin: 0 0 8px; font-size: 21px; }
.vazio p { max-width: 46ch; margin: 0 auto 22px; }
.vazio .btn { border: 0; cursor: pointer; font: inherit; }

/* ============================================= confirmação da ficha pela IA */
/* O bloco mais importante do app. Fica no TOPO da ficha, antes de vídeo e fotos,
   porque é o que bloqueia a publicação — e porque a promessa dos Termos ("nenhum
   anúncio é publicado sem que você revise cada campo") só é verdade se estiver
   difícil de ignorar. */
/* ⚠️ O PAINEL NÃO É PINTADO DE ÂMBAR — ELE É MARCADO. A versão anterior enchia a caixa
   inteira com `--atencao-bg` e, com sete linhas dentro, o resultado era um campo marrom
   ocupando a tela toda: o tom de atenção deixava de destacar e passava a ser o próprio
   fundo, competindo com o valor de cada campo, que é o que se veio ler.

   É o mesmo defeito que o botão "Aplicar" da vitrine tinha — tinta usada como
   SUPERFÍCIE em vez de MARCA. A regra que fica: cor de estado marca a borda, o título e
   a contagem; o fundo continua sendo o da superfície normal.

   O filete de 3px na lateral é o que mantém a promessa dos Termos ("nenhum anúncio é
   publicado sem que você revise cada campo") difícil de ignorar — ele é impossível de
   confundir com um card comum, e não cansa a leitura das sete linhas. `inset` no
   `box-shadow` e não `border-left`: assim ele acompanha o raio da caixa em vez de
   cortar o canto. */
.conf { border-radius: var(--raio-g); padding: 20px; margin: 0 0 26px;
        border: 1px solid var(--linha); background: var(--bg-2); box-shadow: var(--sup); }
.conf.pendente { border-color: var(--atencao-linha);
                 box-shadow: var(--sup), inset 3px 0 0 var(--atencao); }
.conf.ok { border-color: #1f5340; }
.conf-topo { display: flex; gap: 16px; align-items: start; justify-content: space-between;
             flex-wrap: wrap; }
.conf-topo h2 { margin: 0 0 5px; font-size: 18px; letter-spacing: -.01em; }
.conf.pendente .conf-topo h2 { color: var(--atencao); }
.conf.ok .conf-topo h2 { color: #8ce3c0; }
.conf-topo .muted { margin: 0; font-size: 14px; max-width: 62ch; }
.conf-topo .btn { border: 0; cursor: pointer; font: inherit; white-space: nowrap; }

.conf-lista { list-style: none; margin: 18px 0 0; padding: 0; display: grid; gap: 8px; }
.conf-lista li { display: flex; gap: 14px; align-items: center; justify-content: space-between;
                 flex-wrap: wrap; padding: 12px 14px; border-radius: var(--raio);
                 background: var(--bg); border: 1px solid var(--linha); }
.conf-campo { display: grid; gap: 2px; min-width: 0; }
.conf-campo .muted { font-size: 12px; text-transform: uppercase; letter-spacing: .04em; }
.conf-campo b { font-size: 15px; word-break: break-word; }
.conf-acoes { display: flex; gap: 7px; flex: none; }
.conf-acoes .btn, .conf-acoes .btn-ghost { border: 0; cursor: pointer; font: inherit; }
.conf-acoes .btn-ghost { border: 1px solid var(--linha); }
.conf-acoes button:disabled { opacity: .5; cursor: default; }

.conf-aprovados { margin: 16px 0 0; font-size: 14px; }
.conf-aprovados summary { cursor: pointer; color: var(--txt-2); }
.conf-aprovados ul { list-style: none; margin: 12px 0 0; padding: 0; display: grid; gap: 6px; }
.conf-aprovados li { display: flex; gap: 10px; justify-content: space-between;
                     padding: 9px 12px; border-radius: 9px; background: var(--bg); }
.conf-aprovados .muted { font-size: 13px; }

/* ================================================================ cartões */
.card { background: var(--bg-2); border: 1px solid var(--linha);
        border-radius: var(--raio-g); padding: 24px; }
.app-main > .card { width: 100%; }
.estreito { width: min(480px, 100%); margin: 24px auto 0; }
.estreito h1 { margin: 0 0 8px; font-size: clamp(22px, 3.2vw, 27px); letter-spacing: -.02em; }
.estreito > .muted { margin: 0 0 22px; }

/* ============================================================= formulários */
.lbl { display: block; margin: 0 0 6px; font-size: 13px; color: var(--txt-2); }
.estreito .lbl { margin-top: 16px; }

input[type="email"], input[type="password"], input[type="text"],
input[type="tel"], input[type="number"], select, textarea {
  width: 100%; padding: 12px 14px; border-radius: var(--raio);
  border: 1px solid var(--linha); background: var(--bg);
  color: var(--txt); font: inherit; font-size: 15px;
}
input:focus-visible, select:focus-visible, textarea:focus-visible {
  outline: 2px solid var(--acento); outline-offset: 1px; border-color: var(--acento);
}
input:disabled { opacity: .55; }
textarea { resize: vertical; }

/* Alvo do "Corrigir" da confirmação: pisca uma vez para o olho achar o campo depois
   da rolagem. Sem isso, o foco muda e nada indica onde ele foi. */
.piscar { animation: piscar 1.6s ease-out; }
@keyframes piscar {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  15%, 55% { box-shadow: 0 0 0 3px rgba(240,179,84,.45); }
}

/* Prefixo/sufixo grudado no campo (R$ … km). O símbolo dentro do input tira a dúvida
   de "escrevo o R$?", e o "km" evita ler 48.000 como preço. */
.campo-com-prefixo { display: flex; align-items: stretch; border-radius: var(--raio);
                     border: 1px solid var(--linha); background: var(--bg); overflow: hidden; }
.campo-com-prefixo:focus-within { outline: 2px solid var(--acento); outline-offset: 1px;
                                  border-color: var(--acento); }
.campo-com-prefixo span { display: grid; place-items: center; padding: 0 13px;
                          font-size: 14px; color: var(--txt-2); background: var(--bg-2); }
.campo-com-prefixo span:first-child { border-right: 1px solid var(--linha); }
.campo-com-prefixo .sufixo { border-left: 1px solid var(--linha); border-right: 0; }
.campo-com-prefixo input { border: 0; border-radius: 0; background: transparent; min-width: 0; }
.campo-com-prefixo input:focus-visible { outline: none; }

.campo-link { display: flex; align-items: stretch; border-radius: var(--raio);
              border: 1px solid var(--linha); background: var(--bg); overflow: hidden; }
.campo-link:focus-within { outline: 2px solid var(--acento); outline-offset: 1px; }
.campo-link span { display: grid; place-items: center; padding: 0 6px 0 13px;
                   color: var(--txt-2); font-size: 14px; white-space: nowrap;
                   border-right: 1px solid var(--linha); background: var(--bg-2); }
.campo-link input { border: 0; border-radius: 0; background: transparent; flex: 1; min-width: 0; }
.campo-link input:focus-visible { outline: none; }

form .btn { width: 100%; margin-top: 22px; border: 0; cursor: pointer; font: inherit; }
form .btn:disabled { opacity: .6; cursor: default; }
.cf-turnstile { margin-top: 18px; }
.alternar { margin: 18px 0 0; font-size: 14px; }

.aceite { display: flex; gap: 10px; align-items: start; margin: 18px 0 0;
          font-size: 14px; color: var(--txt-2); cursor: pointer; }
.aceite input { width: 18px; height: 18px; margin: 2px 0 0; flex: none;
                accent-color: var(--acento); cursor: pointer; }
.aceite a { text-decoration: underline; }

/* Duas colunas a partir de 620px. `.largo` atravessa — opcionais e observações são
   texto corrido e ficariam apertados em metade da largura. */
/* `min-width: 0` não é detalhe: item de grid tem largura mínima automática igual ao
   conteúdo, e um valor longo (lista de opcionais) estouraria a coluna em vez de
   quebrar. É a causa clássica de layout de grid "vazando" para fora da tela. */
.field { min-width: 0; }
/* Realce de quem chegou aqui pelo aviso de campo faltando. O foco do navegador não
   basta: em `<select>` ele é um contorno fino, e em campo dentro de combobox some
   contra a borda. O pulso dura o suficiente para o olho achar e some sozinho, para
   não virar estado permanente numa ficha que o lojista vai percorrer inteira. */
.campo-alvo { animation: pulso-campo 1.6s ease-out; border-radius: var(--raio); }
@keyframes pulso-campo {
  0%, 55% { box-shadow: 0 0 0 3px rgba(55, 211, 155, .55); }
  100%    { box-shadow: 0 0 0 3px rgba(55, 211, 155, 0); }
}
/* Quem não quer movimento não recebe movimento — o realce vira estático e some. */
@media (prefers-reduced-motion: reduce) {
  .campo-alvo { animation: none; box-shadow: 0 0 0 3px rgba(55, 211, 155, .55); }
}

.ficha-grid { display: grid; gap: 16px 20px; grid-template-columns: 1fr; margin-top: 20px; }
@media (min-width: 620px) {
  .ficha-grid { grid-template-columns: 1fr 1fr; }
  .ficha-grid .largo { grid-column: 1 / -1; }
}

.selo-ia { display: inline-block; margin-left: 6px; padding: 1px 6px; border-radius: 999px;
           font-size: 10px; font-weight: 700; letter-spacing: .04em;
           background: #2b3a52; color: #9fc4ff; cursor: help; vertical-align: middle; }

/* ================================================================== seções */
.tpl-sec { margin: 30px 0 8px; font-size: 12px; letter-spacing: .08em;
           text-transform: uppercase; color: var(--txt-2);
           padding-top: 22px; border-top: 1px solid var(--linha); }
.acoes { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 20px; }
/* ⚠️ DENTRO DO ASSISTENTE AS AÇÕES ALINHAM À DIREITA, na mesma coluna do "avançar" da
   régua de baixo. Antes, "Gerar com IA", "Salvar ficha" e "Gerar o vídeo" nasciam à
   esquerda enquanto o avanço ficava à direita: cada passo tinha o botão principal num
   canto diferente, e o relato foi literal — "às vezes fica do lado esquerdo, às vezes
   do direito". Uma coluna só é o que faz o polegar saber para onde ir sem procurar.
   Fora do assistente (loja, lista) o alinhamento à esquerda continua, porque lá as
   ações abrem a leitura em vez de fechá-la. */
.card.assistente .acoes { justify-content: flex-end; }
.acoes .btn, .acoes .btn-ghost { border: 0; cursor: pointer; font: inherit; }
.acoes .btn-ghost { border: 1px solid var(--linha); }
.acoes button:disabled { opacity: .55; cursor: default; }
label.btn-ghost { cursor: pointer; }

/* ================================================================== opções */
.opcoes { display: grid; gap: 12px; }
.opcao { display: grid; gap: 4px; justify-items: start; text-align: left; padding: 18px;
         border-radius: var(--raio); cursor: pointer; font: inherit;
         border: 1px solid var(--linha); background: var(--bg); color: var(--txt);
         transition: border-color .15s, background .15s; }
.opcao:hover:not(:disabled) { border-color: var(--acento); background: #172031; }
.opcao:disabled { opacity: .5; cursor: default; }
.opcao b { font-size: 16px; }
.opcao .muted { font-size: 14px; }

/* =================================================================== fotos */
.fotos { display: grid; gap: 10px; margin: 4px 0 16px;
         grid-template-columns: repeat(auto-fill, minmax(118px, 1fr)); }
.foto { position: relative; margin: 0; border-radius: var(--raio); overflow: hidden;
        border: 1px solid var(--linha); background: var(--bg); aspect-ratio: 4/3; }
.foto img { width: 100%; height: 100%; object-fit: cover; display: block; }
.foto.capa { border-color: var(--acento); }
.foto figcaption { position: absolute; left: 6px; bottom: 6px; padding: 2px 8px;
                   border-radius: 999px; font-size: 11px; font-weight: 600;
                   background: var(--acento); color: #05130d; }
.foto-acoes { position: absolute; top: 6px; right: 6px; display: flex; gap: 4px; }
.foto-acoes button { width: 27px; height: 27px; border-radius: 8px; cursor: pointer;
                     border: 0; font-size: 13px; line-height: 1;
                     background: rgba(5,8,14,.75); color: #fff; }
.foto-acoes button:hover { background: rgba(5,8,14,.95); }

/* ======================================================= textos do anúncio */
.tabs-canal { display: flex; gap: 6px; flex-wrap: wrap; margin: 0 0 12px; }
.tab-canal { padding: 7px 13px; border-radius: 999px; cursor: pointer; font: inherit;
             font-size: 13px; border: 1px solid var(--linha);
             background: var(--bg); color: var(--txt-2); }
.tab-canal:hover { border-color: var(--acento); }
.tab-canal.ativa { background: var(--acento); border-color: var(--acento);
                   color: #05130d; font-weight: 600; }
.texto-canal { display: grid; gap: 10px; justify-items: start; }
/* `readonly` e não `disabled`: o texto continua selecionável, e é isso que salva o
   caso em que a área de transferência é negada pelo navegador. */
.texto-canal textarea { line-height: 1.55; font-size: 14px; }
.texto-canal .btn-ghost { border: 1px solid var(--linha); cursor: pointer; font: inherit; }

/* ================================================================== avisos */
.msg { margin: 16px 0 0; padding: 11px 14px; border-radius: var(--raio); font-size: 14px; }
.msg.err { background: #2a1417; border: 1px solid #5c2731; color: #ffb3bd; }
.msg.ok  { background: #10241c; border: 1px solid #1f5340; color: #8ce3c0; }
.msg-foto { margin: 10px 0 0; font-size: 13px; min-height: 1em; }

.analise { margin: 0 0 12px; padding: 12px 15px; border-radius: var(--raio); font-size: 14px;
           border: 1px solid var(--linha); background: var(--bg); }
.analise .muted { display: block; font-size: 13px; margin-top: 4px; }
.analise.processando { border-color: #3a4a6b; color: #9fc4ff; }
.analise.pronta { border-color: #1f5340; color: #8ce3c0; }

/* ⚠️ AVISO QUE DEMORA PRECISA SE MEXER. "Escrevendo os textos…" ficava parado de 10 a
   20 segundos, e caixa parada é indistinguível de aviso que já terminou — o lojista
   olha, não vê nada acontecer e toca no botão de novo.

   Não há progresso CONTÁVEL aqui: é uma chamada de LLM, e barra com porcentagem
   inventada por tempo estimado é o que este produto recusa em todo lugar. Então o sinal
   é movimento puro — anel girando e uma listra atravessando a caixa, a mesma linguagem
   da barra das peças, para quem já viu a IA rodar no passo 1 reconhecer a forma. */
.analise.trabalhando { position: relative; overflow: hidden;
                       display: flex; align-items: center; gap: 10px; }
.analise.trabalhando::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(90deg, transparent, rgba(159, 196, 255, .16), transparent);
  animation: ia-varrer 1.8s linear infinite;
}
.girando { flex: none; width: 15px; height: 15px; border-radius: 50%;
           border: 2px solid rgba(159, 196, 255, .28); border-top-color: #9fc4ff;
           animation: girar .9s linear infinite; }
@keyframes girar { to { transform: rotate(360deg); } }
.analise.erro { border-color: #5c2731; color: #ffb3bd; }

.endereco-publico { margin: 0 0 6px; padding: 12px 14px; border-radius: var(--raio);
                    background: var(--bg); border: 1px solid var(--linha); font-size: 14px; }
.endereco-publico a { font-weight: 600; }

.lista-topo { display: flex; gap: 16px; align-items: start; justify-content: space-between;
              flex-wrap: wrap; margin-bottom: 20px; }
.lista-topo h1 { margin: 0 0 4px; }
.lista-topo .btn, .lista-topo .btn-ghost { border: 0; cursor: pointer; font: inherit;
                                           white-space: nowrap; }
.lista-topo .btn-ghost { border: 1px solid var(--linha); }

.rodape-nota { margin: 34px 0 0; font-size: 13px; text-align: center; }
.diag-saida { margin: 12px 0 0; padding: 14px; border-radius: var(--raio); max-height: 46vh;
              overflow: auto; background: var(--bg-2); border: 1px solid var(--linha);
              color: var(--txt-2); font-size: 12px; line-height: 1.5;
              font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
              white-space: pre-wrap; word-break: break-word; }

/* ==================================================== IA em destaque (passo 1) */
/* O vídeo é o PRIMEIRO passo porque é o caminho que o produto quer que o lojista
   tome. Antes de o assistente existir, este bloco vivia no meio de uma página com
   sete seções empilhadas, e o lojista digitava 13 campos à mão sem saber que não
   precisava — o valor inteiro do produto ficava invisível. Hoje a ordem já resolve
   isso; o tratamento visual continua porque o passo 1 é também a promessa da marca.
   (Era `.ia-hero`, que embrulhava a seção toda; agora o painel é quem embrulha.) */
.passo-painel[data-passo="video"] .ia-cab {
  padding: 18px; border-radius: var(--raio-g); border: 1px solid #1d4b3a;
  background: linear-gradient(180deg, rgba(55,211,155,.07), rgba(55,211,155,.02));
}

.ia-cab { display: flex; gap: 14px; align-items: start; }
.ia-cab h2 { margin: 0 0 5px; font-size: clamp(18px, 2.3vw, 22px); letter-spacing: -.02em; }
.ia-cab .muted { margin: 0; font-size: 14px; line-height: 1.55; }
/* O recolhido do passo 1, depois que a IA já rodou. O `<summary>` fica com a cor de
   texto secundário e NÃO com a do acento: ele divide o cartão com o "Gerar com IA",
   e verde ali seria um segundo destaque disputando o dedo — a regra de um botão cheio
   por painel vale para a cor, não só para a classe. O alvo tem 44px de altura porque
   é dedo em celular. */
.ia-mais > summary { cursor: pointer; color: var(--txt-2); font-size: 14px;
                     padding: 6px 0; list-style-position: inside; }
.ia-mais[open] > summary { margin-bottom: 6px; }

.ia-eti { flex: none; display: grid; place-items: center; width: 38px; height: 38px;
          border-radius: 11px; font-size: 12px; font-weight: 800; letter-spacing: .04em;
          color: #04140e; background: var(--acento); }

/* linha do vídeo */
.ia-video { display: flex; gap: 14px; align-items: center; justify-content: space-between;
            flex-wrap: wrap; margin: 18px 0 0; padding: 14px 16px; border-radius: var(--raio);
            border: 1px dashed #2e4a5f; background: rgba(5,8,14,.35); }
.ia-video.tem-video { border-style: solid; border-color: #1f5340; }
.ia-video-txt { display: grid; gap: 2px; }
.ia-video-txt b { font-size: 15px; }
.ia-video-txt .muted { font-size: 13px; }
.ia-video label { cursor: pointer; }
.ia-video label.btn-ghost { border: 1px solid var(--linha); }

/* opção das fotos */
.ia-opt { display: flex; gap: 16px; align-items: start; justify-content: space-between;
          flex-wrap: wrap; margin: 12px 0 0; padding: 14px 16px; border-radius: var(--raio);
          border: 1px solid var(--linha); background: rgba(5,8,14,.35); }
.ia-chk { display: flex; gap: 11px; align-items: start; cursor: pointer; flex: 1 1 300px; }
.ia-chk input { width: 19px; height: 19px; margin: 1px 0 0; flex: none;
                accent-color: var(--acento); cursor: pointer; }
.ia-chk span { display: grid; gap: 3px; }
.ia-chk b { font-size: 15px; }
.ia-chk em { font-style: normal; font-size: 13px; line-height: 1.5; }

/* Botões e não <select>: são quatro opções, e o dedo acerta um alvo de 44px. Um
   select no celular abre roleta nativa para escolher entre quatro números. */
.ia-qtd { display: flex; gap: 6px; flex: none; }
.ia-qtd button { width: 44px; height: 44px; border-radius: var(--raio); cursor: pointer;
                 font: inherit; font-weight: 700; color: var(--txt-2);
                 border: 1px solid var(--linha); background: var(--bg-2); }
.ia-qtd button:hover { border-color: var(--acento); color: var(--txt); }
/* ⚠️ OPÇÃO MARCADA NÃO É AÇÃO, e o verde cheio dizia que era. A regra do assistente é UM
   botão cheio por painel — o que faz o fluxo andar. Com "Com áudio" (ou o "5" das fotos)
   pintado do mesmo verde de "Gerar o vídeo", o painel tinha dois destaques disputando o
   dedo, e o que o lojista precisa achar de olhos fechados (a ação) perdia para uma opção
   que já vinha escolhida por padrão.

   ⚠️ E O TOM DEIXOU DE SER O ACENTO. Esmaecer o verde não bastava: verde claro ao lado
   de verde cheio continua sendo a mesma família, e no passo Divulgar o "Publicar →"
   também fica verde quando as peças ficam prontas — três verdes no mesmo painel, um
   deles sendo só uma escolha já marcada por padrão. Em azul, a régua do produto fica
   sem exceção: verde é a ação que conclui, azul é o que você ESCOLHEU. É o mesmo azul
   do aviso de "processando", que já é a cor do que está em curso e não do que se faz.

   Continua óbvio qual está marcada — borda e texto claros, fundo levemente tingido —,
   só deixa de competir. Vale para os DOIS seletores (áudio da peça e quantidade de
   fotos): separá-los faria o mesmo gesto ter duas aparências em passos seguidos. */
.ia-qtd button.escolhida {
  background: color-mix(in srgb, #9fc4ff 14%, transparent);
  border-color: #6f93c9; color: #cfe0fb;
}

/* "Conferir a ficha" resolve um BLOQUEIO, e a cor certa para isso é a do aviso que ele
   resolve — a mesma caixa de atenção logo acima. Em verde ele empatava com "Publicar",
   e o verde deste produto passou a significar uma coisa só: a ação que conclui. */
#pecas-conferir, .btn.acao-conferir {
  background: var(--atencao-bg); border: 1px solid var(--atencao-linha);
  color: var(--atencao);
}
#pecas-conferir:hover, .btn.acao-conferir:hover {
  background: color-mix(in srgb, var(--atencao) 18%, var(--atencao-bg));
  color: var(--txt);
}
.ia-qtd.desligada { opacity: .4; pointer-events: none; }

.btn-ia { font-size: 16px; padding: 13px 26px; }

/* etapas */
.ia-etapas { list-style: none; display: grid; gap: 8px; margin: 16px 0 0; padding: 0;
             grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.ia-etapa { display: flex; gap: 10px; align-items: start; padding: 11px 13px;
            border-radius: var(--raio); border: 1px solid var(--linha);
            background: rgba(5,8,14,.4); }
.ia-etapa span:last-child { display: grid; gap: 2px; min-width: 0; }
.ia-etapa b { font-size: 14px; }
.ia-etapa em { font-style: normal; font-size: 12.5px; color: var(--txt-2); line-height: 1.45; }
.ia-ic { flex: none; display: grid; place-items: center; width: 22px; height: 22px;
         border-radius: 50%; font-size: 12px; font-weight: 700;
         border: 1px solid var(--linha); color: var(--txt-2); }
.ia-etapa.rodando { border-color: #3a4a6b; }
.ia-etapa.rodando .ia-ic { border-color: #3a4a6b; color: #9fc4ff;
                           animation: gira 1.4s linear infinite; }
.ia-etapa.pronta { border-color: #1f5340; }
.ia-etapa.pronta .ia-ic { border-color: #1f5340; color: #8ce3c0; }
.ia-etapa.falhou { border-color: #5c2731; }
.ia-etapa.falhou .ia-ic { border-color: #5c2731; color: #ffb3bd; }
@keyframes gira { to { transform: rotate(360deg); } }

/* ============================================ lote de fotos vindo da IA */
/* ⚠️ NEUTRO, E ISTO JÁ FOI AMARELO DE ATENÇÃO. O amarelo dizia "falta você escolher",
   e fazia sentido enquanto o lote era uma antessala: nada entrava no anúncio antes de
   um clique em "Usar as marcadas". Hoje as que cabem JÁ ESTÃO no anúncio (o webhook as
   marca) e o botão não existe mais — pintar de atenção uma etapa que não pede ação
   seria treinar o lojista a ignorar a cor de atenção nas etapas que pedem.
   As ações (`.ia-lote-acoes`) sumiram junto com o botão. */
.ia-lote { margin: 0 0 18px; padding: 16px; border-radius: var(--raio-g);
           border: 1px solid var(--linha); background: var(--bg); }
.ia-lote-cab { display: flex; gap: 14px; align-items: start; justify-content: space-between;
               flex-wrap: wrap; margin: 0 0 14px; }
.ia-lote-cab b { font-size: 15px; }
.ia-lote-cab .muted { margin: 3px 0 0; font-size: 13px; max-width: 52ch; }

.grade-ia { grid-template-columns: repeat(auto-fill, minmax(132px, 1fr)); }
/* A figura inteira é o alvo: isto é usado no pátio, com o polegar, e um quadradinho
   de 27px numa grade de 10 fotos erra mais do que acerta. */
.foto.cand { cursor: pointer; opacity: .62; border-color: transparent;
             transition: opacity .12s, border-color .12s; }
.foto.cand:hover { opacity: .85; }
.foto.cand.marcada { opacity: 1; border-color: var(--acento); }
.cand-marca { position: absolute; top: 6px; right: 6px; display: grid; place-items: center;
              width: 25px; height: 25px; border-radius: 50%; font-size: 13px; font-weight: 700;
              color: #04140e; background: var(--acento); opacity: 0; transform: scale(.6);
              transition: opacity .12s, transform .12s; }
.foto.cand.marcada .cand-marca { opacity: 1; transform: scale(1); }

/* Selo de "a IA está trabalhando" no card do estoque. O lojista fecha a tela do
   veículo e vai cadastrar o próximo — sem isto, ele não tem como saber de qual carro
   a análise ainda está rodando. */
.cv-selo.gerando { border-color: #3a4a6b; color: #9fc4ff; }

/* ===================================================== imagem que não carregou */
/* Ícone de imagem quebrada não diz nada, e o lojista conclui que o sistema perdeu as
   fotos dele. A causa quase sempre é a URL pública do R2 (domínio no bucket errado,
   acesso público desligado), e é isso que a mensagem precisa insinuar. */
.img-quebrada { display: grid; place-items: center; text-align: center; padding: 10px;
                background: repeating-linear-gradient(45deg, #141c2a, #141c2a 8px,
                            #101725 8px, #101725 16px); }
.img-quebrada::after { content: "imagem não carregou — confira o Diagnóstico técnico";
                       font-size: 11px; line-height: 1.4; color: var(--txt-2); }
.foto.img-quebrada { aspect-ratio: 4/3; }

/* ============================================================ peças de vídeo */
/* ⚠️ TRÊS COLUNAS FIXAS, e não `auto-fill`. Os formatos são exatamente três e
   sempre os mesmos — não é uma lista que cresce. Com `auto-fill minmax(210px,1fr)`
   uma tela de 1440px abria QUATRO trilhas e os três cards se encolhiam contra a
   esquerda, deixando um vão do tamanho de um card à direita: a fileira lia como se
   faltasse alguma coisa ali. */
/* Duas colunas em toda largura, porque são duas peças (ver `FORMATOS_PECA` no
   `app.js` — o 1:1 saiu). Cada card ganhou a largura que sobrava do terceiro, e é ganho
   que importa: a moldura do 9:16 é a mais estreita da fileira, e a pergunta que se faz
   olhando para a peça — o card cobriu o carro? o preço saiu certo? — não se responde
   nesse tamanho. Medido: num viewport de 1000px a moldura do 9:16 passou de ~202px
   (três colunas) para 316px. No celular não muda nada — lá já eram duas. */
.pecas-grade { display: grid; gap: 16px; margin: 4px 0 0;
               grid-template-columns: repeat(2, 1fr); }

.peca { display: flex; flex-direction: column; border-radius: var(--raio-g);
        overflow: hidden; border: 1px solid var(--linha); background: var(--bg-2); }
.peca.pronta { border-color: #1f5340; }

/* ⚠️ A CÉLULA TEM ALTURA IGUAL NOS DOIS CARDS, E O FORMATO MORA DENTRO DELA. Antes,
   o `aspect-ratio` do formato estava na própria célula — e como os cards dividem a
   mesma fileira do grid, o resultado em tela foi o relato: "os vídeos em formato de
   escada". O card estica para a altura do mais alto (o 9:16), mas a moldura do 16:9
   acabava a 240px do topo, então o rótulo "16:9 Site e YouTube" ficava lá em cima e o
   do 9:16 lá embaixo, com um vão preto de meia tela entre eles. Rodapés em alturas
   diferentes leem como tela desalinhada, e nada no código dizia por quê.

   Agora a célula é sempre `3/4` e a moldura proporcional vive dentro, centrada. A
   proporção continua VISÍVEL — é o que faz o lojista reconhecer o formato sem ler o
   rótulo —, mas ela deixou de mandar na altura do card. Os dois rodapés alinham.

   ⚠️ E o `3/4` NÃO acompanhou a saída do 1:1, de propósito: é a razão que faz o 9:16
   (a peça mais importante, a do Stories) ocupar a célula quase inteira em altura com o
   16:9 ainda cabendo por largura, e essa conta não mudou com um card a menos. Ver a
   conta no `.peca-quadro`. */
.peca-tela { background: var(--bg-2); display: grid; place-items: center;
             aspect-ratio: 3/4; padding: 12px; }

/* ⚠️ AS MOLDURAS SE DIMENSIONAM PELA LARGURA, E NENHUMA POR `height: 100%`.
   Isto foi medido, não deduzido: com `height: 100%` no 9:16 a moldura saía 116x435 —
   proporção 0,267 contra os 0,563 que ela declara. O motivo é que a altura da célula
   vem de `aspect-ratio`, e altura assim não conta como definida para resolver
   porcentagem: o `100%` vira `auto` e a caixa passa a se dimensionar pelo CONTEÚDO,
   que é um `<video>` sem tamanho intrínseco. Não há erro em lugar nenhum — só o vídeo
   do Stories, que é a peça mais importante da tela, esticado feito coluna.
   E o defeito atravessa a inspeção visual: uma moldura estreita e alta "parece" um
   celular, então bate o olho e passa. Quem pega isto é medir `getBoundingClientRect`.

   Largura resolve sempre, porque a da célula vem da trilha do grid. A conta que faz os
   três caberem: a célula é 3/4 (0,75), então o 9:16 (0,5625) ocupa 0,5625/0,75 = 75%
   da largura e encosta exatamente na altura; os outros dois são mais largos que 3/4 e
   cabem com 100%. `max-height` é o cinto de segurança do teto em `vh` do celular. */
.peca-quadro { position: relative; overflow: hidden; border-radius: 10px;
               background: #000; display: grid; place-items: center;
               max-width: 100%; max-height: 100%; }
.peca-quadro.r9x16 { aspect-ratio: 9/16; width: 75%; }
.peca-quadro.r16x9 { aspect-ratio: 16/9; width: 100%; }
.peca-quadro video { width: 100%; height: 100%; object-fit: contain; display: block;
                     background: #000; }
/* ⚠️ SÍMBOLO NO MEIO DO CARD ENQUANTO MONTA, e não a palavra. A pastilha escrita
   "montando…" era um retângulo parado sobre uma imagem parada — e parado ele lê como
   erro, justamente no estado em que a tela passa a maior parte do tempo. O anel é
   maior que o dos avisos (34px contra 15px) porque aqui ele compete com a prévia do
   carro atrás; abaixo disso ele some no meio da foto. */
.peca-girando { position: relative; z-index: 1; width: 34px; height: 34px;
                border-radius: 50%; border: 3px solid rgba(255, 255, 255, .20);
                border-top-color: var(--acento); animation: girar .9s linear infinite; }
.peca-nada { font-size: 12px; color: var(--txt-2); text-align: center; padding: 0 10px; }

/* ⚠️ O QUADRO NÃO PODE FICAR VAZIO ENQUANTO NÃO HÁ VÍDEO PRONTO, que é o estado em
   que esta tela passa a maior parte do tempo (o render leva minutos). A prévia é a
   capa do veículo ou o próprio vídeo enviado, esmaecido, com o rótulo por cima — o
   lojista vê o carro dele e entende que aquilo é o lugar onde o vídeo vai aparecer.
   Ver `montarPecas` no `app.js`. */
.peca-previa { position: relative; width: 100%; height: 100%;
               display: grid; place-items: center; overflow: hidden; }
.peca-previa img, .peca-previa video { position: absolute; inset: 0; width: 100%;
                                       height: 100%; object-fit: cover; display: block;
                                       opacity: .32; pointer-events: none; }
/* `max-width` + `white-space: nowrap` porque a moldura do 9:16 é a mais ESTREITA das
   três: sem isto "ainda não gerado" quebrava em duas linhas e vazava a pílula pelos
   lados, e o card mais importante da tela era o que ficava torto. */
.peca-previa span { position: relative; z-index: 1; padding: 5px 12px; border-radius: 999px;
                    font-size: 12px; color: var(--txt); background: rgba(5,8,14,.72);
                    white-space: nowrap; max-width: calc(100% - 12px); overflow: hidden;
                    text-overflow: ellipsis; }

/* ⚠️ O CARD PRECISA PARECER QUE ESTÁ TRABALHANDO, e a pílula "montando…" sozinha não
   parecia: um retângulo parado com uma palavra é indistinguível de um retângulo parado
   com um erro. O render leva de 2 a 5 minutos, então este é o estado em que a tela fica
   a maior parte do tempo em que o lojista olha para ela.

   A varredura é a MESMA linguagem da barra de progresso da IA (`ia-varrer`), de
   propósito: o lojista já viu esse movimento no passo 1 querendo dizer "estou
   processando", e reusá-lo aqui não pede aprendizado nenhum.

   Fica no `::after` da prévia, ACIMA da imagem esmaecida e ABAIXO da pílula (que é
   `z-index: 1`), então o texto continua legível por cima do brilho passando. */
.peca-previa.montando::after {
  content: ""; position: absolute; inset: 0; z-index: 0;
  background: linear-gradient(100deg, transparent 35%, rgba(55,211,155,.22) 50%,
                              transparent 65%);
  animation: peca-varrer 1.9s linear infinite;
}
@keyframes peca-varrer {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}
/* A borda também respira, para o card inteiro dizer "em curso" — não só o miolo. */
.peca.montando { border-color: var(--acento-linha, var(--linha));
                 animation: peca-pulsar 1.9s ease-in-out infinite; }
@keyframes peca-pulsar { 50% { border-color: var(--acento); } }

/* Sem movimento a informação continua de pé: a pílula "montando…" é o dado, a varredura
   é só o reforço. Mesma regra da barra de progresso. */
@media (prefers-reduced-motion: reduce) {
  .peca-previa.montando::after, .peca.montando { animation: none; }
  /* O anel parado continua sendo um indicador — o que sai é o giro, não o sinal. */
  .peca-girando, .girando { animation: none; }
  .analise.trabalhando::after { animation: none; background: none; }
}

/* ⚠️ O RODAPÉ ENCOSTA NA CÉLULA E QUEM DESCE É A FILEIRA DE BOTÕES. A tentação é
   `margin-top:auto` aqui — e ela devolve a escada no caso que importa: falha PARCIAL,
   com uma peça pronta e duas não. O card pronto tem uma fileira de botões embaixo, os
   outros dois não; empurrado por `auto`, o rótulo "9:16" subia a altura daquela
   fileira e ficava 100px acima de "1:1" e "16:9".
   Com o rodapé colado na célula (que é de altura igual nos três) os rótulos alinham
   sozinhos, e o `auto` vai para `.peca-acoes`, que alinha os botões no pé do card
   mesmo quando um rodapé tem duas linhas e o vizinho tem uma. */
.peca-pe { display: flex; flex-direction: column; gap: 3px;
           padding: 11px 13px 0; }
.peca-pe-l { display: flex; gap: 8px; align-items: baseline; min-width: 0; }
.peca-pe-l .muted { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.peca-pe b { font-size: 14px; flex: none; }
.peca-pe .muted { font-size: 12px; }
.peca-acoes { display: flex; gap: 8px; flex-wrap: wrap; padding: 10px 13px 13px;
              margin-top: auto; }
.peca-acoes .btn-ghost { border: 1px solid var(--linha); cursor: pointer; font: inherit;
                         text-decoration: none; }

/* Botão de escolha com rótulo por extenso (áudio da peça). Os de quantidade são
   quadrados porque cabem um número; estes precisam da frase inteira — "Com áudio e
   legenda" e "Sem áudio" são a diferença entre duas peças diferentes, e abreviar
   obrigaria o lojista a descobrir na peça pronta. */
.ia-qtd button.largo { width: auto; padding: 0 16px; font-weight: 600; font-size: 14px; }

/* Chamada para a peça, dentro do painel de conferência. Só aparece no instante em que
   o portão abre — antes disso o servidor recusaria, e descobrir isso descendo a
   página e levando um 409 gastaria a paciência do lojista com uma regra que o produto
   já conhecia. */
.conf-emenda { display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
               margin: 12px 0 0; padding: 12px 14px; border-radius: var(--raio);
               border: 1px solid #1f5340; background: rgba(55,211,155,.06);
               font-size: 14px; line-height: 1.5; }
.conf-emenda .btn { border: 0; cursor: pointer; font: inherit; white-space: nowrap; }

/* ================================================================= combobox */
/* Substitui o <datalist>. 33 marcas num datalist abrem uma lista maior que a tela do
   celular, sem busca por digitação em boa parte dos navegadores e sem rolagem decente
   em nenhum. Aqui filtra sem acento enquanto se digita — e o campo continua aceitando
   valor livre, que é o contrato dele. */
.combo { position: relative; display: flex; align-items: stretch;
         border: 1px solid var(--linha); border-radius: var(--raio);
         background: var(--bg-2); min-width: 0; }
.combo:focus-within { outline: 2px solid var(--acento); outline-offset: 1px; }
.combo input { flex: 1; min-width: 0; border: 0; border-radius: 0; background: transparent; }
.combo input:focus-visible { outline: none; }
.combo-seta { flex: none; width: 38px; border: 0; border-left: 1px solid var(--linha);
              background: transparent; color: var(--txt-2); cursor: pointer;
              font-size: 13px; border-radius: 0 var(--raio) var(--raio) 0; }
.combo-seta:hover { color: var(--txt); }

.combo-lista { position: absolute; z-index: 40; top: calc(100% + 5px); left: 0; right: 0;
               max-height: 260px; overflow-y: auto; padding: 5px;
               border: 1px solid var(--linha); border-radius: var(--raio);
               background: #101725; box-shadow: 0 18px 44px rgba(0,0,0,.55); }
.combo-item { display: block; width: 100%; text-align: left; padding: 9px 11px;
              border: 0; border-radius: 8px; background: transparent; color: var(--txt);
              font: inherit; font-size: 14px; cursor: pointer; }
.combo-item:hover, .combo-item.ativo { background: #1b2739; }
.combo-vazio { margin: 0; padding: 10px 11px; font-size: 13px; color: var(--txt-2);
               line-height: 1.5; }

/* =============================================================== chips */
/* Grade de seleção. O texto separado por vírgula exigia que o lojista soubesse
   escrever "Freios ABS com EBD" — ele digitava "abs", e o anúncio saía com um item
   que nenhum comprador procura. Cor cheia = selecionado, sem depender de checkbox
   miúdo que o polegar erra. */
/* ⚠️ A CAIXA É COLUNA E QUEM EMBRULHA É A `.chips-linha`. O `flex-wrap` estava aqui,
   e com os grupos (migration_043) isso poria título e botões na mesma fileira. A
   estrutura é: caixa (coluna) > grupo (coluna) > linha (embrulha) > chips. */
.chips { display: flex; flex-direction: column; gap: 12px;
         padding: 12px; border: 1px solid var(--linha); border-radius: var(--raio);
         background: rgba(5,8,14,.3); }
.chips-linha { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.chips-grupo { display: flex; flex-direction: column; gap: 8px; }
/* Fio entre grupos: com 49 botões, o que separa "Segurança" de "Conforto" não pode
   ser só o espaço em branco — na rolagem do celular os dois viram um bloco só. */
.chips-grupo + .chips-grupo { padding-top: 12px; border-top: 1px solid var(--linha); }
/* O título é uma ETIQUETA, não um cabeçalho: ele orienta a varredura e não pode
   disputar leitura com os próprios itens, que são o conteúdo. */
/* Caixa alta miúda é o pior caso de leitura que existe: sem as ascendentes e
   descendentes, a palavra perde a silhueta e o olho tem de soletrar. Como aqui ela é
   necessária (a etiqueta precisa não competir com os itens), compensa-se com tamanho e
   com a cor cheia — não com o cinza apagado, que somaria dois handicaps. */
.chips-titulo { font-size: 12px; font-weight: 700; letter-spacing: .07em;
                text-transform: uppercase; color: var(--txt); opacity: .82; }
/* 14px e não 13.5: meio ponto parece nada e não é. São 49 botões lidos em varredura,
   e o custo de cada um é pago 49 vezes. */
.chip { padding: 8px 14px; border-radius: 999px; cursor: pointer; font: inherit;
        font-size: 14px; color: var(--txt-2); background: var(--bg-2);
        border: 1px solid var(--linha); transition: background .12s, color .12s,
        border-color .12s; }
.chip:hover { border-color: var(--acento); color: var(--txt); }
.chip.ativa { background: var(--acento); border-color: var(--acento); color: #04140e;
              font-weight: 600; }
/* Os diferenciais são argumento de venda, não item de série: laranja para não
   competirem visualmente com a lista de itens, que é longa e verde. */
.chips.destaque .chip.ativa { background: var(--atencao); border-color: var(--atencao);
                              color: #1a1206; }
/* ⚠️ `flex: none` porque a caixa virou COLUNA: o `flex: 1 1 210px` de antes tratava
   210px como altura-base e esticava a linha do "Outro item…" pela sobra vertical. */
.chip-novo { display: flex; gap: 6px; align-items: center; flex: none; min-width: 170px; }
/* Separado do último grupo pelo mesmo fio: acrescentar item à mão é outra ação, não a
   continuação da lista. */
.chips.agrupada > .chip-novo { padding-top: 12px; border-top: 1px solid var(--linha); }
.chip-novo input { flex: 1; min-width: 0; padding: 7px 11px; font-size: 13.5px; }
.chip-novo button { padding: 7px 13px; border-radius: 999px; cursor: pointer; font: inherit;
                    font-size: 13px; color: var(--txt-2); background: transparent;
                    border: 1px dashed var(--linha); }
.chip-novo button:hover { border-style: solid; border-color: var(--acento); color: var(--txt); }

/* ========================================================= identidade da loja */
.temas { display: flex; flex-wrap: wrap; gap: 10px; margin: 0 0 20px; }
.tema-op { display: flex; align-items: center; gap: 9px; padding: 9px 14px 9px 9px;
           border-radius: 999px; cursor: pointer; font: inherit; font-size: 13.5px;
           color: var(--txt-2); background: var(--bg-2);
           border: 1px solid var(--linha); transition: border-color .12s, color .12s; }
.tema-op:hover { border-color: var(--acento); color: var(--txt); }
.tema-op.escolhido { border-color: var(--acento); color: var(--txt); font-weight: 600; }
/* As bolinhas leem as variáveis do PRÓPRIO tema (o `data-tema` está nelas), então
   nenhuma cor é repetida aqui — vêm todas de /temas.css. */
.tema-bolas { display: flex; }
.tema-bolas i { width: 18px; height: 18px; border-radius: 50%; display: block;
                box-shadow: 0 0 0 1.5px #111826; }
.tema-bolas i + i { margin-left: -6px; }

/* ⚠️ ALTURA IGUAL NOS DOIS SLOTS, e é isso que alinha os botões.
   A versão anterior tinha logo de 96px e capa em `aspect-ratio: 4/1` — numa coluna
   larga a capa passava dos 200px de altura, e como cada fileira de botões segue a sua
   própria caixa, os quatro botões terminavam em quatro linhas diferentes. Fixar a
   MESMA altura resolve na origem, sem precisar empurrar nada com margem. */
.imgs-loja { display: grid; gap: 18px; margin: 0 0 6px;
             grid-template-columns: 132px minmax(240px, 1fr); align-items: start; }
@media (max-width: 620px) { .imgs-loja { grid-template-columns: 1fr; } }
.img-slot { display: grid; gap: 8px; min-width: 0; }
.img-alvo { display: grid; place-items: center; overflow: hidden; background: var(--bg);
            border: 1px dashed var(--linha); color: var(--txt-2); font-size: 12px;
            height: 132px; }
.img-alvo img { width: 100%; height: 100%; object-fit: cover; display: block; }
.img-alvo.redonda { width: 132px; border-radius: 22px; }
.img-alvo.larga { width: 100%; border-radius: var(--raio); }

/* ------------------------------------------------------- ações da imagem */
/* ⚠️ CORES DIFERENTES PORQUE AS AÇÕES SÃO DIFERENTES. Os dois botões eram
   `.btn-ghost` idênticos, lado a lado, e um deles apaga a marca da loja — mesma
   silhueta para "enviar" e "destruir" é como o clique errado acontece. */
.img-acoes { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.bt-enviar, .bt-remover { padding: 9px 16px; border-radius: var(--raio); cursor: pointer;
                          font: inherit; font-size: 13.5px; font-weight: 600;
                          white-space: nowrap; transition: background .12s, border-color .12s; }
.bt-enviar { color: var(--acento); background: rgba(55,211,155,.10);
             border: 1px solid #1f5340; }
.bt-enviar:hover { background: rgba(55,211,155,.18); }
.bt-remover { color: #ffb3bd; background: transparent; border: 1px solid #5c2731; }
.bt-remover:hover { background: #2a1417; }

/* ------------------------------------------------------------------ prévia */
/* Duas molduras porque o catálogo é aberto nos dois, e o que quebra é diferente em
   cada um: no computador a capa fica larga e baixa; no celular ela vira quase quadrada
   e a logo cobre metade dela. Mostrar só um dos dois esconde metade dos problemas. */
.previas { display: flex; gap: 20px; flex-wrap: wrap; margin: 18px 0 0; }
.previa-caixa { display: grid; gap: 7px; }
.previa-rot { font-size: 11px; letter-spacing: .08em; text-transform: uppercase;
              color: var(--txt-2); }
.previa { overflow: hidden; border-radius: 12px; border: 1px solid var(--linha);
          background: var(--bg); color: var(--txt); }
.previa.desktop { width: min(480px, 100%); }
.previa.celular { width: 232px; }

.pv-capa { height: 64px; background: var(--bg-2); }
.pv-capa.vazia { background: repeating-linear-gradient(45deg, var(--bg-2), var(--bg-2) 7px,
                 var(--bg) 7px, var(--bg) 14px); }
.pv-capa img { width: 100%; height: 100%; object-fit: cover; display: block; }
.pv-linha { display: flex; align-items: center; gap: 9px; padding: 0 12px 10px; }
.pv-logo { width: 34px; height: 34px; border-radius: 9px; object-fit: cover;
           margin-top: -14px; border: 2px solid var(--bg); background: var(--bg-2);
           display: block; flex: none; }
.pv-logo.vazia { background: var(--linha); }
.pv-linha b { font-size: 13.5px; overflow: hidden; text-overflow: ellipsis;
              white-space: nowrap; }
.pv-grade { display: grid; gap: 9px; padding: 0 13px 14px;
            grid-template-columns: repeat(3, 1fr); }
.previa.celular .pv-grade { grid-template-columns: repeat(2, 1fr); }
.pv-card { border-radius: 9px; border: 1px solid var(--linha); background: var(--bg-2);
           padding: 6px; display: grid; gap: 5px; }
/* ⚠️ A prévia mostra TEXTO, não barras cinzas. Esqueleto responde "o layout é assim",
   que não é a pergunta — a pergunta do lojista é "a cor do preço vai dar para ler no
   meu catálogo?". Com nome e valor escritos, ele compara os temas olhando exatamente
   o que o comprador vai olhar. */
.pv-foto { position: relative; aspect-ratio: 4/3; border-radius: 6px;
           display: grid; place-items: center; color: var(--txt-2); font-size: 11px;
           background: linear-gradient(145deg, var(--linha), var(--bg)); }
.pv-card b { font-size: 10.5px; line-height: 1.25; color: var(--txt);
             overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.pv-preco { font-size: 12px; font-weight: 700; color: var(--acento); }

/* ============================================== assistente de passos do anúncio */
/* A tela do veículo era uma página só com sete blocos empilhados, e o relato de quem
   usou foi literal: "não sei nem onde clicar". Não faltava instrução — faltava ordem.
   Sete coisas visíveis ao mesmo tempo não têm primeira.

   O desenho aqui tem uma regra que vale por todas: EM CADA PASSO existe UM botão
   cheio (`.btn`), e ele é o que leva adiante. Tudo o mais é `.btn-ghost`. É o que
   permite atravessar o fluxo inteiro sem ler, só seguindo o botão verde. */
.passos { margin: 4px 0 0; }
.passos ol { display: flex; align-items: center; gap: 4px; list-style: none;
             margin: 0; padding: 0; }
/* ⚠️ Os quatro zeros são cinto de segurança, não redundância. `/style.css` também é
   carregado aqui e tinha um `.passos li` com `padding:24px` e borda, da seção
   "1. Grave → 2. Confira" da landing: cada chip virava um cartão de 90px e a tela do
   app rolava de lado no celular, sem erro em lugar nenhum. Lá a regra foi escopada
   para `ol.passos`; isto aqui garante que um nome repetido no futuro não volte a
   sequestrar a barra. */
.passos li { flex: 1 1 0; min-width: 0;
             background: none; border: 0; border-radius: 0; padding: 0; }

/* `min-width: 44px` porque no celular o chip não-ativo é só o número, e 40px fica
   abaixo do alvo de toque de quem usa isto em pé, com uma mão. */
.passo-chip { width: 100%; min-width: 44px; min-height: 44px;
              display: flex; align-items: center; justify-content: center;
              gap: 9px; padding: 10px 8px; border-radius: var(--raio); cursor: pointer;
              font: inherit; font-size: 14px; font-weight: 600; color: var(--txt-2);
              border: 1px solid var(--linha); background: var(--bg);
              transition: border-color .15s, color .15s, background .15s; }
.passo-chip:hover:not(:disabled) { border-color: var(--acento); color: var(--txt); }
.passo-chip .passo-n { flex: none; display: grid; place-items: center;
                       width: 24px; height: 24px; border-radius: 999px; font-size: 13px;
                       font-weight: 700; background: var(--linha); color: var(--txt-2); }
.passo-chip .passo-rot { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.passo-chip.feito { color: var(--txt); border-color: #1f5340; }
.passo-chip.feito .passo-n { background: #1f5340; color: var(--acento); }
.passo-chip.ativo { color: var(--txt); border-color: var(--acento);
                    background: rgba(55, 211, 155, .10); }
.passo-chip.ativo .passo-n { background: var(--acento); color: #04140e; }
/* Travado NÃO é o mesmo que desabilitado por engano: são os passos que precisam de um
   veículo criado para anexar arquivo. O cursor e a opacidade dizem "ainda não", e o
   texto do próprio painel diz por quê — o lojista que clica merece a razão. */
.passo-chip.travado, .passo-chip:disabled { opacity: .45; cursor: not-allowed; }

.passo-conta { margin: 10px 0 0; font-size: 13px; }

/* ⚠️ `!important` porque `.acoes` e outros filhos declaram `display:flex`, e um
   `display` explícito vence o `[hidden]` do agente de usuário. Sem isto, trocar de
   passo não esconderia nada — o sintoma seria a página antiga de volta, inteira. */
.passo-painel[hidden] { display: none !important; }
.passo-painel { margin-top: 22px; }

/* ⚠️ O CARTÃO DO ASSISTENTE TEM PISO DE ALTURA, e é a outra metade da correção do
   "salto". Os seis passos têm conteúdo muito desigual — 298px no Publicar contra
   1274px na Ficha, medido —, e sem piso o documento encolhia abaixo da janela ao sair
   de um passo alto para um baixo. Quando isso acontece o navegador GRAMPEIA o scroll
   de volta ao topo: para quem está olhando, a tela pula sozinha.
   Com o piso, o documento nunca fica menor que a janela e não há o que grampear.

   A classe é só do assistente de propósito. `.app-main > .card` pegaria também o
   cartão do estoque, e lá esticar até o pé da janela deixaria um vazio embaixo de uma
   loja com dois carros. */
/* ⚠️ O `:has()` ESTREITA O ALVO DE PROPÓSITO. `min-height: 100%` no cartão não
   resolveria: `.app-main` tira a altura de `flex: 1`, e porcentagem não se resolve
   contra pai de altura automática. O jeito que funciona é o cartão ser ele mesmo um
   item flexível que cresce — e para isso `.app-main` precisa ser coluna flexível.
   Só que a tela do ESTOQUE mora no mesmo `.app-main` e tem vários filhos diretos:
   virar flex ali mataria o colapso de margens entre eles e mexeria numa tela que
   ninguém pediu para mexer. O `:has()` liga a coluna só quando o assistente está
   dentro. */
.app-main:has(> .card.assistente) { display: flex; flex-direction: column; }
.card.assistente { flex: 1; display: flex; flex-direction: column; }
/* O painel aberto absorve a sobra, e a régua de navegação desce para o pé dela. É o
   que faz "← Textos / Publicar →" cair SEMPRE na mesma altura, seja qual for o passo
   — que é a referência de que o dedo se lembra entre uma troca e outra. */
.card.assistente > .passo-painel:not([hidden]) { display: flex; flex-direction: column;
                                                 flex: 1 0 auto; }
.card.assistente > .passo-painel > .passo-nav { margin-top: auto; }

.passo-h { margin: 0 0 6px; font-size: clamp(19px, 2.6vw, 23px); letter-spacing: -.02em; }
/* Divisória dentro do passo que junta Fotos e Textos. O traço em cima é o que faz as
   duas metades se lerem como duas coisas — sem ele, um painel longo com dois botões
   parece um formulário só e o "Gerar textos" fica órfão no meio dele. A primeira não
   ganha traço nem margem: ela encosta na introdução do passo. */
.passo-h3 { margin: 26px 0 6px; padding-top: 20px; font-size: 16px; letter-spacing: -.01em;
            border-top: 1px solid var(--linha); }
.passo-h3:first-of-type { margin-top: 18px; padding-top: 0; border-top: 0; }
.passo-sub { margin: 0 0 14px; font-size: 14px; line-height: 1.55; }
.passo-nota { margin: 8px 0 0; font-size: 13px; }

/* O que falta para publicar. Cor de atenção e não de erro: não é falha do lojista,
   é trabalho que ainda existe. */
.passo-falta { margin: 0 0 14px; padding: 11px 14px; border-radius: var(--raio);
               font-size: 14px; line-height: 1.5;
               border: 1px solid var(--atencao-linha); background: var(--atencao-bg);
               color: var(--txt); }
.passo-travado { margin: 0; padding: 16px; border-radius: var(--raio); font-size: 14px;
                 border: 1px dashed var(--linha); background: var(--bg);
                 color: var(--txt-2); }
.passo-alt { margin: 14px 0 0; font-size: 14px; }

/* Botão que parece link: usado dentro de frase ("Ir para a ficha"), onde um botão
   cheio quebraria a leitura e um <a href="#"> mexeria na URL. */
.link-btn { border: 0; background: none; padding: 0; font: inherit; font-weight: 600;
            color: var(--acento); cursor: pointer; text-decoration: underline; }

.passo-nav { display: flex; align-items: center; justify-content: space-between;
             gap: 12px; margin: 28px 0 0; padding-top: 20px;
             border-top: 1px solid var(--linha); }
.passo-nav .btn, .passo-nav .btn-ghost { border: 0; cursor: pointer; font: inherit; }
.passo-nav .btn-ghost { border: 1px solid var(--linha); }
/* ⚠️ A REGRA ACIMA ZERA A BORDA, e `.acao-conferir` depende dela para existir. As duas
   têm a MESMA especificidade (0,2,0), e esta vem depois no arquivo — o "Conferir a
   ficha" da régua saía sem contorno enquanto o do painel tinha o seu, dois botões com o
   mesmo texto e aparências diferentes na mesma tela. Medido: 0px contra 0,67px. */
.passo-nav .btn.acao-conferir { border: 1px solid var(--atencao-linha); }
.passo-nav button:disabled { opacity: .5; cursor: default; }

/* Enquanto o vídeo sobe, o rótulo do seletor não pode continuar convidando ao
   clique: o upload já está em curso e um segundo arquivo criaria um segundo veículo. */
.ia-video label.ocupado { opacity: .55; pointer-events: none; }

@media (max-width: 640px) {
  /* ⚠️ DUAS FILEIRAS DE TRÊS, TODAS COM NOME. A versão anterior escondia o rótulo de
     todos os passos menos o aberto, para caber seis chips numa linha de 360px — e o
     resultado em tela foi o relato: "um botão com número e a descrição cortada".
     Cinco números sem palavra nenhuma não dizem o que é o passo 4, e o único que
     tinha nome ainda vinha com reticências.

     Empilhar em grade resolve na origem: número em cima, nome embaixo, nome inteiro,
     alvo de 58px. Custa uma fileira de altura, e devolve a única coisa que a barra
     existia para dar — saber o que vem pela frente sem ter que clicar. */
  .passos ol { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px; }
  .passos li { flex: none; }
  .passo-chip { flex-direction: column; gap: 5px; padding: 9px 4px; min-height: 58px;
                font-size: 11.5px; line-height: 1.2; }
  .passo-chip .passo-n { width: 22px; height: 22px; font-size: 12px; }
  .passo-chip .passo-rot { display: block; white-space: normal; text-align: center;
                           overflow: visible; text-overflow: clip; }
  /* ⚠️ O `.passo-nav` DESTE BLOCO SAIU, e não é limpeza: ele tinha `order: -1` no botão
     cheio, uma sobra da época em que os dois botões dividiam a mesma linha e o avanço
     precisava ser puxado para cima. Com a régua empilhada (bloco do fim do arquivo),
     esse `order` invertia a ordem — "avançar" acima de "voltar", exatamente o contrário
     do que aquele bloco documenta. Duas regras para a mesma barra, e a que perdia era
     a que alguém tinha acabado de escrever. */
}

/* ================================================== filtros do estoque (lojista) */
/* Uma loja com 60 carros não acha nada rolando a grade. E o que o LOJISTA procura não
   é o que o comprador procura — ele filtra por "o que ainda me deve trabalho" e "o que
   está no ar", não por marca e preço. Daí dois conjuntos de filtros diferentes, e não
   uma abstração compartilhada que serviria mal aos dois. */
.com-filtros { display: grid; gap: 20px; align-items: start; }
@media (min-width: 900px) {
  .com-filtros { grid-template-columns: 244px minmax(0, 1fr); gap: 26px; }
  /* Gruda ao rolar: com 60 cards, o filtro sumia da tela no primeiro scroll e voltar
     até ele para trocar de marca era rolar a página inteira de volta. */
  .filtros { position: sticky; top: 84px; max-height: calc(100vh - 104px); overflow-y: auto; }
}
.filtros-conteudo { min-width: 0; }

.filtros-cab { display: flex; align-items: center; justify-content: space-between;
               gap: 12px; margin: 0 0 12px; }
.filtros-cab b { font-size: 15px; display: flex; align-items: center; gap: 8px; }
.filtros-n { display: grid; place-items: center; min-width: 20px; height: 20px;
             padding: 0 6px; border-radius: 999px; font-style: normal; font-size: 11px;
             font-weight: 700; background: var(--acento); color: #04140e; }

.filtros-chips { display: flex; flex-wrap: wrap; gap: 7px; margin: 0 0 16px; }
.filtro-chip { display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
               font: inherit; font-size: 13px; padding: 7px 12px; border-radius: 999px;
               border: 1px solid var(--acento); background: rgba(55, 211, 155, .12);
               color: var(--txt); }
.filtro-chip span[aria-hidden] { color: var(--txt-2); font-size: 15px; line-height: 1; }
.filtro-chip:hover span[aria-hidden] { color: var(--txt); }

.filtro-grupo { padding: 14px 0 0; margin: 0 0 14px; border-top: 1px solid var(--linha); }
.filtro-rot { display: block; margin: 0 0 8px; font-size: 12px; letter-spacing: .07em;
              text-transform: uppercase; color: var(--txt-2); }
.filtro-busca { width: 100%; padding: 11px 13px; border-radius: var(--raio); font: inherit;
                font-size: 15px; border: 1px solid var(--linha); background: var(--bg);
                color: var(--txt); }
.filtro-busca::placeholder { color: var(--txt-2); }

/* Linha inteira clicável, rótulo à esquerda e contagem à direita: é a forma que o
   lojista já conhece de qualquer portal, e o alvo de toque vira a largura da barra. */
.filtro-op { display: flex; align-items: center; justify-content: space-between; gap: 10px;
             width: 100%; min-height: 40px; padding: 9px 11px; margin: 0 0 3px;
             border: 1px solid transparent; border-radius: 9px; cursor: pointer;
             font: inherit; font-size: 14.5px; text-align: left;
             background: none; color: var(--txt-2); }
.filtro-op:hover { background: var(--bg); color: var(--txt); }
.filtro-op.ativo { background: rgba(55, 211, 155, .12); border-color: var(--acento);
                   color: var(--txt); font-weight: 600; }
.filtro-op em { font-style: normal; font-size: 12px; color: var(--txt-2); flex: none; }

.filtros-conta { margin: 0 0 14px; font-size: 14px; }

/* Só para leitor de tela: o "×" do chip é `aria-hidden`, então sem isto o botão de
   remover filtro seria anunciado apenas como o nome do filtro — e quem não vê a tela
   não teria como saber que clicar ali tira, e não aplica. */
.so-leitor { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
             overflow: hidden; clip-path: inset(50%); white-space: nowrap; border: 0; }

/* A zona de risco (`.perigo` / `.btn-perigo`) VIVIA AQUI e saiu junto com o botão de
   excluir do último passo — a exclusão hoje é a lixeira do card, em "Meus veículos".
   Ver o comentário no painel `publicar` do `app.js`. Se um dia voltar a existir uma
   ação sem volta numa tela, ela merece cor e caixa próprias de novo. */

/* O `<summary>` é o botão de abrir no celular. Some no desktop, onde a barra fica
   sempre aberta — um botão "Filtros" ao lado de uma lista já visível confunde. */
.filtros-abrir { display: flex; align-items: center; justify-content: space-between;
                 padding: 13px 15px; cursor: pointer; font-weight: 600; font-size: 15px;
                 list-style: none; border: 1px solid var(--linha); border-radius: var(--raio);
                 background: var(--bg-2); color: var(--txt); }
.filtros-abrir::-webkit-details-marker { display: none; }
.filtros-abrir::after { content: "▾"; color: var(--txt-2); }
.filtros-caixa[open] .filtros-abrir { border-radius: var(--raio) var(--raio) 0 0; }
.filtros-corpo { padding: 14px 15px 16px; border: 1px solid var(--linha); border-top: 0;
                 border-radius: 0 0 var(--raio) var(--raio); background: var(--bg-2); }
.filtros-corpo .filtro-grupo:first-of-type { border-top: 0; padding-top: 0; }

@media (min-width: 900px) {
  .filtros-abrir { display: none; }
  /* ⚠️ AQUI ESTAVA A CAUSA DE "NÃO DÁ PRA DIFERENCIAR O QUE É O QUE". Esta regra zerava
     fundo, borda e raio no desktop: a coluna de filtros ficava flutuando no mesmo preto
     da página, e a ÚNICA caixa desenhada da região inteira era a opção ativa, verde e
     com borda — que então lia como se fosse o card do veículo.

     O painel volta a ter corpo, e com o fundo `--bg` de propósito: o card do veículo é
     `--bg-2`, mais claro que a página, elevado. Dar `--bg-2` ao painel repetiria a
     confusão com outra borda. Igual ao fundo e com borda, ele lê como recorte da
     página — a regra que a tela passa a ensinar é CONTEÚDO É ELEVADO, CONTROLE É
     RECUADO. Cor nenhuma nova: só a troca de papel entre dois tokens que já existiam. */
  .filtros-corpo { padding: 16px; border: 1px solid var(--linha); border-top: 1px solid var(--linha);
                   border-radius: var(--raio-g); background: var(--bg); }
  /* No desktop o cabeçalho é o que nomeia a caixa — sem o `<summary>`, que some. */
  .filtros-cab b { font-size: 12px; letter-spacing: .08em; text-transform: uppercase;
                   color: var(--txt-2); }
}

/* Explica um painel curto em vez de escondê-lo: loja com uma marca só não tem o que
   filtrar além da busca, e caixa quase vazia sem motivo é a mesma pergunta de novo
   ("o que é isto?"). Some sozinha quando a segunda marca entrar no estoque. */
.filtro-nota { margin: 14px 0 0; padding-top: 14px; border-top: 1px solid var(--linha);
               font-size: 12.5px; line-height: 1.5; }

/* ============================================== situação do teste / assinatura */
/* Antes disto, os tetos eram cobrados em três endpoints e não apareciam em tela
   nenhuma: o lojista descobria o limite ao tomar um erro no meio de uma publicação.
   Com o teste de 7 dias seria pior — ele acordaria com o catálogo fora do ar sem
   nunca ter lido que havia prazo. */
.situacao { display: flex; align-items: center; justify-content: space-between; gap: 18px;
            flex-wrap: wrap; margin: 0 0 22px; padding: 16px 18px;
            border: 1px solid var(--linha); border-radius: var(--raio-g);
            background: var(--bg-2); }
.situacao > div { display: grid; gap: 6px; min-width: 0; }
.situacao b { font-size: 15.5px; }
.situacao span { font-size: 13.5px; line-height: 1.55; color: var(--txt-2); max-width: 74ch; }
.situacao .btn, .situacao .btn-ghost { flex: none; text-decoration: none; white-space: nowrap; }

/* Tom de atenção só nos dois últimos dias. Banner alarmante desde o primeiro dia vira
   paisagem, e aí não avisa nada quando importa. */
.situacao.atencao { border-color: var(--atencao-linha); background: var(--atencao-bg); }
.situacao.atencao b { color: var(--atencao); }
/* Fora do ar é o único estado em que o produto parou de entregar — merece a cor da
   zona de risco, a mesma do botão de excluir. */
.situacao.fora { border-color: #7d3348; background: #1c1218; }
.situacao.fora b { color: #ff9db4; }

.sit-usos { display: flex; flex-wrap: wrap; gap: 8px; }
.sit-uso { padding: 5px 11px; border-radius: 999px; font-size: 13px;
           border: 1px solid var(--linha); background: var(--bg); color: var(--txt-2); }
.sit-uso b { font-size: 13px; color: var(--txt); }
/* Teto batido: o número que trava a próxima ação não pode ter a mesma cor dos outros. */
.sit-uso.cheio { border-color: var(--atencao-linha); background: var(--atencao-bg); }
.sit-uso.cheio b { color: var(--atencao); }

@media (max-width: 640px) {
  .situacao { flex-direction: column; align-items: stretch; }
  .situacao .btn, .situacao .btn-ghost { text-align: center; }
}

/* Dica sob o campo da ficha. Vem do SCHEMA (`campos[].dica`, migration_040), não de um
   caso especial no front — o formulário inteiro é desenhado pelo schema, e um `if` por
   campo devolveria a ficha para dentro do código, um `if` por vez. */
.campo-dica { display: block; margin: 6px 0 0; font-size: 12.5px; line-height: 1.45;
              color: var(--txt-2); }

/* ==================================================== tela de entrar, enxuta */
/* Era um cartão alto e solto no meio do escuro: título grande, muito respiro entre
   os campos e nada em volta. Numa tela de duas entradas e um botão, o espaço vazio
   não organiza nada — só empurra o botão para baixo da dobra no celular.
   O casco (cabeçalho com Início/Blog e rodapé) vive no `app/index.html`. */
/* ⚠️ `.app-main > .card.estreito` E NÃO `.estreito`. Havia um `.app-main > .card
   { width: 100% }` logo acima que vence `.estreito { width: min(480px,100%) }` por
   especificidade (0,2,0 contra 0,1,0) — então o cartão de login NUNCA respeitou os
   480px e ocupava os 1120px da coluna inteira, esticado de ponta a ponta numa tela
   de dois campos. Precisa ser tão específico quanto a regra que o atropelava. */
.app-main > .card.estreito { width: min(400px, 100%); padding: 26px 24px; margin: 0 auto; }
.estreito h1 { font-size: clamp(20px, 3vw, 24px); margin-bottom: 6px; }
.estreito > .muted { margin: 0 0 16px; font-size: 14px; }
.estreito .lbl { margin-top: 12px; }
.estreito input[type="email"], .estreito input[type="password"] { padding: 11px 13px; }
.estreito .btn { width: 100%; margin-top: 16px; text-align: center; }

/* Centraliza na vertical quando a tela é SÓ o cartão (entrar, onboarding, erro). O
   `:has()` é o que permite fazer isso sem uma classe posta por JS em quatro telas; se
   um navegador não o suportar, o cartão volta a ficar no topo — que é o que ele já
   fazia, não uma tela quebrada. */
.app-main:has(> .estreito) { display: grid; place-items: center; align-content: center;
                             padding: 24px 0 48px; }
.cf-turnstile:not(:empty) { margin-top: 14px; }
.cf-turnstile:empty { margin: 0; }
.alternar { margin: 14px 0 0; }
.aceite { margin: 14px 0 0; }

/* O rodapé do app repete o da landing, mas aqui ele não pode grudar no conteúdo
   quando a tela é curta — `margin-top:auto` empurra para o fim da viewport, que é
   o que o `.app-body` em coluna permite. */
.app-body > .rodape { margin-top: auto; }

/* ------------------------------------------------- lixeira no card do estoque */
/* Separada das demais ações pelo espaçador: é a única do card que não tem volta, e
   encostá-la em "Editar" faria o polegar errar num card de 268px. */
.cv-espaco { flex: 1; }
.cv-lixo { display: grid; place-items: center; width: 36px; height: 36px; flex: none;
           border-radius: 9px; cursor: pointer; border: 1px solid var(--linha);
           background: none; color: var(--txt-2); }
.cv-lixo svg { width: 17px; height: 17px; fill: currentColor; }
.cv-lixo:hover { border-color: #7d3348; background: #2a1620; color: #ff9db4; }
.cv-lixo:disabled, .cv-lixo.ocupado { opacity: .5; cursor: default; }

/* Recusa por PLANO. É a única mensagem do app que pede uma decisão comercial — no
   texto cinza de 13px dos avisos de upload, o lojista conclui que o produto quebrou
   em vez de entender que o teste acabou. */
.plano-bloqueio { display: flex; align-items: center; justify-content: space-between;
                  gap: 16px; flex-wrap: wrap; margin: 14px 0 0; padding: 16px 18px;
                  border-radius: var(--raio); border: 1px solid var(--atencao-linha);
                  background: var(--atencao-bg); }
.plano-bloqueio > div { display: grid; gap: 4px; min-width: 0; }
.plano-bloqueio b { font-size: 15.5px; color: var(--atencao); }
.plano-bloqueio span { font-size: 13.5px; line-height: 1.55; color: var(--txt); max-width: 70ch; }
.plano-bloqueio .btn { flex: none; text-decoration: none; white-space: nowrap; }
@media (max-width: 640px) {
  .plano-bloqueio { flex-direction: column; align-items: stretch; }
  .plano-bloqueio .btn { text-align: center; }
}

/* ------------------------------------------- legenda das ações da confirmação */
/* Cada linha carrega a COR do próprio botão. É o que transforma a leitura em
   reconhecimento: o lojista bate o olho no verde da legenda e acha o verde na linha
   do campo, sem decodificar quarenta palavras de texto corrido. */
.conf-legenda { list-style: none; margin: 14px 0 0; padding: 0; display: grid; gap: 7px; }
.conf-legenda li { display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
                   font-size: 13.5px; color: var(--txt-2); }
.conf-legenda span { flex: none; padding: 3px 10px; border-radius: 999px;
                     font-size: 12.5px; font-weight: 600; }
.leg-ok { background: var(--acento); color: #04140e; }
.leg-corrigir { border: 1px solid var(--linha); background: var(--bg); color: var(--txt); }
.leg-ignorar { border: 1px solid var(--atencao-linha); background: transparent;
               color: var(--atencao); }

/* Ignorar tem cor própria — sem ela, "Corrigir" e "Ignorar" eram dois botões
   cinzentos idênticos, e a legenda perderia a única coisa que a faz funcionar. É o
   tom de ATENÇÃO e não o de perigo: apagar um campo se desfaz digitando de novo. */
/* ⚠️ EM REPOUSO ELE É NEUTRO, e o âmbar só aparece na INTENÇÃO. Preenchido, ele era
   sete retângulos marrons na mesma tela — e como a linha tem três botões, a cor que
   deveria significar "cuidado, isto apaga" virava padrão visual da lista.
   No hover e no foco ele assume a cor toda: quem está prestes a clicar vê o aviso. */
.btn-ignorar { border: 1px solid var(--linha); background: transparent;
               color: var(--txt-2); border-radius: var(--raio); cursor: pointer;
               font: inherit; font-weight: 600; }
.btn-ignorar:hover, .btn-ignorar:focus-visible {
  border-color: var(--atencao-linha); background: var(--atencao-bg);
  color: var(--atencao); }
.btn-ignorar:disabled { opacity: .5; cursor: default; }

/* ==================================================== o app no celular, apertado */
/* ⚠️ ESTA É A TELA REAL DO PRODUTO. O lojista usa isto em pé, no pátio, no celular,
   com uma mão — e o que o desenho de mesa produz ali é sempre o mesmo defeito: cada
   bloco ocupa uma tela inteira, e ler o passo custa quatro rolagens. As três causas
   estavam medidas em px de desktop: o respiro do cartão (24px de todos os lados), a
   grade dos vídeos (uma coluna, e o 9:16 virava um retângulo de 640px de altura) e o
   corpo dos textos de apoio, que são explicação e não conteúdo. */
@media (max-width: 640px) {
  .app-main { width: min(1120px, 94vw); padding: 20px 0 64px; }
  .card { padding: 16px 14px; }

  .topo-pagina { gap: 12px; margin: 0 0 18px; }
  .passo-h { font-size: 19px; }
  .passo-sub, .passo-nota, .passo-alt { font-size: 13px; line-height: 1.5; }
  .passo-falta, .passo-travado { padding: 11px 12px; font-size: 13px; }
  .tpl-sec { margin: 22px 0 6px; padding-top: 16px; }

  /* O cabeçalho do passo 1 é a promessa da marca, mas não precisa de três linhas de
     texto para isso no celular — o selo "IA" e o título já dizem. */
  .ia-cab { gap: 10px; }
  .passo-painel[data-passo="video"] .ia-cab { padding: 14px; }
  .ia-cab .muted { font-size: 13px; }
  .ia-video, .ia-opt { padding: 12px 13px; }
  .ia-chk em { font-size: 12.5px; }
  .ia-qtd { flex-wrap: wrap; }
  .ia-etapas { gap: 6px; }
  .ia-etapa { padding: 9px 11px; }
  .ia-etapa em { font-size: 12px; }

  /* ⚠️ DUAS COLUNAS, e é por isto que o card do 9:16 deixou de tomar a tela toda: numa
     coluna só, a moldura vertical ficava com 640px de altura num aparelho de 360px de
     largura — o lojista rolava uma tela inteira de retângulo preto por formato. Em
     duas colunas os formatos cabem de um olhar, que é como se escolhe entre eles. O
     teto em `vh` é o cinto de segurança para telas estreitas e altas.

     As colunas agora são as mesmas do desktop; o que sobra aqui é o aperto do `gap` e
     do `padding`, mais o teto de altura. */
  .pecas-grade { gap: 10px; }
  .peca-tela { max-height: 44vh; padding: 8px; }
  .peca-pe { padding: 9px 10px 0; gap: 6px; }
  .peca-pe b { font-size: 13px; }
  .peca-pe .muted { font-size: 11.5px; }
  /* Lado a lado e em partes iguais: a 390px o card mede ~155px, e "Baixar" +
     "Apagar" com o padding de desktop somam 142px + gap — quebravam em duas fileiras
     e cada peça ganhava 50px de altura à toa. Divididos ao meio cabem, e o alvo de
     toque ainda cresce. */
  .peca-acoes { padding: 8px 10px 10px; gap: 6px; flex-wrap: nowrap; }
  .peca-acoes > * { flex: 1 1 0; text-align: center; padding-left: 6px;
                    padding-right: 6px; }

  .fotos { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 8px; }
  .grade-ia { grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); }

  /* As pílulas quebram em duas fileiras num aparelho de 360px, e é o comportamento
     certo: são o filtro principal da tela e precisam continuar legíveis. Encolhem o
     bastante para caberem duas por linha sem virar alvo pequeno demais. */
  .resumo { gap: 6px; margin: 0 0 16px; }
  .pil { padding: 9px 13px; gap: 6px; }
  .pil b { font-size: 16px; }
  .pil span { font-size: 12.5px; }

  .grade-veiculos { gap: 12px; }
  .cv-novo { min-height: 150px; padding: 20px 16px; }
  .conf { padding: 15px 14px; }
  .conf-topo h2 { font-size: 17px; }
  .conf-topo .muted { font-size: 13px; }

  /* "instagram.com/" e o prefixo mais longo do app, e ele divide a mesma regra que
     "R$", "KM" e "+55". Em 360px, aos 14px com 13px de respiro dos dois lados, ele
     comeria ~126px de um campo de ~280px — quase metade da largura para escrever um
     nome de perfil de uma palavra. Encolher o prefixo (nunca o campo) devolve o
     espaco a quem digita, e os prefixos curtos nao perdem nada com isso. */
  .campo-com-prefixo span { padding: 0 9px; font-size: 13px; }

  /* ⚠️ TRÊS BOTÕES NUMA RÉGUA QUE NÃO QUEBRA NEM ENCOLHE. `.conf-acoes` nasceu com
     dois ("Está certo" e "Corrigir") e cabia; o "Ignorar" chegou depois e passou da
     borda. A causa é a soma de duas declarações que sozinhas são inofensivas:
     `flex: none` impede encolher, e a ausência de `flex-wrap` impede quebrar — a
     régua vira largura FIXA de ~280px dentro de um cartão de ~254px, e o excesso sai
     pela direita da janela levando rolagem horizontal junto.

     Em grade de três colunas iguais eles dividem o que há, e o alvo de toque continua
     inteiro porque a altura não muda. */
  .conf-acoes { display: grid; grid-template-columns: repeat(3, 1fr);
                gap: 6px; width: 100%; }
  .conf-acoes .btn, .conf-acoes .btn-ghost, .conf-acoes .btn-ignorar {
    padding: 10px 6px; text-align: center; }

  /* Mesma causa no rodapé de navegação do passo, com um agravante: os rótulos são
     escritos pelo `nav()` e um deles tem 20 caracteres ("Salvar e continuar →").
     Somado ao "← Vídeo" passa da largura do cartão, e `justify-content: space-between`
     não quebra linha — só afasta os dois até estourarem.

     Empilhados, cada um ocupa a linha inteira. A ordem do DOM não muda: voltar em
     cima, avançar embaixo, que é onde o polegar está. */
  .passo-nav { flex-direction: column; align-items: stretch; gap: 8px; }
  .passo-nav .btn, .passo-nav .btn-ghost { text-align: center; }
  /* O `<span></span>` que o `nav()` põe no lugar do botão ausente viraria uma sobra de
     8px no topo do primeiro passo e no fim do último. */
  .passo-nav > span:empty { display: none; }
}

/* ============================================== o fluxo de três passos (vazio) */
/* O desenho e a animação vivem no `style.css`, que o app carrega — aqui só o que muda
   por estar DENTRO de um card. O card é `--bg-2`, então os nós precisam do fundo
   recuado para existir; com o mesmo tom, o desenho sumia dentro da própria caixa. */
.fluxo-app { max-width: 420px; margin: 0 auto 22px; }
.fluxo-app .no rect { fill: var(--bg); }
.fluxo-app .pulsos path { filter: url(#brilho-app); }

/* ================================================ barra de progresso da IA */
/* ⚠️ NÃO É BARRA DE PORCENTAGEM, e a distinção é o ponto. O RunPod não devolve
   progresso — o job vai, some, e volta por webhook no fim —, então uma barra enchendo
   por tempo estimado seria número inventado. E número inventado se desmascara sozinho:
   basta a primeira vez que ela para em 90% por cinco minutos.

   A parte CHEIA conta etapas terminadas, que é verdade. O bloco que corre logo depois
   dela não tem número: só diz "esta está acontecendo agora". O movimento é o que
   responde de longe a pergunta que a roda de 22px não respondia — "está rodando ou
   travou?" —, e era essa a queixa. */
.ia-progresso { margin: 16px 0 0; }
.ia-barra { position: relative; height: 8px; border-radius: 999px; overflow: hidden;
            background: var(--bg); border: 1px solid var(--linha); }
.ia-feito { position: absolute; inset: 0 auto 0 0; border-radius: 999px;
            background: var(--acento); transition: width .4s ease; }
/* O bloco em curso encosta no que já terminou e é mais apagado: ele não é conquista,
   é promessa. Com a mesma cor cheia, a barra mentiria dizendo que já chegou ali. */
.ia-andando { position: absolute; top: 0; bottom: 0; background: var(--acento);
              opacity: .3; }
.ia-progresso.rodando .ia-andando { animation: ia-pulsar 1.5s ease-in-out infinite; }
/* A listra que atravessa a barra inteira é o sinal de vida que se lê de relance, mesmo
   sem olhar para o número. */
.ia-progresso.rodando .ia-barra::after {
  content: ""; position: absolute; inset: 0; border-radius: 999px;
  background: linear-gradient(90deg, transparent, rgba(55, 211, 155, .5), transparent);
  animation: ia-varrer 1.8s linear infinite;
}
@keyframes ia-pulsar { 50% { opacity: .62; } }
@keyframes ia-varrer {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(100%); }
}
.ia-progresso-rot { margin: 9px 0 0; font-size: 13.5px; line-height: 1.5;
                    color: var(--txt-2); }
.ia-progresso-rot b { color: var(--txt); }

/* Sem movimento a barra continua informando: a parte cheia é a informação, e ela não
   depende de animação nenhuma. */
@media (prefers-reduced-motion: reduce) {
  .ia-progresso.rodando .ia-barra::after,
  .ia-progresso.rodando .ia-andando { animation: none; }
}

/* --------------------------------- o mesmo aviso, visto da lista de veículos */
/* ⚠️ "DE CARA" É AQUI, e não só dentro do veículo. O lojista dispara a IA, volta para o
   estoque e fica olhando um selo de 11px escrito "IA ⟳" — que num card com quatro selos
   não se distingue dos outros. A faixa atravessa a base da foto: é a única coisa em
   movimento na grade, e por isso é a única que o olho acha sem procurar. */
.cv-processando { position: absolute; left: 0; right: 0; bottom: 0; height: 4px;
                  overflow: hidden; background: rgba(5, 8, 14, .55); }
.cv-processando::after {
  content: ""; position: absolute; inset: 0; border-radius: 999px;
  background: linear-gradient(90deg, transparent, var(--acento), transparent);
  animation: ia-varrer 1.6s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .cv-processando::after { animation: none; background: var(--acento); opacity: .5; }
}

/* ================================================ o estado do passo do vídeo */
/* ⚠️ ISTO ERA UM PARÁGRAFO DE 13px NA COR MAIS APAGADA DA PALETA, e vinha DEPOIS do
   botão. A frase que ele carrega — "toque em Gerar com IA" — é a coisa mais acionável
   da tela inteira, e estava com o menor peso tipográfico e o último lugar na ordem de
   leitura. O relato foi "o lojista fica procurando"; a tela realmente escondia.

   Agora é bloco, com ícone, título e detalhe, ANTES da ação. E cobre os quatro estados
   do passo, inclusive os dois que antes não diziam nada: sem vídeo (o botão estava
   desabilitado sem explicar por quê) e durante o envio. */
.estado-passo { display: flex; gap: 12px; align-items: flex-start; margin: 0 0 14px;
                padding: 13px 15px; border-radius: var(--raio);
                border: 1px solid var(--linha); background: var(--bg); }
.ep-ic { flex: none; display: grid; place-items: center; width: 26px; height: 26px;
         border-radius: 50%; font-size: 13px; font-weight: 700; line-height: 1;
         border: 1px solid var(--linha); color: var(--txt-2); }
.ep-txt { min-width: 0; display: grid; gap: 3px; }
.ep-txt b { font-size: 15px; }
.ep-txt em { font-style: normal; font-size: 13px; line-height: 1.5; color: var(--txt-2); }

/* "Pronto" é o estado que PEDE UMA AÇÃO, e por isso é o único que usa o acento: ele
   tem de puxar o olho para o botão que vem logo abaixo. Os outros três informam. */
.estado-passo.pronto { border-color: var(--acento-linha); background: var(--acento-fraco); }
.estado-passo.pronto .ep-ic { border-color: var(--acento-linha); color: var(--acento); }
.estado-passo.enviando { border-color: #3a4a6b; }
.estado-passo.enviando .ep-ic { border-color: #3a4a6b; color: #9fc4ff; }
.estado-passo.erro { border-color: #5c2731; }
.estado-passo.erro .ep-ic { border-color: #5c2731; color: #ffb3bd; }

/* ⚠️ ESTA BARRA TEM PORCENTAGEM DE VERDADE, ao contrário da barra da IA logo abaixo
   dela. Aqui o número é bytes já enviados pelo navegador — o próprio upload sabe. Lá
   seria tempo estimado de um job do RunPod, que ninguém sabe. Duas barras parecidas
   dizendo coisas de naturezas diferentes é confuso; a diferença fica no texto, que
   aqui traz o "%" e lá traz "2 de 4 prontas". */
.ep-barra { display: block; height: 6px; margin: 7px 0 1px; border-radius: 999px;
            overflow: hidden; background: var(--bg-2); border: 1px solid var(--linha); }
.ep-barra i { display: block; height: 100%; border-radius: 999px;
              background: var(--acento); transition: width .25s ease; }

/* ------------------------------------------ o relógio e a linha de expectativa */
/* ⚠️ ESTE PAR RESOLVE ANSIEDADE, NÃO INFORMA PROGRESSO. São duas perguntas diferentes:
   "está andando?" e "quanto falta?". A barra acima responde a segunda no que dá para
   saber (etapas prontas); estas duas linhas respondem a primeira — um número que anda
   de segundo em segundo, e a faixa de tempo observada.
   "Costuma levar" e não uma previsão: ninguém sabe quanto este job vai demorar, e um
   número exato aqui seria a mesma mentira que a barra de porcentagem recusa. */
.ia-relogio { display: flex; flex-wrap: wrap; gap: 4px 10px; align-items: baseline;
              margin: 7px 0 0; font-size: 13px; }
.ia-relogio [data-relogio] { flex: none; padding: 2px 9px; border-radius: 999px;
                             font-variant-numeric: tabular-nums; font-weight: 700;
                             color: var(--acento); background: rgba(55, 211, 155, .12);
                             border: 1px solid #1f5340; }
.ia-relogio em { font-style: normal; color: var(--txt-2); line-height: 1.5; }

/* A etapa em curso é a única coisa em movimento do painel — é assim que o olho a acha
   sem ler as quatro. A varredura fica na BASE do cartão, onde não atravessa o texto. */
.ia-etapa.rodando { position: relative; overflow: hidden; }
.ia-etapa.rodando::after {
  content: ""; position: absolute; left: 0; right: 0; bottom: 0; height: 3px;
  background: linear-gradient(90deg, transparent, var(--acento), transparent);
  animation: ia-varrer 1.6s linear infinite;
}
@media (prefers-reduced-motion: reduce) {
  .ia-etapa.rodando::after { animation: none; background: var(--acento); opacity: .55; }
}

/* A nota do envio: fica abaixo da barra e em tom menor, porque é tranquilizante e não
   instrução — quem está esperando não precisa de mais uma coisa para decidir. */
.ep-nota { display: block; margin: 7px 0 0; font-style: normal; font-size: 12.5px;
           line-height: 1.5; color: var(--txt-2); }

/* ------------------------------------------------------------------- visor */
/* Ver a foto e assistir ao vídeo em tamanho grande.

   ⚠️ TUDO NESTA TELA É MINIATURA, e essa era a queixa. A grade de fotos são quadrados
   de ~110px e a moldura do 9:16 é mais estreita ainda — mas as perguntas que o lojista
   faz olhando para elas ("esta foto ficou boa?", "o card cobriu o carro?", "o preço
   saiu certo?") não se respondem nesse tamanho. Sem o visor, a única forma de conferir
   era publicar o anúncio e abrir o link, ou baixar a peça e abrir no aparelho.

   O `overflow: hidden` no body é o que impede a página de rolar POR TRÁS do visor no
   celular — sem ele, o dedo que arrasta para ver a foto rola o assistente, e ao fechar
   o lojista está noutro passo sem ter pedido. A calha de rolagem já está reservada em
   `html` (ver o topo do arquivo), então esconder aqui não escorrega a página de lado. */
body.com-visor { overflow: hidden; }

/* ⚠️ AS CALHAS LATERAIS DE 66px NÃO SÃO ENFEITE: são o lugar das setas. Sem elas a
   mídia chega à borda e passa POR BAIXO dos botões — o que só não aparecia numa janela
   larga, porque o palco tem teto de 1100px e sobrava margem. Numa janela de ~1000px a
   conta fecha em zero e a seta volta a cobrir a foto. */
.visor { position: fixed; inset: 0; z-index: 90; display: grid; place-items: center;
         padding: max(56px, env(safe-area-inset-top)) 66px
                  max(16px, env(safe-area-inset-bottom)); }
.visor-fundo { position: absolute; inset: 0; background: rgba(3, 6, 12, .93); }

.visor-palco { position: relative; margin: 0; max-width: min(1100px, 100%);
               max-height: 100%; display: flex; flex-direction: column;
               align-items: center; gap: 10px; }
/* `contain` e não `cover`: aqui a pergunta é o que a mídia MOSTRA, e cortar as bordas
   para preencher a caixa esconderia justamente o que se veio conferir. */
.visor-palco img, .visor-palco video {
  display: block; width: auto; height: auto; max-width: 100%;
  max-height: calc(100vh - 150px); object-fit: contain;
  border-radius: var(--raio); background: #05080e;
}
.visor-palco figcaption { display: flex; flex-wrap: wrap; gap: 4px 14px;
                          justify-content: center; font-size: 13.5px; color: var(--txt-2); }
.visor-conta { font-variant-numeric: tabular-nums; }

/* Fechar e navegar ficam FORA do palco, ancorados na tela: dentro dele mudariam de
   lugar conforme a proporção da mídia — o 9:16 e o 16:9 têm larguras muito diferentes,
   e o ✕ saltaria de posição a cada peça. */
.visor-x, .visor-seta { position: absolute; z-index: 2; display: grid; place-items: center;
                        border-radius: 999px; border: 1px solid var(--linha);
                        background: rgba(5, 8, 14, .88); color: var(--txt);
                        cursor: pointer; line-height: 1; }
.visor-x:hover, .visor-seta:hover { background: rgba(5, 8, 14, 1); border-color: var(--acento); }
.visor-x { top: 10px; right: 10px; width: 44px; height: 44px; font-size: 17px; }
.visor-seta { top: 50%; transform: translateY(-50%); width: 46px; height: 46px;
              font-size: 28px; padding-bottom: 4px; }
.visor-seta.ant { left: 8px; }
.visor-seta.prox { right: 8px; }

/* O botão de ampliar, por cima da mídia, onde o clique nela já tem outro dono — a
   candidata do lote (a figura inteira marca/desmarca) e a peça (o `<video>` tem
   controles). Ver os comentários no `app.js`. */
.midia-zoom { position: absolute; top: 6px; right: 6px; z-index: 2;
              width: 32px; height: 32px; display: grid; place-items: center;
              border-radius: 8px; border: 1px solid var(--linha);
              background: rgba(5, 8, 14, .8); color: var(--txt);
              font-size: 15px; line-height: 1; cursor: zoom-in; }
.midia-zoom:hover { background: rgba(5, 8, 14, .96); border-color: var(--acento); }
/* Na candidata o canto direito já é da marca de escolhida (`.cand-marca`): dois alvos
   sobrepostos ali fariam ampliar e marcar disputarem o mesmo polegar. */
.foto.cand .midia-zoom { right: auto; left: 6px; }
/* A foto já publicada não precisa de botão: as ações dela são ★ e ✕, que ficam no
   canto, e o resto da imagem está livre para abrir o visor. */
.foto img.ampliavel { cursor: zoom-in; }

/* ⚠️ NO CELULAR A SETA NÃO PODE FICAR NO MEIO DA LATERAL, e ficava. Medido a 390px de
   viewport: a mídia ocupa quase toda a largura, então os dois botões de 46px caíam EM
   CIMA da foto — bem no meio, que é exatamente onde está o que o lojista abriu o visor
   para olhar. Elas descem para os cantos de baixo, ao lado do contador, que é onde o
   polegar já está; o `padding-bottom` do visor é o que garante a faixa livre para elas,
   e o teto de altura da mídia é o que impede a foto de invadi-la. */
@media (max-width: 560px) {
  .visor { padding: max(56px, env(safe-area-inset-top)) 10px
                    calc(72px + env(safe-area-inset-bottom)); }
  .visor-seta { top: auto; bottom: 12px; transform: none; width: 44px; height: 44px; }
  .visor-palco img, .visor-palco video { max-height: calc(100vh - 210px); }
}
