/* ============================================
   LOJA — a vitrine pública do CLIENTE.

   Folha isolada de propósito: não importa nada de application/dashboard, e
   nenhum token daqui vaza pra lá. A loja é servida no domínio do cliente e não
   pode herdar a marca do Giants num deploy que ninguém pediu.

   MOBILE PRIMEIRO: a maioria entra pelo link do WhatsApp, no celular, em rede
   ruim. Layout de uma coluna é o padrão; o desktop é que ganha exceção.

   UM MARKUP, DOIS ENQUADRAMENTOS. Não existe view de celular neste módulo (nem
   `.mobile.erb`, nem variante, nem leitura de User-Agent): duas cópias do HTML
   são duas verdades de preço. Quem sabe que existe celular é ESTA folha e a
   `<nav class="loja-barra">`, que é sempre renderizada e some no desktop.

   ORDEM DO ARQUIVO — mexer respeitando, senão a cascata vira loteria:
     1. tokens            (:root, a §1.2 da tipografia — que fecha com a única
                           regra de caixa alta da folha — e as classes de tema)
     2. base              (reset curto, foco, movimento, utilitários)
     3. chassi            (topo, barra, main, rodapé)
     4. componentes       (chips, card, botões, folha, chat, formulários…)
     5. só no celular
     6. desktop (min-width: 700px)

   CONTRATO COM AS OUTRAS FRENTES: o vocabulário de classes inteiro nasce aqui.
   As telas (vitrine, carrinho, checkout, chat) escrevem ERB usando estas
   classes e NÃO editam CSS — é o que impede quatro frentes de inventarem quatro
   botões primários diferentes na mesma loja.

   O SISTEMA É FECHADO, E HÁ TESTE PROVANDO (auditoria_experiencia_real_test):
     · tipografia: SEIS degraus, e nenhum deles escrito em px fora da §1 —
       --t-rotulo 12 · --t-apoio 14 · --t-corpo 16 · --t-num 20 · --t-titulo 25
       · --t-display 31. Cada `font-size` desta folha declara o `line-height` da
       banda no MESMO bloco: metade da queixa de "letra grandona" era o 1.5
       global abrindo caixa de 48px num preço de 32. Contraste de peso vem de
       TRÊS pesos (400/600/700), e --pe-forte é raro de propósito: preço,
       total, número do pedido e h1. O 800 (eram 23 usos) saiu da loja.
     · espaçamento: `padding`, `margin` e `gap` saem de --e1..--e9. Nenhum
       literal de px, e não se inventa um décimo token pra acomodar dois casos.
       A banda de cima (48/64/96) é de PÁGINA e não de componente: ela existe
       porque separar duas SEÇÕES com o mesmo teto do padding de uma pílula
       de cupom é o que fazia a vitrine ler como uma massa só.
     · cor: nenhum hex novo fora do bloco de tokens. A próxima conta é azul.
     · vermelho: `background: var(--loja-cor)` cabe em TRÊS regras da folha
       inteira — a CTA, a ação do popup e a fala do cliente no chat. Fora
       delas o primário só existe como FIO ou TEXTO (anel de foco, traço da
       aba ativa, barra do Turbo, preço em promoção). Ver o orçamento no fim
       da §1; o teste conta as regras, não os pixels.
     · caixa alta: UMA regra, num degrau só. Seis componentes em uppercase ao
       mesmo tempo foi o que fez tudo gritar junto — e nenhum deles é botão.
     · altura de controle: --toque, --alt-campo, --alt-cta e --alt-cartao.
       Nenhum `min-height` em px fora da §1.
   ============================================ */

/* ============================================
   1. TOKENS

   Cor é DADO, não código: os seis valores abaixo são só o fallback de quem
   nunca abriu a tela de aparência. O que manda em produção é o `style=` do
   <body>, escrito por LojaHelper#loja_style_vars a partir de Loja::Tema — que
   sanitiza na escrita E na leitura. A próxima conta é azul e nada aqui muda.
   ============================================ */

:root {
  --loja-cor: #111111;
  --loja-fundo: #ffffff;
  --loja-tinta: #16181d;
  --loja-tinta-fraca: #666c78;
  --loja-borda: #e4e6ea;
  --loja-destaque: #b42318;
  /* Header próprio (chave `cor_topo`): sem ele, logo clara de fundo
     transparente — que é o caso da Dynamo — some no branco da página. O default
     é branco justamente pra que quem não configurou nada continue vendo a loja
     exatamente como ela era antes desta folha. */
  --loja-cor-topo: #ffffff;
  --loja-raio: 12px;
  /* RAIO DE CAMPO E DE FOTO NÃO É DECISÃO DO DONO. `--loja-raio` é a chave de
     personalidade (card, botão, chip, selo) e pode ir a 22px; um <input> de
     22px vira pílula e o campo de nome do checkout deixa de parecer campo.
     Este raio é fixo de propósito: forma de campo é legibilidade, não marca. */
  --loja-raio-campo: 10px;

  /* DERIVADOS: é o que faz a loja parecer PROJETADA a partir de uma cor só,
     em qualquer conta, sem pedir mais nenhuma decisão ao dono. Cada um é
     declarado duas vezes de propósito: a primeira linha é o valor que navegador
     velho entende, a segunda é a mistura de verdade. Sem o par, um navegador
     sem `color-mix` deixaria a variável inválida e o botão sairia sem fundo.

     `color-mix` mora AQUI e só aqui — nunca no helper, nunca no `style=` inline:
     valor calculado indo pro atributo `style` fura o portão de sanitização do
     tema, que é o que segura injeção de CSS no domínio do cliente. */
  --loja-cor-forte: #000000;
  --loja-cor-forte: color-mix(in srgb, var(--loja-cor) 82%, #000);
  --loja-cor-veu: var(--loja-fundo);
  --loja-cor-veu: color-mix(in srgb, var(--loja-cor) 8%, var(--loja-fundo));
  --loja-superficie: #f6f7f8;
  --loja-superficie: color-mix(in srgb, var(--loja-tinta) 4%, var(--loja-fundo));

  /* O FIO. É ele que passa a fechar card, resumo, entrega e item do carrinho.
     `--loja-borda` fica SÓ para forma de campo: caixa de <input> é legibilidade
     e precisa continuar mais forte que o fio que só separa duas coisas. */
  --loja-filete: #e9eaee;
  --loja-filete: color-mix(in srgb, var(--loja-tinta) 11%, var(--loja-fundo));

  /* A PLACA DA FOTO É BRANCA E FIXA, e é decisão contra o gosto: a maioria das
     652 fotos raspadas é JPEG de fundo BRANCO, e numa placa cinza cada foto
     desenha um retângulo branco visível dentro dela — a fileira fica pior do
     que sem placa. O que faz as fotos lerem como uma coleção é o enquadramento
     idêntico (mesma proporção, mesmo respiro, contain), não a cor do chão. */
  --loja-foto: #ffffff;

  /* O PALCO — o preto derivado da TINTA DA CONTA, não um preto nosso. Garante
     chassi escuro legível em qualquer loja carregando o matiz da marca dela (a
     conta azul recebe um preto azulado) sem pedir uma decisão nova ao dono. */
  --loja-palco: #101114;
  --loja-palco: color-mix(in srgb, var(--loja-tinta) 22%, #000);
  --loja-palco-tinta:       #ffffff;
  --loja-palco-tinta-fraca: rgb(255 255 255 / .70);
  --loja-palco-filete:      rgb(255 255 255 / .16);

  /* O RESPIRO DENTRO DA PLACA. É ele, e só ele, que faz foto de 652 origens
     diferentes ler como uma marca só: nenhuma encosta na borda. */
  --foto-respiro:       var(--e4);
  --foto-respiro-ficha: var(--e6);

  /* Tinta do topo. Cai por classe (`loja--topo-tinta-clara`), não por cor
     livre: o dono sabe responder "a minha logo é clara ou escura?", não sabe
     escolher um cinza que passe em contraste sobre o header dele. */
  --loja-topo-tinta: var(--loja-tinta);
  --loja-topo-tinta-fraca: var(--loja-tinta-fraca);
  --loja-topo-borda: var(--loja-borda);

  /* UMA SOMBRA SÓ, E ELA É DE COISA FLUTUANTE. A `--loja-sombra-1` (a de
     repouso, que o card usava no hover) saiu: 24 sombras iguais numa grade
     não hierarquizam nada. Sombra na loja significa "isto está por cima da
     página" — tirinha, folha e popup, e mais nada. */
  --loja-sombra-2: 0 8px 28px rgb(0 0 0 / .16);
  /* Uma curva só pra loja inteira. A folha vivia com ZERO transição e ZERO
     :hover — é daí que vinha a sensação de página morta. */
  --loja-t: 160ms cubic-bezier(.2, .7, .3, 1);
  /* A SEGUNDA CURVA, mais lenta, é só de foto e folha: o que ocupa muita área
     na tela precisa de mais tempo pra não parecer um salto. */
  --loja-t-lenta: 240ms cubic-bezier(.2, .7, .3, 1);

  --e1: 4px;
  --e2: 8px;
  --e3: 12px;
  --e4: 16px;
  --e5: 24px;
  --e6: 32px;
  /* A BANDA DE PÁGINA. A escala parava em 32px, então o ar máximo entre duas
     SEÇÕES da vitrine era o mesmo teto do padding de uma pílula de cupom: era
     estruturalmente impossível dizer "aqui acaba um assunto e começa outro". */
  --e7: 48px;   /* respiro entre blocos de uma tela; row-gap da grade no celular */
  --e8: 64px;   /* separação entre SEÇÕES da vitrine; row-gap da grade no desktop */
  --e9: 96px;   /* topo do rodapé e fim de página no desktop */

  /* Alvo de dedo. 44px é o piso da WCAG e o piso desta loja: todo botão, link
     de ação e ícone se mede por ele. */
  --toque: 44px;
  /* AS OUTRAS TRÊS ALTURAS, pelo mesmo motivo do espaçamento: 40/44/48/52/56
     estavam soltos como literal em cinco regras, e altura é o que o olho lê
     como ritmo — dois controles vizinhos com 8px de diferença parecem defeito
     de alinhamento, não hierarquia. */
  --alt-campo:  48px;   /* <input>, botão secundário, controle − n +, busca */
  --alt-cta:    52px;   /* a CTA primária cheia. UMA por tela. */
  --alt-cartao: 64px;   /* cartão de escolha (retirada × entrega) */

  /* A BARRA DE BAIXO TEM DUAS ALTURAS. Na vitrine ela é só a faixa de atalhos;
     na tela do produto e na do carrinho ela ganha o slot de ação
     (`content_for :barra_acao`) e passa a medir 113px. A TELA é quem declara
     que preencheu o slot (`body.loja--com-acao`, escrita pelo layout a partir
     do próprio `content_for`), e os três consumidores — FAB da Bia, tirinha e
     chão do body — continuam lendo um token só. */
  --loja-barra-base: 56px;   /* a faixa de atalhos, sempre presente */
  --loja-barra-acao: 0px;    /* o slot de compra, só onde a tela o preenche */
  /* A SOMA NÃO MORA AQUI — e essa é a correção nº 1 desta passada.
     `--loja-barra-altura` era declarada neste :root, ou seja, resolvida com o
     `--loja-barra-acao: 0px` do PRÓPRIO :root. A override que vem logo abaixo
     (`body.loja--com-acao`) chegava tarde: o valor já tinha sido substituído no
     elemento de cima e herdado pronto. O token dizia 57px numa barra que mede
     113px — e o resultado, medido no Chrome a 390x844, era o FAB da Bia sobre
     40% do botão de comprar (`element intercepts pointer events`, o dedo do
     cliente também não passava) e o cartão "Retirar na loja" enterrado no
     checkout. A soma passou para `body.loja` (§2), o mesmo elemento que recebe
     a classe — ver o comentário lá. */
}

/* 8px de padding do slot + os 52px de `--alt-cta`, que é a altura da CTA
   primária. Se um dos dois mudar, muda aqui junto — há teste somando os três
   números (test/auditoria_experiencia_real_test.rb). */
body.loja--com-acao { --loja-barra-acao: 60px; }

/* ============================================
   1.2  TIPOGRAFIA — A ESCALA FECHADA DA LOJA

   SEIS DEGRAUS. Razão 1,25 do corpo pra cima (16→20→25→31); abaixo do corpo,
   dois degraus com papéis MUTUAMENTE EXCLUSIVOS — 12 só existe em caixa alta
   com tracking, 14 só existe em tinta fraca. É o que separa opticamente
   12/14/16 sem inventar diferença de tamanho que o celular não mostra.

   REGRA DURA: nenhuma declaração `font-size` desta folha escreve px fora daqui.
   Todas são var(--t-*), e há teste lendo o arquivo pra provar.

   TETO DO CELULAR: 31px. Nada na loja passa disso em 390px — a loja parecendo
   outdoor foi a primeira queixa do dono.
   ============================================ */
:root {
  /* DUAS FAMÍLIAS, AS DUAS DO APARELHO. Zero requisição, zero Google Fonts.
     Declaradas no :root (e não em `body.loja`) DE PROPÓSITO: as classes de tema
     do dono moram no <body>, e uma declaração direta no elemento vence a
     herança do :root. Era esse o defeito silencioso: `body.loja` declarava
     font-family DEPOIS de `body.loja--fonte-serif`, com a mesma especificidade
     (0,1,1), então a fonte que o dono escolhia no painel nunca chegava à tela. */
  --loja-fonte-texto:  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --loja-fonte-titulo: var(--loja-fonte-texto);

  /* OS SEIS DEGRAUS, com o papel de cada um escrito. Papel novo não ganha
     degrau novo: ganha um dos seis. */
  --t-rotulo:  12px;  /* CAPS obrigatório. Marca do card, rótulo de seção, itens da barra, selo. */
  --t-apoio:   14px;  /* Sempre em tinta fraca. Sabor, riscado, nota, unitário, rodapé, meta. */
  --t-corpo:   16px;  /* Corpo, descrição, NOME DO PRODUTO no card, todo <input>, rótulo de botão. */
  --t-num:     20px;  /* Só número que decide: preço do card, total do resumo, total do item. */
  --t-titulo:  25px;  /* h1 das telas internas e a abertura da vitrine. */
  --t-display: 31px;  /* Só dois usos: preço da ficha e número do pedido. */

  /* ENTRELINHA POR BANDA — declarada JUNTO de todo font-size, sem exceção.
     Metade da queixa de "letra grandona" era o 1.5 global abrindo caixa de 48px
     num preço de 32: o buraco de ar em volta, não o tamanho. */
  --lh-rotulo:  1.2;
  --lh-apoio:   1.45;
  --lh-corpo:   1.55;
  --lh-num:     1.25;
  --lh-titulo:  1.15;
  --lh-display: 1.05;

  /* TRACKING COM TRÊS USOS NOMEADOS, E NENHUM OUTRO. */
  --ls-caps:    .10em;   /* exclusivo do --t-rotulo em caixa alta */
  --ls-titulo:  -.01em;  /* --t-titulo */
  --ls-display: -.02em;  /* --t-display */

  /* TRÊS PESOS. O 800 sai da loja inteira (eram 23 usos) e o 500 também: nada
     tinha peso porque tudo tinha peso. --pe-forte é RARO — preço do card,
     preço da ficha, total do resumo, total do item, número do pedido e h1. */
  --pe-normal: 400;
  --pe-medio:  600;
  --pe-forte:  700;
}

/* RAIO E FONTE NÃO SÃO VALOR, SÃO CLASSE. Enum fechado no serviço (ver
   Loja::Tema) vira `loja--raio-suave` aqui e a folha resolve o resto — é o que
   mata a injeção por unidade ("12px; background:url("...")"): nenhum valor do
   dono chega a ser escrito como CSS. */
.loja--raio-reto { --loja-raio: 2px; }
.loja--raio-suave { --loja-raio: 12px; }
.loja--raio-redondo { --loja-raio: 22px; }

/* O TEMA DE FONTE DO DONO SÓ TROCA TOKEN — e é assim que ele volta a funcionar.
   Nenhuma destas classes declara `font-family`: quem declara é `body.loja`, uma
   vez, lendo `--loja-fonte-texto`. Enquanto elas declaravam a família, a linha
   de `body.loja` (mesma especificidade 0,1,1, e escrita depois na folha) vencia
   a disputa e apagava a escolha do painel em silêncio.
   Stacks LOCAIS: nada de <link> pro Google Fonts. Meio segundo de tela branca
   no 3G é caro numa loja que abre pelo link do WhatsApp — o caráter vem de
   tracking, contraste e ar, não de família remota. */
body.loja--fonte-sistema {
  --loja-fonte-texto: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  --loja-fonte-titulo: var(--loja-fonte-texto);
}
body.loja--fonte-serif {
  --loja-fonte-texto: Georgia, "Times New Roman", "Liberation Serif", serif;
  --loja-fonte-titulo: var(--loja-fonte-texto);
}
body.loja--fonte-condensada {
  --loja-fonte-texto: "Arial Narrow", "Helvetica Neue Condensed", "Liberation Sans Narrow", Impact, sans-serif;
  --loja-fonte-titulo: var(--loja-fonte-texto);
}

/* TOPO CLARO × TOPO ESCURO. A classe vem da chave `topo_tinta`; o default
   (`escura`) é exatamente a loja de antes desta folha. */
.loja--topo-tinta-clara {
  --loja-topo-tinta: #ffffff;
  --loja-topo-tinta-fraca: rgb(255 255 255 / .72);
  --loja-topo-borda: rgb(255 255 255 / .14);
}

/* O CHASSI ESCURO — OPT-IN POR CONTA (chave `chassi`, default CLARO).

   É assim que o preto volta sem furar o orçamento do vermelho: o palco é o
   chassi (topo, barra do polegar e rodapé), o catálogo continua em branco
   calmo, e a logo branca — que hoje some sobre o branco da página — finalmente
   tem onde pousar. A padaria da conta seguinte não herda nada disto: sem a
   chave gravada, esta classe nem chega ao <body>.

   VEM DEPOIS de `.loja--topo-tinta-clara` DE PROPÓSITO: as duas moram no mesmo
   <body> e têm a mesma especificidade (0,1,0), então quem decide é a ordem do
   arquivo. Sobre o palco a tinta é branca, e a escolha `topo_tinta: escura` do
   dono não pode devolver texto preto sobre preto.

   O par --loja-palco-tinta-fraca (.70 de branco) sobre --loja-palco dá 9,5:1 —
   folga sobre os 4,5:1 da WCAG, que é o que o rodapé inteiro usa.

   `--loja-cor-topo` só cai aqui quando o dono NÃO gravou `cor_topo`: se ele
   gravou, o helper emite o valor dele no `style=` do <body>, e style inline
   vence classe (ver LojaHelper#loja_palco_no_topo?). */
.loja--chassi-escuro {
  --loja-cor-topo:         var(--loja-palco);
  --loja-topo-tinta:       var(--loja-palco-tinta);
  --loja-topo-tinta-fraca: var(--loja-palco-tinta-fraca);
  --loja-topo-borda:       var(--loja-palco-filete);
}

/* ============================================
   O ORÇAMENTO DO VERMELHO — a regra que resolve "cinco botões gritando" de
   forma AUDITÁVEL, e é ela que fecha o sistema pra manutenção: sem um número
   escrito, em dois sprints alguém pinta um botão secundário de primário e a
   tese apodrece calada.

   `background: var(--loja-cor)` pode aparecer em TRÊS regras da folha inteira,
   e são estas:
     1. .loja-btn                   — a CTA primária (uma por tela, garantida
                                      por `.loja-acao-repetida` no celular)
     2. .loja-popup__cta            — a única ação do diálogo de aviso
     3. .loja-chat__bolha--cliente  — a fala da própria pessoa na conversa
   `--loja-cor-forte` não abre uma quarta: ele só existe no `:hover` da CTA, que
   é a mesma superfície mudando de estado, não uma superfície nova.

   Fora dessas três o primário existe como FIO ou como TEXTO, sempre em área
   pequena: o anel de `:focus-visible`, a `.turbo-progress-bar` (3px atravessando
   o alto da página), o preço em promoção, o traço de 2px sob a aba ativa, o de
   3px sob o banner e o tracejado de 1px da pílula de cupom do popup. Fio de 2px
   não é bloco de 90x44: é por isso que a conta é de REGRA e de superfície, não
   de aparições da variável.

   E é por isso também que a conta NÃO se faz contando pixels da cor no Chrome:
   a Dynamo gravou `cor_destaque` igual a `cor`, então uma varredura por valor
   de cor devolve os selos de desconto junto com as CTAs e acusa sete blocos
   onde existe um. Quem responde "isto é ação?" é a variável, não o hex.

   O que PERDEU o vermelho no caminho: o botão do card, o botão Buscar, o chip
   ativo, o badge do carrinho, o FAB da Bia e o tracejado do PIX. `.loja-selo`
   nunca contou aqui — desconto é `--loja-destaque`, semântica, não cor de ação.
   Medido: de 7 blocos primários na primeira dobra do celular para ZERO na
   vitrine e UM na ficha.
   ============================================ */

/* ============================================
   1.3  A COMBINAÇÃO CANÔNICA DO RÓTULO — o único caixa alta da loja.

   Seis componentes de três seções diferentes copiavam a MESMA combinação
   (12/600/CAPS/--ls-caps) em cinco regras espalhadas, cada uma escrevendo as
   cinco declarações de novo. Não era duplicação de estilo: era duplicação de
   DECISÃO — mexer no degrau do rótulo pedia cinco edições, e quem esquecesse
   uma criava um sétimo degrau sem que nada avisasse.

   A caixa alta é o grito mais barato do CSS, e era ela que fazia marca, selo,
   barra, SKU e rótulo de seção brigarem entre si e com o produto. Sobrando uma
   regra só, o teto vira aritmética: subir a caixa alta pra qualquer outro lugar
   passa a ser uma regra NOVA, e o portão da auditoria reprova.

   Mora aqui, no fim da §1 — colada na escala — e não junto de cada componente
   DE PROPÓSITO: o que esta regra diz é uma COMBINAÇÃO tipográfica, e quem
   responde por combinação é a escala. Cada componente continua dono do que é
   dele — cor, margem, borda, fundo — na seção dele. Ordem: esta regra vem antes
   de todas elas, então basta um componente redeclarar pra vencer; nenhum
   precisa, e nenhum deve.

   `.loja-produto__meta .loja-chip` entra pelo mesmo motivo dos outros e não
   muda de nome (é assert_select de teste de integração): na ficha o chip não é
   aba de navegação, é a procedência do produto — mesmo papel do SKU ao lado.
   ============================================ */
.loja-rotulo,
.loja-card__marca,
.loja-produto__sku,
.loja-produto__meta .loja-chip,
.loja-barra__item,
.loja-selo {
  font-size: var(--t-rotulo); line-height: var(--lh-rotulo);
  font-weight: var(--pe-medio);
  text-transform: uppercase; letter-spacing: var(--ls-caps);
}

/* ============================================
   2. BASE
   ============================================ */

* { box-sizing: border-box; }

body.loja {
  margin: 0;
  background: var(--loja-fundo);
  color: var(--loja-tinta);
  /* UM LUGAR SÓ DECLARA A FAMÍLIA, e ele lê o token — ver §1.2. Escrita à mão
     aqui, esta linha apagava a fonte que o dono escolhia no painel. */
  font-family: var(--loja-fonte-texto);
  font-size: var(--t-corpo);
  line-height: var(--lh-corpo);
  -webkit-text-size-adjust: 100%;
  /* A ALTURA REAL DA BARRA, DECLARADA NO ELEMENTO CERTO.
     Aqui — e não no :root — porque é ESTE elemento que ganha
     `loja--com-acao`. Declarada no pai, a soma congelava com o slot em 0px.
     +1px = a borda de cima da barra, que também tapa conteúdo.
     Os três consumidores (FAB da Bia, tirinha e o chão do body) continuam
     lendo um token só; agora ele herda do <body> já com o valor da tela. */
  --loja-barra-altura: calc(var(--loja-barra-base) + var(--loja-barra-acao) + 1px);
}

a { color: inherit; }
img { max-width: 100%; height: auto; display: block; }
[hidden] { display: none !important; }

/* FOCO VISÍVEL EM TODA A LOJA. Antes desta regra não existia nenhum: quem
   navega por teclado (ou por controle de TV, que é como parte do público vê
   vitrine) ficava sem saber onde estava. `:focus-visible` e não `:focus` pra
   não desenhar anel no clique de mouse. */
:focus-visible {
  outline: 3px solid var(--loja-cor);
  outline-offset: 2px;
}
/* Sobre o header e a barra o anel usa a tinta local: num topo preto, um anel da
   cor primária pode sumir dentro do próprio fundo.
   `--loja-topo-tinta` e NÃO `currentColor`, que era o que estava escrito aqui e
   funcionava só por acidente. Nos links dá no mesmo (a tinta deles JÁ é esse
   token), mas a CTA da barra tem `color: #fff` — e a barra passou a ler
   `--loja-cor-topo`, que sem chassi escuro é branco. Medido no Chrome a 390×844
   com o botão focado por teclado: anel branco sobre fundo branco, razão 1:1, no
   botão que mais converte da loja, em TODA conta que não ligou o palco (que é o
   default). O token responde claro×escuro sozinho: tinta escura sobre a barra
   branca, branco sobre o palco. */
.loja-topo :focus-visible,
.loja-barra :focus-visible,
/* O RODAPÉ É O TERCEIRO LADO DO PALCO e entrou aqui pelo mesmo motivo dos
   outros dois: com o chassi escuro, um anel na cor da conta (que na Dynamo é
   quase preta) desenhava um retângulo invisível em volta do link do WhatsApp —
   quem navega por teclado perdia a última linha da página. */
.loja-rodape :focus-visible { outline-color: var(--loja-topo-tinta); }

/* Movimento é enfeite; conteúdo não é. Quem pediu menos movimento no sistema
   recebe a loja inteira sem transição — inclusive as das outras frentes, porque
   a regra é global. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 1ms !important;
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    scroll-behavior: auto !important;
  }
}

.loja-wrap { max-width: 1200px; margin: 0 auto; padding: 0 var(--e4); }

.loja-sr {
  position: absolute; width: 1px; height: 1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap;
}
.loja-skip { position: absolute; left: -9999px; }
.loja-skip:focus { left: var(--e2); top: var(--e2); background: var(--loja-fundo); padding: var(--e2); z-index: 60; }

/* LETRA MIÚDA DE APOIO — a frase honesta embaixo do campo, a nota de
   consentimento do "avise-me", o "nada é cobrado automaticamente".
   `.loja-nota` é o nome CANÔNICO. `p.loja-aviso` continua aqui como ponte: a
   classe `.loja-aviso` estava fazendo dois trabalhos opostos nesta folha (letra
   miúda numa regra, grade do bloco de "avise quando chegar" na outra), e o
   seletor de elemento separa os dois sem exigir que esta frente edite as views
   do checkout, que são de outra. Quem mexer no checkout troca por `loja-nota`. */
.loja-nota,
p.loja-aviso {
  color: var(--loja-tinta-fraca);
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
  margin: var(--e2) 0;
}

/* ---- CONTEÚDO QUE TROCA SEM RECARREGAR A PÁGINA ----
   O Turbo põe `aria-busy="true"` no <turbo-frame> sozinho enquanto ele busca; a
   `transition-delay` é o que impede o pisca-pisca de quem responde em 80ms. */
.loja-troca { transition: opacity var(--loja-t); }
.loja-troca[aria-busy="true"] { opacity: .55; transition-delay: 140ms; }
/* A barra de progresso do Turbo nasce azul (#0076ff): a marca de outra pessoa
   atravessando a loja do cliente a cada visita. */
.turbo-progress-bar { height: 3px; background: var(--loja-cor); }
/* Uma CTA primária por tela: com a barra fixa preenchida, o botão inline
   idêntico some — só no celular, porque no desktop a barra não existe. */
@media (max-width: 699px) {
  body.loja--com-acao .loja-acao-repetida { display: none; }
}

/* ============================================
   3. CHASSI — topo, barra, rodapé
   ============================================ */

.loja-topo {
  background: var(--loja-cor-topo);
  color: var(--loja-topo-tinta);
  border-bottom: 1px solid var(--loja-topo-borda);
  position: sticky;
  top: 0;
  z-index: 20;
  /* iPhone com notch: sem isto o header entra por baixo da ilha e a logo fica
     cortada justamente no aparelho em que a loja mais abre. */
  padding-top: env(safe-area-inset-top);
}
/* Modificador para quem quiser o header escuro sem mexer no tema da conta
   (tela de exemplo, prévia do painel). O caminho normal é a chave `cor_topo`. */
.loja-topo--escuro {
  --loja-cor-topo: var(--loja-tinta);
  --loja-topo-tinta: #ffffff;
  --loja-topo-tinta-fraca: rgb(255 255 255 / .72);
  --loja-topo-borda: rgb(255 255 255 / .14);
}
.loja-topo__linha {
  display: flex; align-items: center; justify-content: space-between;
  padding-top: var(--e3); padding-bottom: var(--e3); gap: var(--e3);
}
.loja-topo__marca {
  /* O NOME DA LOJA É TÍTULO, NÃO GRITO: 25/600 com tracking negativo no lugar
     de 20/800. Quem não subiu logo lê a marca aqui, e um wordmark de peso 800
     ao lado de um botão de peso 800 é o empate que fazia nada guiar a leitura. */
  font-size: var(--t-titulo); line-height: var(--lh-titulo);
  letter-spacing: var(--ls-titulo); font-weight: var(--pe-medio); text-decoration: none;
  color: inherit; display: inline-flex; align-items: center;
  min-height: var(--toque);
}
.loja-topo__acoes { display: flex; align-items: center; gap: var(--e2); }
.loja-topo__acao {
  display: inline-flex; align-items: center; justify-content: center;
  gap: var(--e1); min-width: var(--toque); min-height: var(--toque);
  text-decoration: none; color: inherit; font-weight: var(--pe-medio);
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
  border-radius: 999px; padding: 0 var(--e2);
  transition: background-color var(--loja-t);
}
.loja-topo__acao:hover { background: rgb(127 127 127 / .16); }
.loja-topo__carrinho { position: relative; }
/* Os ícones são SVG inline com `stroke: currentColor`: herdam a tinta do
   topo (clara ou escura) sem uma segunda decisão de cor. */
.loja-topo__icone { width: 22px; height: 22px; display: block; flex: none; }

/* O contador. `:empty` some sozinho: o <span> continua no DOM (é alvo estável
   de turbo_stream, id `loja_badge_carrinho`) mesmo com o carrinho vazio. */
/* O CONTADOR VESTE O TOPO, NÃO A AÇÃO. Em vermelho ele era o sétimo bloco
   primário da primeira dobra, e o mais inútil: informa, não convida a clicar.
   Em tinta do topo ele lê igual no header branco e no palco preto, com o
   número na cor do próprio fundo do topo. */
.loja-badge {
  background: var(--loja-topo-tinta); color: var(--loja-cor-topo); border-radius: 999px;
  padding: var(--e1) var(--e2); font-size: var(--t-apoio); font-weight: var(--pe-medio);
  font-variant-numeric: tabular-nums; line-height: var(--lh-apoio);
}
.loja-badge:empty { display: none; }

/* Teto em altura, nunca em largura: logo largo espremido some, logo alto
   empurra o carrinho pra fora da primeira dobra. 32 e não 40 porque o header
   é palco de marca, não outdoor: com 40px a faixa passava de 64px de altura e
   comia a primeira dobra do celular, que é do produto. */
.loja-topo__logo { max-height: 32px; width: auto; object-fit: contain; }

/* A LINHA DA PROMESSA — retirada, entrega, PIX. Fica colada no topo, dentro do
   fluxo: faixa fixa come a primeira dobra do celular, que é onde o produto tem
   que estar. */
.loja-promessa {
  background: var(--loja-superficie);
  border-bottom: 1px solid var(--loja-borda);
  color: var(--loja-tinta-fraca);
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
}
.loja-promessa__linha {
  display: flex; gap: var(--e2); align-items: center; justify-content: center;
  flex-wrap: wrap; padding: var(--e2) 0; margin: 0 auto; text-align: center;
  max-width: 62ch;
}

/* A BARRA DE BAIXO — a única marcação do módulo que existe por causa do
   celular. Sempre renderizada; o desktop a esconde (ver §6). São 200 bytes
   contra um segundo eixo de manutenção. */
.loja-barra {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 30;
  /* A BARRA É A OUTRA PONTA DO PALCO. Ela lê o par do TOPO (--loja-cor-topo /
     --loja-topo-tinta) e não o da página: com o chassi escuro, topo, barra e
     rodapé viram uma faixa só, em vez de um preto em cima e um branco embaixo.
     Na conta que não configurou nada os dois pares valem o mesmo (topo branco,
     borda de sempre), e a barra sai idêntica à de antes desta fatia. */
  background: var(--loja-cor-topo);
  color: var(--loja-topo-tinta);
  border-top: 1px solid var(--loja-topo-borda);
  box-shadow: var(--loja-sombra-2);
  padding-bottom: env(safe-area-inset-bottom);
}
/* O slot de ação (comprar, fechar pedido, total) fica ACIMA dos atalhos: é o
   gesto que a pessoa veio fazer, e o polegar alcança primeiro o que está mais
   perto da borda. */
.loja-barra__acao { padding: var(--e2) var(--e4) 0; }
.loja-barra__acao:empty { display: none; }
.loja-barra__alvos { display: flex; align-items: stretch; }
.loja-barra__item {
  flex: 1 1 0; display: inline-flex; flex-direction: column;
  align-items: center; justify-content: center; gap: var(--e1);
  min-height: var(--loja-barra-base); text-decoration: none; color: var(--loja-topo-tinta-fraca);
  /* O 12/600 em caixa alta vem da combinação canônica do rótulo (§1.3): aqui
     fica só o que é da barra. */
  transition: color var(--loja-t), background-color var(--loja-t);
}
/* Cinza neutro e não --loja-cor-veu (que é a primária diluída no fundo da
   PÁGINA): sobre o palco preto aquele véu é quase branco, e o hover virava um
   bloco claro dentro da barra escura. O mesmo cinza do hover do header. */
.loja-barra__item:hover { background: rgb(127 127 127 / .16); }
/* O ATIVO DEIXA DE SER VERMELHO. Ele marca onde a pessoa está — informação,
   não convite: em --loja-cor ele empatava com a CTA logo acima, na mesma barra.
   Tinta cheia contra tinta fraca já diz "é aqui", e sobra vermelho pro botão. */
.loja-barra__item--ativo { color: var(--loja-topo-tinta); }
.loja-barra__icone { width: 22px; height: 22px; display: block; }

/* O RODAPÉ FECHA O PALCO. Mesmo par do topo e da barra, pelo mesmo motivo — e
   --e9 de ar acima dele: com 32px o rodapé encostava na última fileira de
   cards e parecia mais uma seção do catálogo. Fim de página é fim de assunto. */
.loja-rodape {
  background: var(--loja-cor-topo);
  border-top: 1px solid var(--loja-topo-borda); margin-top: var(--e9);
  padding: var(--e5) 0 var(--e6); color: var(--loja-topo-tinta-fraca);
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
}
.loja-rodape__nome { font-weight: var(--pe-medio); color: var(--loja-topo-tinta); margin: 0 0 var(--e1); }
.loja-rodape__links { display: flex; gap: var(--e4); }
.loja-rodape__links a { min-height: var(--toque); display: inline-flex; align-items: center; }

/* ============================================
   4. COMPONENTES
   ============================================ */

/* ---- flash e cabeçalhos ---- */
.loja-flash { border-radius: var(--loja-raio); padding: var(--e3) var(--e4); margin: var(--e4) 0; font-weight: var(--pe-medio); }
.loja-flash--ok { background: #eaf7ee; color: #1c6b35; }
.loja-flash--erro { background: #fdecec; color: #a12626; }

/* A ABERTURA DA VITRINE — a frase que o DONO escreveu, promovida a primeira
   voz da página. O `.loja-hero` inteiro saiu: o h1 dele repetia a logo palavra
   por palavra a 40px de distância dela (virou `.loja-sr` na view: SEO e leitor
   de tela intactos, zero pixel gasto duas vezes), e o parágrafo — o único texto
   da página que ninguém mais diz — era justamente o que o celular escondia.
   LINHA CURTA, MAS NÃO 20ch — E O NÚMERO SAIU DA RÉGUA, NÃO DO GOSTO. A
   descrição da Dynamo tem 103 caracteres; medida no Chrome a 390×844, em 20ch
   ela imprimia uma coluna de 278px e SEIS linhas de 25/700 — 173px de bloco
   bold logo abaixo do header, que é literalmente o outdoor que o dono mandou
   apagar. Em 26ch a coluna vira 358px no celular (a largura do wrap, ou seja:
   nada é estreitado à força) e QUATRO linhas, 115px; no desktop ela ainda
   fecha em ~450px, que é o que mantém isto lendo como abertura e não como
   parágrafo de catálogo. Com a banda de margem uma faixa abaixo (--e5/--e4, e
   não --e7/--e5), o primeiro card da vitrine sobe de y=652 para y=562.
   ESSE AJUSTE FICOU CURTO: 115px ainda é um bloco de negrito de 25px no topo
   da vitrine. A regra abaixo vai além e separa os dois casos — frase curta é
   abertura, texto longo é texto. */
/* A FAIXA DE PROCURA: busca e filtro na mesma linha.
   São a mesma pergunta ("como eu acho o que quero?") e estavam empilhadas em
   duas faixas de 44px+48px. Juntas, o primeiro produto sobe ~60px no celular.
   O campo manda no espaço; o gatilho ocupa o que precisa e não encolhe. */
.loja-procura {
  display: flex;
  align-items: center;
  gap: var(--e3);
  flex-wrap: wrap;
}
/* `flex: 1 1 auto` + `min-width`, nunca flex-basis em px: dentro de um
   container em coluna o basis vira ALTURA, e a folha tem teste proibindo. */
.loja-procura .loja-busca { flex: 1 1 auto; min-width: 180px; }
/* O gatilho da folha é o `div[data-controller=loja-folha]` que o partial
   renderiza — ele não pode esticar nem encolher. */
.loja-procura > [data-controller="loja-folha"] { flex: 0 0 auto; }

/* A ABERTURA TEM DOIS TAMANHOS, E QUEM ESCOLHE É O TEXTO DO DONO.
   Esta regra nasceu como headline de 25px/700 com 26ch — e ficou linda com a
   frase curta que o desenho imaginou. Só que o campo por trás dela é a
   DESCRIÇÃO da loja, texto livre que o dono escreve no painel: a da Dynamo tem
   duas frases, e em 25px/700 com 26ch elas viraram SEIS LINHAS de texto grande
   que empurraram o primeiro produto de y=366 para y=652 — pior do que estava
   antes do redesenho, e exatamente a "letra grandona" que ele reclamou.
   Frase curta continua sendo abertura; texto longo é texto e se comporta como
   tal. Quem decide é a view (LojaHelper#classe_da_abertura), contando
   caracteres — não um `if` de estilo escondido aqui. */
.loja-abertura {
  margin: var(--e5) 0 var(--e4);
  max-width: 26ch;
  font-family: var(--loja-fonte-titulo);
  font-size: var(--t-titulo); line-height: var(--lh-titulo);
  letter-spacing: var(--ls-titulo); font-weight: var(--pe-forte);
}

.loja-abertura--longa {
  max-width: 52ch;
  font-family: var(--loja-fonte-texto);
  font-size: var(--t-corpo); line-height: var(--lh-corpo);
  letter-spacing: 0; font-weight: var(--pe-normal);
  color: var(--loja-tinta-fraca);
}

/* No celular ela para em três linhas: o resto está no rodapé, e a vitrine tem
   um assunto só — o produto. */
@media (max-width: 699px) {
  .loja-abertura--longa {
    display: -webkit-box; -webkit-line-clamp: 3; -webkit-box-orient: vertical;
    overflow: hidden;
  }
}

.loja-resultado, .loja-vazio, .loja-voltar { color: var(--loja-tinta-fraca); }
/* O CAMINHO DE VOLTA É APOIO, NÃO ABERTURA. Ele é a PRIMEIRA linha da ficha, e
   em 16px herdados do corpo era o primeiro texto que o olho pegava numa tela
   cujo assunto é a foto e o preço. Um degrau abaixo, em peso médio, ele
   continua achável e continua com o alvo de 44px — só para de disputar a
   entrada da página com o nome do produto. */
.loja-voltar {
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
  font-weight: var(--pe-medio); margin: var(--e4) 0 0;
}
.loja-voltar a { min-height: var(--toque); display: inline-flex; align-items: center; }

/* `.loja-secao` MORREU AQUI, e com ela o empate que a folha carregava: a mesma
   classe era h1 do checkout, h2 da vitrine e <p> da folha de filtros, então
   "Ordenar por" imprimia do tamanho do h1 do carrinho. O papel de cada uma é
   diferente e agora o degrau também é.

   O TÍTULO é o h1 das telas internas (carrinho, checkout, "não achei"): o mesmo
   degrau da abertura da vitrine, porque é a mesma função — o nome da tela.

   `.loja-ok h1` ENTRA AQUI PELO ELEMENTO, e não por uma classe na view, porque
   o defeito era do elemento: aquele h1 nunca teve `font-size` nenhum na folha e
   saía no default do agente de usuário — 32px no Chrome e 24px no Safari, que
   aplica a escala de h1 aninhado por ele estar dentro de um <section>. A tela
   mais importante do funil imprimia tamanho diferente conforme o aparelho do
   cliente, e ninguém decidiu isso. Preso ao seletor, ele não volta a depender
   de alguém lembrar da classe. */
.loja-titulo,
.loja-ok h1 {
  font-family: var(--loja-fonte-titulo);
  font-size: var(--t-titulo); line-height: var(--lh-titulo);
  letter-spacing: var(--ls-titulo); font-weight: var(--pe-forte);
  margin: var(--e5) 0 var(--e3);
}

/* O RÓTULO DE SEÇÃO é o outro lado: "Em oferta hoje" e "Categoria" não são
   títulos de tela, são etiquetas de prateleira. Em 25px eles competiam com o
   produto; em 12 CAPS com o filete correndo até a borda do wrap, eles DIVIDEM a
   página sem gritar — que é o trabalho que o tamanho estava tentando fazer.

   --e8 acima e --e3 abaixo, e essa assimetria é a regra toda: o rótulo tem que
   estar mais perto do que ele nomeia do que do que veio antes. Empatados em
   24/12 como estavam, a distância que separava dois ASSUNTOS era quase a mesma
   que separava o rótulo do próprio conteúdo, e nada agrupava nada.

   O 12/600 em caixa alta não está escrito aqui: ele é a combinação canônica do
   rótulo, e mora uma vez só na §1.3 com os outros cinco componentes que a
   copiavam. O que sobra nesta regra é o que é DELA — o filete e a assimetria. */
.loja-rotulo {
  color: var(--loja-tinta-fraca);
  border-bottom: 1px solid var(--loja-filete);
  padding-bottom: var(--e2);
  margin: var(--e8) 0 var(--e3);
}

/* ---- CHIPS: a navegação de categoria que substitui o <select> de 22 opções.
   Rolagem horizontal com scroll-snap — no celular, roleta nativa com 22 itens é
   onde a pessoa desiste de filtrar.

   OS NOMES NÃO MUDAM, O DESENHO SIM. `.loja-chips`, `.loja-chip` e
   `.loja-chip--ativo` são assert_select em três testes de integração e são
   contrato com as outras frentes; o que muda aqui é só a pintura — a pílula
   vira ABA. Vinte e duas pílulas de borda, fundo e raio 999px eram vinte e dois
   retângulos desenhados acima da primeira fileira de produtos, e a ativa,
   pintada de --loja-cor, era o maior bloco vermelho da vitrine competindo com o
   botão de comprar de cada card. Aba não tem caixa: tem tinta e um traço. ---- */
.loja-chips {
  display: flex; gap: var(--e2); margin: var(--e3) 0;
  overflow-x: auto; scroll-snap-type: x proximity;
  padding-bottom: var(--e1);
  /* A BARRA DE ROLAGEM SOME E O DEGRADÊ ENTRA NO LUGAR DELA. Os 10px de faixa
     cinza sob os chips liam-se como elemento quebrado, e mesmo assim nada
     dizia que a fileira continua fora da tela. O corte suave na direita diz —
     é a affordance que a barra nunca foi. */
  scrollbar-width: none;
  /* Prefixo junto: iOS 15.3 e Android velho (que é metade do público desta
     loja) só entendem o `-webkit-`. Sem ele não quebra nada — só não desbota. */
  -webkit-mask-image: linear-gradient(90deg, #000 88%, transparent);
  mask-image: linear-gradient(90deg, #000 88%, transparent);
}
.loja-chips::-webkit-scrollbar { display: none; }
.loja-chip {
  scroll-snap-align: start; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  /* O ALVO CONTINUA INTEIRO: 44px de altura sem caixa desenhada em volta. Aba
     de texto que vira alvo de 20px é a troca que não se faz — o dedo não sabe
     que a borda sumiu. O padding cai de --e4 pra --e3 porque sem fundo os 16px
     viravam buraco entre as palavras em vez de respiro dentro da pílula. */
  min-height: var(--toque); padding: 0 var(--e3);
  border: 0; background: none; color: var(--loja-tinta-fraca);
  font: inherit; font-size: var(--t-apoio); line-height: var(--lh-apoio);
  font-weight: var(--pe-medio); text-decoration: none;
  white-space: nowrap; cursor: pointer;
  transition: color var(--loja-t), box-shadow var(--loja-t);
}
@media (hover: hover) {
  .loja-chip:hover { color: var(--loja-tinta); }
}
/* O TRAÇO DE 2px É O ÚNICO VERMELHO DA VITRINE — e cabe no orçamento porque
   `box-shadow` não é fundo: são ~40x2px de tinta contra os ~90x44 de bloco que
   a pílula ativa pintava. A tinta cheia contra a fraca já diz "é aqui" sozinha;
   o traço é o que sobrevive ao daltônico e à foto da tela no WhatsApp. */
.loja-chip--ativo {
  color: var(--loja-tinta); box-shadow: inset 0 -2px 0 var(--loja-cor);
}

/* ---- GRADE E CARD ---- */

/* Duas colunas já no celular: card grande demais esconde o segundo produto e
   faz a pessoa achar que a loja tem um item só.

   COLUNAS COLADAS, FILEIRAS LONGE — e é por isso que o gap deixou de ser um
   número só. Com 12px nos dois eixos, a distância entre dois produtos DA MESMA
   fileira era igual à distância entre duas fileiras, e a grade lia como uma
   massa de cards em vez de linhas de prateleira. O eixo vertical é o que separa
   um grupo do outro; o horizontal só precisa impedir que dois cards se toquem. */
.loja-grade {
  display: grid; grid-template-columns: repeat(2, 1fr);
  column-gap: var(--e4); row-gap: var(--e7);
}

/* O CARD DEIXOU DE SER CAIXA. A borda de 1px fechava um retângulo por produto —
   24 por tela — e o que o olho lia era a GRADE, não o que está à venda. Quem
   tem forma agora é a PLACA da foto; o texto vive solto no papel, como legenda
   de catálogo. Sobra aqui só o que é estrutura. */
.loja-card {
  display: flex; flex-direction: column;
  /* O conteúdo do card não pode empurrar a coluna: nome longo e formulário de
     "avise-me" já estouraram a grade horizontalmente uma vez. */
  min-width: 0;
}
/* HOVER SÓ ONDE HÁ PONTEIRO DE VERDADE — no celular o `:hover` gruda depois do
   toque e o card fica aceso até a pessoa tocar em outro lugar.
   O CARD NÃO GANHA MAIS BORDA VERMELHA: a cor de ação pintando o contorno de 24
   retângulos conforme o mouse passeia é justamente o vermelho como mobília. O
   que se move é a FOTO, dentro da placa, e o nome sublinha (é ele o link). A
   placa não muda de cor: ela é o chão fixo que faz 652 fotos de origens
   diferentes lerem como uma coleção — piscar esse chão desfaz o efeito. */
@media (hover: hover) {
  .loja-card__foto img { transition: transform var(--loja-t-lenta); }
  .loja-card:hover .loja-card__foto img { transform: scale(1.03); }
  .loja-card:hover .loja-card__nome { text-decoration: underline; }
}
.loja-card__link { text-decoration: none; color: inherit; display: block; }
/* O CORPO DO CARD — todo o texto e o botão, separado da placa porque são duas
   coisas de naturezas diferentes: a placa é uma superfície fechada, o corpo é
   texto correndo no papel da página. `flex: 1` é o que mantém o botão colado no
   chão de TODOS os cards da fileira (o `margin-top: auto` do `.loja-card__form`
   precisa de um pai com altura). Ele é filho DIRETO do `.loja-card` — dentro de
   um <a>, o auto-margin perde a referência. */
.loja-card__corpo {
  display: flex; flex-direction: column; gap: var(--e1);
  flex: 1 1 auto; min-width: 0;
  /* SEM RESPIRO LATERAL: sem a borda do card, o texto alinha com a borda da
     PLACA — é isso que o faz ler como legenda embaixo da foto em vez de
     conteúdo dentro de uma caixa. Em cima fica o único espaço que separa a
     placa do texto, porque os `margin-top` que cada filho carregava foram
     apagados (ver abaixo). */
  padding: var(--e3) 0 0;
}

/* ---- A PLACA DA FOTO ----
   A correção mais barata do repositório: 652 fotos raspadas de fornecedores
   diferentes (fundo branco, fundo transparente, PNG recortado, JPEG com moldura
   impressa) passam a ocupar o MESMO miolo, com o mesmo respiro em volta, sobre
   o mesmo chão. Nenhum arquivo de imagem é tocado e nenhum job reprocessa nada
   — o que faz a fileira parecer uma coleção é o enquadramento idêntico.
   O CHÃO É BRANCO FIXO (--loja-foto) e não temático: a maioria dessas fotos é
   JPEG de fundo branco, e sobre o cinza de --loja-superficie cada uma desenhava
   um retângulo branco visível dentro da placa. Numa loja de chassi escuro a
   placa continua branca de propósito — ela é o lightbox do produto. */
.loja-card__foto,
.loja-produto__foto {
  aspect-ratio: 1 / 1; background: var(--loja-foto);
  border: 1px solid var(--loja-filete); border-radius: var(--loja-raio);
  /* O RESPIRO INTERNO É O TRUQUE INTEIRO: nenhuma foto encosta na borda, então
     a de fundo transparente e a de moldura branca terminam na mesma altura. */
  padding: var(--foto-respiro);
  display: flex; align-items: center; justify-content: center; overflow: hidden;
  /* Âncora do selo: sem `relative` aqui, o `-X%` posicionado escapa pro canto
     da PÁGINA. */
  position: relative;
}
/* A placa da ficha tem o dobro do respiro porque ela tem o dobro do tamanho: o
   que se conserva entre as duas telas é a PROPORÇÃO do ar, não o número. */
.loja-produto__foto { padding: var(--foto-respiro-ficha); }
/* `contain` e não `cover`: em suplemento o rótulo é o que identifica sabor e
   tamanho, e recorte de quadrado corta justamente o rótulo. */
.loja-card__foto img,
.loja-produto__foto img { width: 100%; height: 100%; object-fit: contain; }
.loja-card__sem-foto {
  font-weight: var(--pe-medio); color: var(--loja-tinta-fraca);
  font-size: var(--t-num); line-height: var(--lh-num);
}

/* OS MARGIN DOS FILHOS DO CORPO SUMIRAM, TODOS. O card tinha dois sistemas de
   ritmo brigando — o `gap` do flex e um `margin-top` por filho — e a distância
   entre duas linhas era a maior dos dois, o que mudava de linha pra linha. Um
   sistema só: quem espaça aqui dentro é o `gap` do `.loja-card__corpo`.
   O `margin: 0` também apaga o do agente de usuário: `.loja-card__marca`,
   `__sabor`, `__economia` e a linha de preço são <p>, e o 1em de cima e de
   baixo que o navegador dá a eles é espaçamento que ninguém escolheu.

   Marca em caixa alta miúda — o peso de marca vindo de tipografia, não de fonte
   remota. O degrau e a caixa alta são a combinação canônica do rótulo (§1.3);
   aqui sobra a tinta fraca, que é o que põe a marca ABAIXO do nome do produto. */
.loja-card__marca { margin: 0; color: var(--loja-tinta-fraca); }
/* EXATAMENTE duas linhas. Sem o clamp, um nome de três linhas empurra o preço
   e o botão do card vizinho pra baixo e a grade fica dentada. */
.loja-card__nome {
  /* O IDENTIFICADOR VOLTA AO TOPO DA HIERARQUIA: 16/600 em tinta cheia. Em
     13/500 o nome perdia em mancha pro preço e pro botão, e numa fileira de
     nove "TASTY WHEY 3W 900G" era justamente o nome que a pessoa procurava. */
  font-size: var(--t-corpo); margin: 0; font-weight: var(--pe-medio); line-height: 1.35;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical;
  overflow: hidden; min-height: 2.7em;
}
/* O SABOR EM LINHA PRÓPRIA. 336 dos 652 produtos trazem o sabor no fim do nome,
   em caixa alta, exatamente onde o clamp de duas linhas corta — e nove cards de
   "TASTY WHEY 3W 900G" com a mesma foto liam-se como catálogo duplicado. Um
   degrau ABAIXO do nome e em tinta fraca: é qualificador, não título.
   Publicada aqui a pedido da fatia do card, que a usava emprestando
   `.loja-nota` — cuja margem de bloco não cabe dentro do card. */
.loja-card__sabor {
  margin: 0; font-size: var(--t-apoio); line-height: var(--lh-apoio);
  font-weight: var(--pe-normal); color: var(--loja-tinta-fraca);
}
/* O `margin-top` daqui era LETRA MORTA e por isso enganava quem lesse a regra:
   o preço é um <span> em linha dentro de um flex de baseline, e margem vertical
   de caixa em linha não desloca nada. */
.loja-card__preco {
  margin: 0; font-weight: var(--pe-forte);
  font-size: var(--t-num); line-height: var(--lh-num);
  font-variant-numeric: tabular-nums;
}
/* Preço em promoção é o único texto corrido em vermelho da loja: é o gatilho,
   e ele só aparece quando há promoção de verdade. */
.loja-card--promo .loja-card__preco,
.loja-card__preco--promo { color: var(--loja-cor); }
/* O "de R$" é secundário DE PROPÓSITO: o número que decide a compra é o que se
   paga hoje. Preço riscado do mesmo tamanho do preço vigente confunde. */
.loja-card__preco-de,
.loja-produto__preco-de {
  color: var(--loja-tinta-fraca); font-size: var(--t-apoio); line-height: var(--lh-apoio);
  font-weight: var(--pe-normal);
  text-decoration: line-through; margin: 0; font-variant-numeric: tabular-nums;
}
.loja-card__economia {
  /* Sai da cor de ação DE PROPÓSITO: a informação aqui é o NÚMERO, e a cor de
     ação em três lugares no mesmo card deixa de significar "aperte aqui". */
  margin: 0; font-size: var(--t-apoio); line-height: var(--lh-apoio);
  font-weight: var(--pe-medio); color: var(--loja-tinta);
  font-variant-numeric: tabular-nums;
}
/* A LINHA DE PREÇO é <p>: sem este `margin: 0` ela carrega o 1em do navegador e
   abre 16px acima e abaixo do número que decide a compra — o buraco de ar que
   fazia o card inteiro parecer maior do que precisa. */
.loja-preco-linha { display: flex; align-items: baseline; flex-wrap: wrap; gap: var(--e2); margin: 0; }
.loja-card__form { margin-top: auto; padding-top: var(--e2); }
/* O CARD NÃO PODE PULAR NO CLIQUE. "Adicionar" virando um rótulo de duas linhas
   media 62px: o card crescia 14px e a fileira inteira refluía no gesto mais
   importante da loja. Uma linha, altura travada — e travada no DEGRAU DE TOQUE:
   44px é o piso do dedo e o teto deste botão, porque a CTA cheia de 52px é uma
   por tela e ela não é esta (é a da ficha e a da barra).
   O seletor é do CARD e não do `__form` de propósito: o "Avise-me" do esgotado
   mora fora do form e o "✓ Adicionado" que volta do turbo_stream troca o form
   inteiro — os três precisam da MESMA altura, senão a fileira reflui no gesto. */
.loja-card .loja-btn { white-space: nowrap; min-height: var(--toque); }

/* O "+" É IRMÃO DO BOTÃO, NÃO FILHO — e a razão é dura: o botão é um
   `<input type="submit">`, que não tem filho nenhum. Trocar por <button> pra
   caber o ícone custaria o `input[type=submit]` que o teste do card crava e o
   `submit_tag` que o plano manda manter, e o rótulo escrito ("Adicionar") é
   contrato — o card perde a cor, nunca a palavra. Então o glifo é absoluto
   dentro do <form>, que tem exatamente a caixa do botão, com `pointer-events`
   desligado pra devolver o alvo de 44px inteiro ao dedo.
   `top`/`bottom: 0` com `margin: auto 0` centra na vertical sem px cru. */
.loja-card__form form { position: relative; }
.loja-card__mais {
  position: absolute; left: var(--e3); top: 0; bottom: 0; margin: auto 0;
  pointer-events: none;
}

/* ESGOTADO. O item continua na prateleira pelo SEO e pelo sinal de demanda
   (são 390 pedidos de aviso), mas não pode competir com o que dá pra comprar
   agora — daí a foto esmaecida e o selo cinza.
   O QUE ESMAECE É O CONTEÚDO DA PLACA, NUNCA A PLACA. `opacity` num pai cria
   grupo, e o selo mora dentro da foto desde que virou etiqueta: esmaecendo o
   container inteiro, "Esgotado" saía a 2,2:1 (branco sobre o cinza já lavado
   pelos .55) — e desde que o card parou de repetir a palavra abaixo do preço,
   essa etiqueta é o ÚNICO lugar que diz que o produto acabou. Listando os
   filhos, a foto continua apagada e o selo volta aos 5,3:1 de sempre. */
.loja-card--esgotado .loja-card__foto img,
.loja-card--esgotado .loja-card__sem-foto,
.loja-card--esgotado .loja-card__nome { opacity: .55; }
.loja-card__esgotado,
.loja-produto__esgotado {
  font-weight: var(--pe-medio); color: var(--loja-tinta-fraca); margin: var(--e2) 0;
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
}

/* Escassez só quando ela é VERDADE (1..3 no estoque, ver Product). Frase de
   urgência permanente é a que ninguém acredita mais. */
.loja-card__ultimas,
.loja-produto__ultimas {
  color: var(--loja-destaque); font-size: var(--t-apoio); line-height: var(--lh-apoio);
  font-weight: var(--pe-medio); margin: 0;
}

/* ---- SELO ----
   O selo é ABSOLUTO dentro da foto, nunca no fluxo: no fluxo ele empurrava o
   nome do produto pra baixo e desalinhava a grade inteira. O posicionamento
   está preso ao container da foto (e não no `.loja-selo` solto) pra que um selo
   usado em linha, um dia, continue sendo um selo em linha.
   O DISCO DE 48px MORREU. Ele era o segundo maior bloco de cor da vitrine —
   dois por fileira, do tamanho do botão de comprar — e sobre a placa branca
   virava o assunto do card, no lugar do produto. Etiqueta miúda: a informação
   continua legível de longe (é o único caixa alta dentro da placa) e ocupa a
   área de um selo, não a de um botão.
   ELA DESCE PRO PÉ DA PLACA porque o alto da foto é onde mora o rótulo do
   produto na maioria das 652 imagens — o disco cobria justamente a marca que a
   pessoa procura. Embaixo, ele cai no respiro da placa, sobre branco.
   A CAIXA ALTA VOLTA, pelo degrau --t-rotulo, que é o único da folha que a
   carrega — e ela não está escrita aqui: o selo é um dos seis componentes da
   combinação canônica do rótulo (§1.3), a ÚNICA regra da folha que escreve
   `text-transform`. Nesta sobra o que é do selo: o fundo de desconto e a forma
   de etiqueta. */
.loja-selo {
  display: inline-flex; align-items: center; justify-content: center;
  background: var(--loja-destaque); color: #fff; border-radius: var(--loja-raio-campo);
  padding: var(--e1) var(--e2); white-space: nowrap;
}
.loja-card__foto > .loja-selo,
.loja-produto__foto > .loja-selo {
  position: absolute; bottom: var(--e2); left: var(--e2); z-index: 1;
}
/* "-25%" é número: tabular pra que dois selos vizinhos na mesma fileira tenham
   a mesma largura de dígito e a etiqueta não dance de card pra card. */
.loja-selo--pct { font-variant-numeric: tabular-nums; }
.loja-selo--cinza { background: var(--loja-tinta-fraca); }

/* ---- PÁGINA DO PRODUTO ---- */
.loja-produto { display: grid; gap: var(--e4); margin: var(--e4) 0; }
.loja-produto h1 {
  font-family: var(--loja-fonte-titulo);
  font-size: var(--t-titulo); line-height: var(--lh-titulo);
  letter-spacing: var(--ls-titulo); font-weight: var(--pe-forte);
  margin: 0 0 var(--e2);
}

/* ---- O RÓTULO MIÚDO DA FICHA: o SKU e as duas portas de procedência ----
   O SKU vivia EMPRESTANDO `.loja-card__marca`. O comentário da view pedia uma
   regra própria e ela nasce aqui: enquanto a classe era a do card, qualquer
   mexida no card mudava a ficha, e nada na folha dizia que as duas telas
   estavam presas uma na outra. Os dois passaram a dividir o degrau pelo lugar
   certo — a combinação canônica do rótulo, na §1.3 —, e não por empréstimo de
   classe de outra tela.
   OS DOIS LINKS DE META continuam `.loja-chip` (é assert_select em teste de
   integração, e nome de classe é contrato), mas aqui eles NÃO são aba de
   navegação: em 14/600 na mesma tela de um h1 de 25 eles liam como um segundo
   título logo acima do nome do produto. Em 12 CAPS de tinta fraca voltam a ser
   o que são — a procedência —, com o alvo de 44px que o `.loja-chip` já dá. */
.loja-produto__sku,
.loja-produto__meta .loja-chip { color: var(--loja-tinta-fraca); }
/* Margem zero, e o espaço quem dá é o filete do dinheiro, logo abaixo: o <p>
   carrega 1em do navegador em cima e embaixo, e era esse ar não escolhido que
   soltava o SKU do nome a que ele pertence. */
.loja-produto__sku { margin: 0; }
/* O PADDING LATERAL DA ABA SAI. `.loja-chip` reserva --e3 de cada lado porque
   na vitrine ele é alvo numa fileira rolante; aqui isso recuava a linha de
   meta 12px em relação ao h1, e a coluna de decisão passava a ter duas bordas
   esquerdas. O alvo continua inteiro pela altura. */
.loja-produto__meta { display: flex; flex-wrap: wrap; gap: var(--e3); margin: 0; }
.loja-produto__meta .loja-chip { padding: 0; }
@media (hover: hover) {
  /* Sem caixa, sem fundo e sem cor de ação, o sublinhado é a única affordance
     que sobra pra dizer que estes dois rótulos são porta. */
  .loja-produto__meta .loja-chip:hover { text-decoration: underline; }
}

/* ---- O FILETE ENTRE IDENTIDADE E DINHEIRO ----
   Até aqui a ficha era uma pilha só — marca, nome, SKU, riscado e preço com o
   mesmo ar entre eles — e o olho não tinha onde parar antes do número que
   decide. Um fio de 1px separa "o que é isto" de "quanto custa" sem gastar uma
   caixa cinza, que é justamente o que sai do bloco de entrega logo abaixo.
   O FIO MORA NO PRIMEIRO ELEMENTO DO DINHEIRO, e qual é ele muda com a
   promoção: com oferta o bloco abre pelo riscado, sem oferta abre pelo preço.
   Por isso os dois recebem a regra e o preço a devolve quando vem DEPOIS do
   riscado — senão a ficha em promoção desenharia duas linhas, uma delas no
   meio do número. */
.loja-produto__preco {
  /* O DISPLAY, num dos seus dois únicos usos na loja: 31 contra o h1 de 25.
     O preço é o maior número da tela porque é ele que decide — em 24 contra 22
     a razão era 1,09×, ou seja, nada guiava. */
  font-family: var(--loja-fonte-titulo);
  font-size: var(--t-display); line-height: var(--lh-display);
  letter-spacing: var(--ls-display); font-weight: var(--pe-forte);
  margin: var(--e5) 0 var(--e3);
  padding-top: var(--e4);
  border-top: 1px solid var(--loja-filete);
  font-variant-numeric: tabular-nums;
}
/* Escopo na ficha: `.loja-preco-linha` também é a linha de preço do card, e lá
   ela não separa assunto nenhum — é a última linha do corpo. */
.loja-produto__dados .loja-preco-linha {
  margin-top: var(--e5); padding-top: var(--e4);
  border-top: 1px solid var(--loja-filete);
}
.loja-preco-linha + .loja-produto__preco {
  /* --e1 e não zero: o riscado é o "antes" do mesmo número, e colados eles se
     leem como um preço de duas linhas. */
  margin-top: var(--e1); padding-top: 0; border-top: 0;
}

.loja-produto__descricao { color: var(--loja-tinta-fraca); max-width: 62ch; }
.loja-produto__form { display: flex; flex-wrap: wrap; align-items: center; gap: var(--e2); margin: var(--e4) 0; }
/* A PÍLULA DO − n + É CONTROLE DE CAMPO, e sai de --alt-campo, não do piso de
   toque: --toque é o mínimo que o dedo exige (e continua sendo a altura dos
   botões DENTRO dela), --alt-campo é o degrau que o <input> e o secundário
   desta loja usam. Escopo na ficha de propósito: a mesma pílula mora na linha
   de item do carrinho, onde a altura é decisão da fatia do carrinho. */
.loja-produto__form .loja-qtd { min-height: var(--alt-campo); }

/* "ENTREGA E RETIRADA" PERDE A CAIXA. Era o único bloco cinza com borda e raio
   da ficha, e caixa é justamente o que esta folha deixou de usar pra agrupar:
   ele empatava em peso visual com a placa da foto e roubava o olho do preço.
   O agrupamento passa a ser o mesmo do resto da página — `.loja-rotulo` com o
   filete correndo até a borda — e o texto fica em CORPO cheio, não em apoio:
   isto se lê pra decidir entre buscar e receber. */
.loja-produto__entrega {
  margin: 0;
  font-size: var(--t-corpo); line-height: var(--lh-corpo);
}
/* O 1em do navegador em cada <p> abria 16px entre duas frases que são a mesma
   resposta. E vale também pro `.loja-nota` de dentro, que traz margem de bloco
   pensada pra fora daqui. */
.loja-produto__entrega p { margin: 0 0 var(--e2); }

/* Trilho horizontal dos relacionados: no celular, quatro cards em grade empurram
   o rodapé pra longe; em trilho eles cabem num gesto.
   62% E NÃO 46%: com 46 cabiam dois cards inteiros e um fiapo do terceiro, e o
   trilho lia como uma fileira da grade que alguém cortou — ninguém arrastava.
   Com 62 sobra um card e um pedaço largo do seguinte, que é a única coisa que
   diz "isto rola". No desktop continua em 24%, onde há largura pra quatro. */
.loja-trilho {
  display: grid; grid-auto-flow: column; grid-auto-columns: 62%;
  gap: var(--e3); overflow-x: auto; scroll-snap-type: x proximity;
  padding-bottom: var(--e2);
}
.loja-trilho > * { scroll-snap-align: start; }

/* ---- BOTÕES ----
   A ALTURA SAI DE TOKEN (--alt-cta), porque isto é dedo em celular e porque a
   barra fixa soma essa altura pra saber quanto chão reservar.
   O UPPERCASE MORRE AQUI. Caixa alta em botão é rótulo gritado, e ela estava em
   seis componentes ao mesmo tempo — quando tudo grita, nada é ouvido. Sobra
   caixa alta num degrau só, o --t-rotulo. ---- */
.loja-btn {
  font-family: inherit;
  font-size: var(--t-corpo); line-height: 1.2;
  font-weight: var(--pe-medio); cursor: pointer;
  text-transform: none; letter-spacing: 0;
  border: 1px solid var(--loja-cor); border-radius: var(--loja-raio);
  background: var(--loja-cor); color: #fff;
  padding: var(--e3) var(--e4); min-height: var(--alt-cta); text-decoration: none;
  display: inline-flex; align-items: center; justify-content: center; text-align: center;
  transition: background-color var(--loja-t), border-color var(--loja-t), transform var(--loja-t);
}
@media (hover: hover) {
  .loja-btn:hover { background: var(--loja-cor-forte); border-color: var(--loja-cor-forte); }
  /* DEPOIS do `.loja-btn:hover` porque empatam em especificidade (0,2,0) e quem
     vence é a ordem; e dentro do `hover: hover` porque no celular o estado
     grudava depois do toque — o card ficava com o botão "aceso" até a pessoa
     tocar em outro lugar. */
  .loja-btn--fraco:hover { background: var(--loja-superficie); border-color: var(--loja-tinta-fraca); }
}
.loja-btn:active { transform: translateY(1px); }
.loja-btn[disabled] { opacity: .5; cursor: default; }
.loja-btn--cheio { width: 100%; }
/* O SECUNDÁRIO PERDEU O VERMELHO, e é esta linha que apaga cinco blocos
   primários da primeira dobra do celular sem tocar em nenhuma view: eram branco
   com texto e borda na cor de ação, então "Adicionar", "Avise-me", "Atualizar",
   "Aplicar" e a paginação disputavam atenção com a CTA de verdade. Agora ele é
   papel, tinta e o fio do campo — quem for primário na tela é o único com
   fundo. */
.loja-btn--fraco {
  background: var(--loja-fundo); color: var(--loja-tinta);
  border-color: var(--loja-borda);
}
/* CONTROLE FINO — o gatilho da folha de filtros, que mora na mesma linha das
   abas de categoria. Ele NÃO pode virar aba (deixaria de parecer coisa que se
   aperta e viraria a 23ª categoria), mas também não pode ter a altura de uma
   CTA ao lado de uma fileira de 44px: o degrau de toque é o que empata os dois
   pelo alto e mantém a linha lendo como uma linha só. O texto acompanha o das
   abas — mesmo degrau, mesmo peso — porque é vizinho delas, não do checkout. */
.loja-btn--fino {
  min-height: var(--toque); padding: 0 var(--e4);
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
}
/* "✓ No carrinho": o estado de confirmação do card, depois do turbo_stream.
   Continua sendo botão (leva ao carrinho), só muda de peso visual. */
.loja-btn--ok {
  background: var(--loja-superficie); color: var(--loja-tinta);
  border-color: var(--loja-borda);
}
.loja-btn--ok:hover { background: var(--loja-superficie); border-color: var(--loja-tinta-fraca); }
/* Botão-texto (remover, voltar): não é caixa alta — é leitura, não gesto de
   marca. */
.loja-btn--texto {
  background: none; border: none; color: var(--loja-tinta-fraca);
  text-decoration: underline; text-transform: none; letter-spacing: 0;
  font-weight: var(--pe-medio); padding: var(--e2); min-height: var(--toque);
}
.loja-btn--texto:hover { background: none; color: var(--loja-tinta); }
/* O BOTÃO DO WHATSAPP É VERDE, e não da cor da conta. Ele leva pra FORA, pra
   marca de outra empresa: vestido de --loja-cor, ele gastava o único primário
   da tela num gesto que não é a compra — e na confirmação o pedido já foi
   feito, então aquele vermelho convidava de novo pra coisa nenhuma. O verde é
   marca de terceiro (legado permitido, como o flash e o selo de erro), nunca a
   cor da conta.
   Vale nos dois lugares em que este botão aparece: a saída da conversa da Bia
   (freio estourado) e a confirmação do pedido. Era uma regra de escopo
   (`.loja-chat__saida .loja-btn`) e virou modificador porque a confirmação não
   tem como emprestar o escopo do chat.
   DEPOIS do `.loja-btn`: as duas empatam em especificidade (0,1,0) e quem
   decide é a ordem do arquivo. */
.loja-btn--wa { background: #128c7e; border-color: #128c7e; }
@media (hover: hover) {
  /* No celular o :hover gruda depois do toque, e o botão fica aceso até a
     pessoa tocar em outro lugar — o mesmo motivo das outras regras de hover. */
  .loja-btn--wa:hover { background: #0d6d62; border-color: #0d6d62; }
}

/* "Perguntar no WhatsApp" É LINK, NÃO BOTÃO — e essa é a diferença entre uma
   CTA e uma saída. A ficha já tem a sua ação primária (uma por tela); sem
   sublinhado, em peso médio e com alvo de 44px, este aqui lia como um botão a
   que faltou o fundo. Sublinhado, ele é o que é. */
.loja-link-wa {
  font-weight: var(--pe-medio); text-decoration: underline;
  display: inline-flex; align-items: center; min-height: var(--toque);
}

/* ---- − n + ----
   Um controle, dois modos: com JS ele salva sozinho (debounce), sem JS o form
   de sempre continua ali com o botão de enviar. ---- */
.loja-qtd {
  display: inline-flex; align-items: center;
  border: 1px solid var(--loja-borda); border-radius: 999px;
  background: var(--loja-fundo); overflow: hidden;
}
.loja-qtd__btn {
  min-width: var(--toque); min-height: var(--toque);
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer;
  /* O GLIFO PERDE O PESO: 20/400. O − e o + são controle, não número que
     decide — quem decide é o valor entre eles. */
  font: inherit; font-size: var(--t-num); line-height: var(--lh-num);
  font-weight: var(--pe-normal); color: inherit;
  transition: background-color var(--loja-t);
}
.loja-qtd__btn:hover { background: var(--loja-superficie); }
.loja-qtd .loja-input,
.loja-qtd__valor {
  width: 46px; min-width: 0; text-align: center; border: 0; border-radius: 0;
  background: none; font-weight: var(--pe-medio); font-variant-numeric: tabular-nums;
  min-height: var(--toque); padding: 0;
}

/* ---- CARRINHO ----
   A CAIXA SAI, A LINHA FICA. Cada item era um cartão com borda e raio: doze
   itens desenhavam doze molduras concorrentes numa tela que tem um assunto só
   — a conta. Vira lista, que é o que ela sempre foi: filete entre uma linha e
   outra, ar de --e4 em cima e embaixo, e a única forma fechada da tela passa a
   ser a placa da foto. ---- */
.loja-itens { display: flex; flex-direction: column; margin: var(--e4) 0; }
.loja-item {
  display: grid; gap: var(--e3); align-items: center;
  grid-template-columns: auto 1fr auto;
  padding: var(--e4) 0;
}
/* O filete é ENTRE itens, e não embaixo de cada um: quem fecha a lista por
   baixo é o fio do resumo, logo adiante. Com um em cada item, os dois cairiam
   a --e4 de distância e a última linha ganharia um fio duplo que ninguém
   desenhou. */
.loja-item + .loja-item { border-top: 1px solid var(--loja-filete); }
/* Miniatura de 64px: sem foto, o carrinho é uma lista de texto e a pessoa não
   reconhece o que escolheu — que é onde ela remove o item certo por engano.
   E É A MESMA PLACA DO CARD, em escala: este era o último lugar da loja onde a
   foto ainda pousava no cinza de --loja-superficie, então a mesma imagem que na
   vitrine lia como coleção, aqui desenhava um quadrado branco dentro de um
   quadrado cinza. Mesmo chão, mesmo fio, mesmo respiro proporcional. */
.loja-item__foto {
  width: 64px; height: 64px;
  background: var(--loja-foto); border: 1px solid var(--loja-filete);
  border-radius: var(--loja-raio-campo); padding: var(--e2); overflow: hidden;
  display: flex; align-items: center; justify-content: center;
}
.loja-item__foto img { width: 100%; height: 100%; object-fit: contain; }
/* O `gap` é o que substitui a caixa: sem ele, nome, unitário e o − n + ficam
   grudados num bloco só, e a linha volta a precisar de borda em volta pra se
   distinguir da de baixo. */
.loja-item__nome { display: flex; flex-direction: column; gap: var(--e2); min-width: 0; }
/* O <strong> nasce em 700 do navegador, e --pe-forte nesta tela é do DINHEIRO.
   O nome do item cai para o mesmo 600 do nome no card — é o mesmo objeto, lido
   na mesma altura de hierarquia. */
.loja-item__nome > strong { font-weight: var(--pe-medio); }
.loja-item__unit {
  color: var(--loja-tinta-fraca); font-size: var(--t-apoio); line-height: var(--lh-apoio);
  font-variant-numeric: tabular-nums;
}
.loja-item__qtd { display: flex; gap: var(--e2); align-items: center; }
/* 40px era o único controle da loja abaixo do piso de toque, e ele mora na
   tela onde a pessoa corrige a compra. */
.loja-item__qtd .loja-btn { min-height: var(--toque); padding: var(--e2) var(--e3); }
.loja-item__total {
  font-size: var(--t-num); line-height: var(--lh-num);
  font-weight: var(--pe-forte); font-variant-numeric: tabular-nums;
}
/* A lixeira é alvo de 44px de verdade, não um ícone de 16px com padding. */
.loja-item__remover {
  min-width: var(--toque); min-height: var(--toque);
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer; color: var(--loja-tinta-fraca);
  font: inherit; font-size: var(--t-num); line-height: var(--lh-num); border-radius: 999px;
  transition: background-color var(--loja-t), color var(--loja-t);
}
.loja-item__remover:hover { background: var(--loja-superficie); color: var(--loja-destaque); }

/* A CONTA NÃO É UM CARTÃO. Ela é a última coisa que se lê antes de pagar, e
   uma caixa em volta de quatro números só compete com o único que decide. Um
   filete em cima já diz "aqui começa a soma". */
.loja-resumo {
  border-top: 1px solid var(--loja-filete);
  padding-top: var(--e4); margin: var(--e4) 0;
}
/* AS LINHAS SÃO CONFERÊNCIA, O VALOR É TINTA. Itens, desconto e entrega são o
   que a pessoa confere; o que ela lê de longe é o total. Empatados todos em 16
   de tinta cheia, era preciso ler os quatro pra achar o que importa. */
.loja-resumo p {
  display: flex; justify-content: space-between; gap: var(--e3); margin: var(--e2) 0;
  font-size: var(--t-apoio); line-height: var(--lh-apoio); color: var(--loja-tinta-fraca);
}
/* `nowrap` PORQUE DINHEIRO NÃO QUEBRA. Medido no Chrome a 390px, no resumo do
   checkout: com o nome do produto ocupando a linha inteira, "R$ 18,84" partia
   entre o "R$" e o número, e a conferência do pedido saía com dois valores em
   duas linhas e um em uma. O flex encolhe o nome, que tem para onde encolher —
   o valor não tem. */
.loja-resumo strong {
  font-variant-numeric: tabular-nums; color: var(--loja-tinta); white-space: nowrap;
}
/* O `p.` NÃO É ENFEITE DE SELETOR: com o degrau de apoio agora declarado em
   `.loja-resumo p` (0,1,1), a classe sozinha (0,1,0) PERDIA o font-size e o
   número que fecha a conta imprimia 14px, do tamanho da linha de conferência.
   O total é o mesmo degrau do preço do card — --t-num em --pe-forte. */
.loja-resumo p.loja-resumo__total {
  font-size: var(--t-num); line-height: var(--lh-num); font-weight: var(--pe-forte);
  color: var(--loja-tinta);
  border-top: 1px solid var(--loja-filete); padding-top: var(--e3);
}
/* `.loja-resumo--compacto` ficou SEM REGRA de propósito: ela existia só pra
   baixar as linhas do resumo do checkout a --t-apoio, e esse agora é o degrau
   de toda linha de resumo. A classe segue no markup do checkout e da
   confirmação (fatia deles) sem efeito nenhum — o que não pode sobrar aqui é
   uma regra que finge decidir alguma coisa. */

.loja-acoes { display: flex; flex-direction: column; gap: var(--e2); margin: var(--e4) 0; }

/* ---- FORMULÁRIOS ----
   `flex: 1 1 auto` e NÃO `1 1 160px`: dentro de `.loja-form` (que é coluna), o
   flex-basis vira ALTURA — era daí que saía o campo de 160px de altura no
   checkout e o "avise-me" estourando 47px pra fora do card. Largura de campo em
   linha se resolve com `min-width`, que não muda de eixo. ---- */
.loja-input {
  font-family: inherit;
  /* --t-corpo é 16px, e aqui isso não é gosto: abaixo de 16 o iOS dá zoom ao
     focar e desalinha a tela inteira do checkout, justamente onde o dinheiro
     sai. Nenhum degrau menor pode ser usado num <input> desta loja. */
  font-size: var(--t-corpo); line-height: var(--lh-corpo);
  padding: var(--e3); border: 1px solid var(--loja-borda);
  /* Raio de CAMPO, não o do dono: com `loja--raio-redondo` o campo de nome do
     checkout virava pílula de 22px e deixava de parecer campo. */
  border-radius: var(--loja-raio-campo); background: var(--loja-fundo); color: inherit;
  /* A ALTURA SAI DE --alt-campo, NÃO DO PISO DE TOQUE. 44px é o mínimo que o
     dedo exige; 48 é o degrau de CAMPO desta loja. Empatado com o piso, o
     <input> ficava 8px mais baixo que o botão ao lado dele (o "Aplicar" do
     cupom, a lupa da busca) — e altura diferente entre dois controles vizinhos
     na mesma linha lê como desalinhamento, não como hierarquia. */
  flex: 1 1 auto; min-width: 0; min-height: var(--alt-campo); width: 100%;
  transition: border-color var(--loja-t), box-shadow var(--loja-t);
}
/* O FOCO É ANEL FINO, NUNCA FAIXA. A borda troca para a cor da conta e um halo
   de 3px do véu dela acompanha. No checkout são cinco campos empilhados a --e3
   de distância: uma faixa larga encostaria num campo no outro e a pessoa
   perderia qual deles está recebendo o que ela digita.
   `box-shadow` e não `outline`: o outline global de `:focus-visible` (§2) é o
   do TECLADO e não aparece no toque — este realce precisa aparecer no celular,
   que é onde o checkout é preenchido. */
.loja-input:focus { border-color: var(--loja-cor); box-shadow: 0 0 0 3px var(--loja-cor-veu); }
/* Campo numérico é largura fixa em qualquer container, sem depender do eixo. */
.loja-input--num { flex: 0 0 auto; width: 84px; text-align: center; }
/* ---- A BUSCA ----
   Uma LINHA, não um formulário. O botão vermelho de 48px ao lado do campo era o
   primeiro bloco primário da vitrine e o mais caro: ele gritava "aperte aqui"
   para uma ação que ninguém faz sem antes digitar, e roubava a cor do único
   botão que devolve dinheiro. O gesto continua idêntico — Enter submete, e sem
   JS é o mesmo GET de sempre —, só que agora ele veste a lupa que o topo já
   usa, e o campo perde a caixa: um filete embaixo basta pra dizer "escreva
   aqui" quando o cursor pisca em cima dele. */
.loja-busca { display: flex; flex-wrap: nowrap; align-items: center; gap: var(--e2); margin: var(--e4) 0; }
.loja-busca .loja-input {
  width: auto; min-width: 0;
  /* Sem caixa: só o fio de baixo. `--loja-borda` e não `--loja-filete` porque
     isto ainda é forma de CAMPO — o fio que separa duas coisas é mais fraco do
     que o fio que diz onde se digita. */
  border: 0; border-bottom: 1px solid var(--loja-borda); border-radius: 0;
  background: none;
  /* O padding lateral sai junto com a caixa: sem borda dos lados, ele só
     empurrava o texto pra dentro de um retângulo que não existe mais. */
  padding: 0; min-height: var(--alt-campo);
}
/* `:focus` e não `:focus-visible`: o fio de baixo trocando de cor não é anel de
   teclado, é o próprio campo dizendo que é ele que está recebendo o que se
   digita — e isso vale também pro toque.
   `box-shadow: none` NÃO É ENFEITE: o halo do `.loja-input` desenha os quatro
   lados, e este campo não tem quatro lados — ele é só o fio de baixo, e o halo
   devolveria na tela exatamente a caixa que esta regra tirou.
   O `outline` NÃO ENTRA NESSA LISTA, e a distinção custou caro: um `outline:
   none` aqui vence o `:focus-visible` global por especificidade (0,3,0 contra
   0,1,0) e casa também no Tab — quem chega no campo pelo teclado ficava com um
   fio de 1px trocando de cor como único sinal, metade do perímetro que a WCAG
   2.4.11 pede. O anel de 3px é o único lugar da loja onde o realce de foco tem
   área, e é justamente o campo em que ele não pode faltar. */
.loja-busca .loja-input:focus {
  border-bottom-color: var(--loja-cor); box-shadow: none;
}
/* A lupa é um SUBMIT de verdade, com o alvo de dedo inteiro e sem fundo: o
   rótulo "Buscar" continua existindo para o leitor de tela em `.loja-sr`. */
.loja-busca__lupa {
  flex: 0 0 auto;
  min-width: var(--toque); min-height: var(--toque);
  display: inline-flex; align-items: center; justify-content: center;
  background: none; border: 0; border-radius: 999px; cursor: pointer;
  color: var(--loja-tinta-fraca); padding: 0;
  transition: background-color var(--loja-t), color var(--loja-t);
}
.loja-busca__lupa:hover { background: rgb(127 127 127 / .16); color: var(--loja-tinta); }
.loja-busca__lupa svg { width: 22px; height: 22px; display: block; }

.loja-form { display: flex; flex-direction: column; gap: var(--e2); margin: var(--e4) 0; }
.loja-form label { font-weight: var(--pe-medio); margin-top: var(--e3); }
/* O PAGAMENTO PERDE A CAIXA. Era o único retângulo com borda e raio do
   checkout, e caixa deixou de ser como esta folha agrupa: quem agrupa é o
   `.loja-rotulo` com o filete correndo até a borda, logo acima das duas
   opções. Uma caixa ali significava que aquelas duas linhas eram um assunto
   diferente das cinco de cima — e são o mesmo pedido. */
.loja-form__pagamento { margin: var(--e3) 0; }
/* 317x24px era o alvo do "Combinar na entrega" — 24px de altura na tela onde o
   dinheiro é decidido. Vira linha de 44px, que é o piso do módulo. */
.loja-form__pagamento label {
  display: flex; align-items: center; gap: var(--e2);
  /* O padding lateral saiu junto com a borda: sem caixa em volta, ele só
     recuava as duas linhas 12px em relação aos campos de cima. */
  font-weight: var(--pe-normal); margin: 0; padding: 0; min-height: var(--toque);
  cursor: pointer;
}
/* DENTRO DO FORMULÁRIO O RÓTULO SEPARA PASSOS, NÃO ASSUNTOS. Os --e8 de cima
   são ar de PÁGINA — o que separa "Em oferta hoje" de "Mais da loja" na
   vitrine. Entre "Seus dados" e "Como você quer receber" eles abririam 64px de
   nada no meio de uma tarefa que se preenche de cima pra baixo, e cada seção
   pareceria uma tela nova. O filete continua sendo o mesmo. */
.loja-form .loja-rotulo { margin: var(--e7) 0 var(--e3); }

/* Um campo por linha, rótulo em cima: é o que cabe no polegar. */
.loja-campo { display: flex; flex-direction: column; gap: var(--e1); margin: 0 0 var(--e3); }
/* O RÓTULO DO CAMPO É APOIO, E O QUE SE DIGITA É O CONTEÚDO. Em 16px de tinta
   cheia ele tinha o mesmo peso do texto de dentro do campo, e a coluna do
   checkout virava uma pilha de dez linhas iguais — nada dizia o que é pergunta
   e o que é resposta. Um degrau abaixo, em tinta fraca, a resposta é o que
   sobra em preto. */
.loja-campo > label {
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
  font-weight: var(--pe-medio); color: var(--loja-tinta-fraca);
}

/* ESCOLHA EM CARTÃO (retirada × entrega). O <input> continua sendo o rádio de
   verdade — é ele que o form envia e é ele que funciona sem JS; o cartão é só o
   alvo grande em volta. */
.loja-escolha { display: grid; gap: var(--e2); margin: var(--e3) 0; }
/* 1px, E NÃO 2. Dois pixels de borda são peso de caixa em volta de um rádio:
   na folha de filtros, com 33 marcas, eram 33 molduras grossas empilhadas — a
   lista lia como uma parede, e o item escolhido não tinha como se destacar de
   vizinhos igualmente pesados. O fio fino separa igual e devolve o destaque
   pra quem está marcado. */
.loja-escolha__opcao {
  display: flex; align-items: center; gap: var(--e3);
  border: 1px solid var(--loja-borda); border-radius: var(--loja-raio);
  padding: var(--e3) var(--e4); min-height: var(--alt-cartao); cursor: pointer;
  transition: border-color var(--loja-t), background-color var(--loja-t);
}
.loja-escolha__opcao:hover { border-color: var(--loja-tinta-fraca); }
/* O ESCOLHIDO DEIXA DE ACENDER EM VERMELHO. Ele acendia com a cor da compra —
   borda em --loja-cor e fundo no véu dela —, e é a cor de ação sendo gasta pra
   dizer "Maior desconto está marcado". Marcar é tinta e chão: borda em
   --loja-tinta, fundo em --loja-superficie. A regra é uma só de propósito, e é
   a mesma que responde por "Retirar na loja" no checkout. */
.loja-escolha__opcao:has(input:checked) { border-color: var(--loja-tinta); background: var(--loja-superficie); }
.loja-escolha__titulo { font-weight: var(--pe-medio); display: block; }
.loja-escolha__nota {
  color: var(--loja-tinta-fraca); font-size: var(--t-apoio); line-height: var(--lh-apoio);
  display: block;
}

/* ---- CHECKOUT CONFIRMADO / PIX ---- */
.loja-ok { text-align: center; padding: var(--e5) 0 var(--e2); }
/* Mais apertado que o --e3 do `.loja-titulo`: aqui embaixo do h1 vem o rótulo
   e o número do pedido, que são a mesma frase que o título começou. */
.loja-ok h1 { margin-bottom: var(--e2); }
/* O RÓTULO DA CONFIRMAÇÃO NÃO DIVIDE PÁGINA: ele nomeia o número que vem logo
   abaixo dele ("PEDIDO" / "#1234"). Com o filete, um fio de 1px atravessaria
   uma coluna centrada sem separar assunto nenhum; com os --e8, o rótulo ficaria
   mais perto do total do que do próprio número que ele nomeia. */
.loja-ok .loja-rotulo { border-bottom: 0; padding-bottom: 0; margin: var(--e5) 0 0; }
/* Número do pedido grande: é o que o cliente manda no WhatsApp junto do
   comprovante, e é o que o dono procura no painel. */
.loja-pedido-numero {
  font-family: var(--loja-fonte-titulo);
  font-size: var(--t-display); line-height: var(--lh-display);
  letter-spacing: var(--ls-display); font-weight: var(--pe-forte);
  margin: var(--e2) 0;
  font-variant-numeric: tabular-nums;
}
/* O BLOCO DO PIX PERDE O TRACEJADO VERMELHO. Um retângulo pontilhado na cor de
   ação em volta de meia tela era o maior desenho primário do funil inteiro — e
   ele não é botão: é onde se LÊ a chave e se aponta a câmera. Chão de
   --loja-superficie, o mesmo cinza de todo agrupamento desta loja, com o
   respiro de bloco dentro (a chave e o QR não podem encostar na borda). */
.loja-pix {
  background: var(--loja-superficie); border-radius: var(--loja-raio);
  padding: var(--e6); margin: var(--e4) 0;
}
/* Dentro da caixa o rótulo é o NOME DO BLOCO, no alto dele: sem filete (a caixa
   já fecha) e sem o ar de página em cima. */
.loja-pix .loja-rotulo { border-bottom: 0; padding-bottom: 0; margin: 0 0 var(--e3); }
.loja-pix__chave {
  font-size: var(--t-num); line-height: var(--lh-num);
  /* QUEBRA POR PALAVRA, não por caractere. `break-all` partia a chave em
     qualquer letra: um e-mail virava "contato@dynamosuple" / "mentos.com.br" e
     uma chave aleatória quebrava no meio de um bloco de dígitos — quem confere
     a chave lendo da tela perde a conta. Assim ela só parte quando não couber
     de jeito nenhum, e nos separadores que já existem. */
  font-weight: var(--pe-medio); overflow-wrap: break-word;
}
/* O QR sai como SVG inline (rqrcode, já no Gemfile): imagem remota de QR seria
   a chave PIX viajando pra fora do domínio do cliente. */
.loja-pix__qr { display: flex; justify-content: center; margin: var(--e3) 0; }
.loja-pix__qr svg { width: 220px; height: auto; max-width: 100%; }
.loja-copiar { display: flex; gap: var(--e2); align-items: center; flex-wrap: wrap; }

/* ---- FOLHA DESLIZANTE (<dialog>) ----
   Carrinho, filtros e chat usam a MESMA folha: um markup, dois enquadramentos —
   no celular sobe de baixo, no desktop ancora à direita. ---- */
.loja-folha {
  position: fixed; inset: 0; width: 100%; max-width: none; height: auto;
  max-height: none; margin: 0; padding: 0; border: 0; background: transparent;
  overflow: hidden; z-index: 50;
}
.loja-folha::backdrop { background: rgb(0 0 0 / .45); }
.loja-folha__caixa {
  position: absolute; left: 0; right: 0; bottom: 0;
  display: flex; flex-direction: column; max-height: 88vh;
  background: var(--loja-fundo); color: var(--loja-tinta);
  border-radius: var(--loja-raio) var(--loja-raio) 0 0;
  box-shadow: var(--loja-sombra-2);
  padding-bottom: env(safe-area-inset-bottom);
}
/* O título da folha é RÓTULO DE DIÁLOGO, não título de página: 16/600 em caixa
   normal. Em --t-titulo ele empataria com o h1 que continua visível atrás do
   backdrop, e a folha passaria a parecer uma segunda página em cima da
   primeira em vez do painel que ela é. */
.loja-folha__topo {
  display: flex; align-items: center; justify-content: space-between; gap: var(--e3);
  padding: var(--e3) var(--e4); border-bottom: 1px solid var(--loja-borda);
  font-size: var(--t-corpo); line-height: var(--lh-corpo); font-weight: var(--pe-medio);
}
.loja-folha__corpo { flex: 1 1 auto; overflow-y: auto; padding: var(--e4); }
/* DENTRO DA FOLHA O RÓTULO NÃO TEM PÁGINA PRA DIVIDIR. Os --e8 que separam duas
   seções da vitrine, aqui, seriam 64px de nada entre o título do diálogo e
   "Ordenar por" — e o alto de uma folha que sobe do polegar é o lugar mais caro
   da tela. O ritmo aqui é de lista, não de página. */
.loja-folha__corpo .loja-rotulo { margin: var(--e5) 0 var(--e3); }
/* `:first-of-type` e não `:first-child`: os campos escondidos (`q`, `todos`)
   são os primeiros filhos do formulário, então `:first-child` nunca casaria. */
.loja-folha__corpo > [role="radiogroup"]:first-of-type > .loja-rotulo { margin-top: 0; }
/* Rodapé grudado: o total e o "fechar pedido" não podem depender de a pessoa
   rolar até o fim de uma lista de 12 itens. */
.loja-folha__rodape {
  border-top: 1px solid var(--loja-borda); padding: var(--e3) var(--e4);
  display: grid; gap: var(--e2); background: var(--loja-fundo);
}
.loja-folha__fechar {
  min-width: var(--toque); min-height: var(--toque);
  background: none; border: 0; cursor: pointer; color: var(--loja-tinta-fraca);
  font: inherit; font-size: var(--t-num); line-height: var(--lh-num); border-radius: 999px;
}
.loja-folha__fechar:hover { background: var(--loja-superficie); }

/* ---- TOAST ----
   "Whey adicionado · Ver carrinho" e some sozinho. Fica ACIMA da barra fixa:
   toast tapando o botão de comprar é o gesto seguinte perdido. ---- */
.loja-toast {
  /* `left: 50%` + `translateX(-50%)` SEM `right` deixava a largura
     shrink-to-fit limitada a `viewport − left`, ou seja, metade da tela: a
     "tirinha" media 187,5x129px (seis linhas) num celular de 390px. Com os dois
     lados presos e `width: max-content`, ela volta a ser uma linha e centraliza
     pelo `margin-inline`. */
  position: fixed; left: var(--e4); right: var(--e4);
  width: max-content; max-width: calc(100% - var(--e6)); margin-inline: auto;
  bottom: calc(var(--loja-barra-altura) + var(--e3) + env(safe-area-inset-bottom));
  z-index: 45;
  display: flex; align-items: center; gap: var(--e3);
  background: var(--loja-tinta); color: #fff;
  border-radius: 999px; padding: var(--e2) var(--e4);
  box-shadow: var(--loja-sombra-2);
  font-size: var(--t-apoio); line-height: var(--lh-apoio); font-weight: var(--pe-medio);
  animation: loja-sobe var(--loja-t) both;
}
/* "Ver carrinho" media 56x42px: é o gesto que a tirinha existe pra oferecer, e
   ele estava abaixo do piso de toque desta loja. O `min-height: auto` que havia
   aqui era exatamente o que o abaixava. */
.loja-toast a {
  font-weight: var(--pe-medio); text-decoration: underline;
  min-height: var(--toque); display: inline-flex; align-items: center;
}
@keyframes loja-sobe {
  from { opacity: 0; transform: translateY(12px); }
  to { opacity: 1; transform: translateY(0); }
}

/* ---- A BIA NA LOJA ---- */
.loja-chat {
  position: fixed; right: var(--e4);
  bottom: calc(var(--loja-barra-altura) + var(--e3) + env(safe-area-inset-bottom));
  z-index: 40;
}
.loja-chat__fab {
  display: inline-flex; align-items: center; gap: var(--e2);
  min-height: var(--alt-cta); padding: 0 var(--e4); border-radius: 999px;
  /* O FAB VESTE O PALCO, NÃO A COR DE AÇÃO. Ele flutua sobre a grade inteira,
     no desktop, ao lado de nenhuma compra: em vermelho era um bloco primário
     permanente competindo com o botão de cada card, pra oferecer conversa. No
     preto do chassi ele lê como coisa da LOJA (mesma faixa do topo e do
     rodapé), e continua achável por estar flutuando. */
  background: var(--loja-palco); color: var(--loja-palco-tinta); border: 0; cursor: pointer;
  font: inherit; font-weight: var(--pe-medio);
  font-size: var(--t-apoio); line-height: var(--lh-apoio); box-shadow: var(--loja-sombra-2);
  transition: background-color var(--loja-t), transform var(--loja-t);
}
/* --loja-tinta e não a primária forte: o palco é a tinta da conta escurecida
   em 22%, então a própria tinta é o mesmo matiz um degrau mais CLARO — o hover
   levanta o botão em vez de trocá-lo de cor. Sem hex novo e sem token novo. */
.loja-chat__fab:hover { background: var(--loja-tinta); }
.loja-chat__fab:active { transform: translateY(1px); }
/* O painel é a mesma folha do carrinho, com altura de conversa.
   z-index 75 e não 50: o popup de aviso vive em 70, e com o painel embaixo ele
   nascia DENTRO da conversa aberta, no meio das bolhas. */
.loja-chat__painel {
  position: fixed; inset: auto 0 0 0; z-index: 75;
  display: flex; flex-direction: column; height: 88vh;
  background: var(--loja-fundo); color: var(--loja-tinta);
  border-radius: var(--loja-raio) var(--loja-raio) 0 0;
  box-shadow: var(--loja-sombra-2);
  padding-bottom: env(safe-area-inset-bottom);
}
.loja-chat__cabecalho {
  display: flex; align-items: center; justify-content: space-between; gap: var(--e3);
  padding: var(--e3) var(--e4); border-bottom: 1px solid var(--loja-borda); font-weight: var(--pe-medio);
}
/* "Falando sobre: Whey Growth 1kg" — é o que faz "quero 2 desse" funcionar. */
.loja-chat__contexto {
  padding: var(--e2) var(--e4); background: var(--loja-superficie);
  color: var(--loja-tinta-fraca); font-size: var(--t-apoio); line-height: var(--lh-apoio);
  border-bottom: 1px solid var(--loja-borda);
}
.loja-chat__bolhas {
  flex: 1 1 auto; overflow-y: auto; padding: var(--e4);
  display: flex; flex-direction: column; gap: var(--e2);
}
.loja-chat__bolha {
  max-width: 84%; padding: var(--e2) var(--e3); border-radius: 16px;
  /* CORPO, e não apoio: a bolha é texto pra ler, e sai em tinta cheia dos dois
     lados da conversa — o degrau de apoio desta folha é sempre tinta fraca. */
  font-size: var(--t-corpo); line-height: var(--lh-corpo);
  white-space: pre-wrap; overflow-wrap: anywhere;
}
.loja-chat__bolha--cliente {
  align-self: flex-end; background: var(--loja-cor); color: #fff;
  border-bottom-right-radius: 4px;
}
.loja-chat__bolha--bia {
  align-self: flex-start; background: var(--loja-superficie); color: var(--loja-tinta);
  border-bottom-left-radius: 4px;
}
/* Os três chips prontos: HTML do servidor, sem request nenhum ao abrir o
   painel. Abrir a conversa não pode custar LLM. */
.loja-chat__chips { display: flex; gap: var(--e2); flex-wrap: wrap; padding: 0 var(--e4) var(--e2); }
.loja-chat__composer {
  display: flex; gap: var(--e2); align-items: flex-end;
  padding: var(--e3) var(--e4); border-top: 1px solid var(--loja-borda);
}
.loja-chat__composer .loja-input { width: auto; }
/* A SAÍDA. Freio estourado não vira erro: vira o botão verde de continuar no
   WhatsApp, com o carrinho no texto. Degradação vira conversão. */
.loja-chat__saida {
  display: grid; gap: var(--e2); padding: var(--e4); text-align: center;
}
/* O verde saiu daqui e virou `.loja-btn--wa` (ver §4, nos botões): a
   confirmação do pedido tem o MESMO botão e não podia emprestar um escopo do
   chat pra pintá-lo. */

/* ---- "AVISE QUANDO CHEGAR" ----
   `div.loja-aviso` é o BLOCO (grade); a letra miúda é `.loja-nota`. Ver a nota
   na §2 sobre a colisão de nome que existia aqui. ---- */
div.loja-aviso { display: grid; gap: var(--e2); }
.loja-aviso__form { display: grid; gap: var(--e2); }
.loja-aviso__ok { font-weight: var(--pe-medio); color: #1a7f37; margin: 0; }
.loja-aviso__erro:empty { display: none; }
.loja-aviso__erro {
  color: var(--loja-destaque); margin: 0;
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
}
.loja-aviso__nota {
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
  color: var(--loja-tinta-fraca); margin: 0;
}

/* ---- PAGINAÇÃO ---- */
/* ---- PAGINAÇÃO ----
   Dois botões de 52px e borda vermelha embaixo de uma grade para dizer
   "anterior" e "próxima": é peso de CTA num gesto que não vende nada. Vira o
   que sempre foi, um par de links — e ganha --e8 em volta, que é o mesmo ar que
   separa duas seções, porque virar de página TAMBÉM é trocar de assunto. */
.loja-paginacao {
  display: flex; align-items: center; justify-content: center; gap: var(--e4);
  margin: var(--e8) 0;
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
  color: var(--loja-tinta-fraca);
}
.loja-paginacao .loja-btn { font-size: var(--t-apoio); line-height: var(--lh-apoio); }
/* "Ver os 652 produtos" continua sendo BOTÃO — é a porta do catálogo inteiro,
   não navegação de lista. Só o ar cresce: com --e5 ele encostava na última
   fileira de uma grade que agora respira 48px entre linhas. */
.loja-vertudo { margin: var(--e7) 0; }

/* ============================================
   AVISOS DA LOJA — banner, popup e recado de rodapé
   ============================================ */

/* Faixa do topo: dentro do fluxo, nunca `position: fixed`. Faixa fixa come a
   primeira dobra do celular, que é onde o produto tem que estar. */
/* FUNDO DE TINTA, NÃO DE COR DE AÇÃO. O bloco vermelho de 375x120px passava em
   contraste (4,99:1) e mesmo assim contrariava a regra de identidade escrita no
   plano: "nunca [vermelho] como fundo de área grande — seguro em bold,
   cansativo em bloco". O vermelho volta a ser o fio de baixo e o cupom; o palco
   é preto. */
/* --loja-palco e não --loja-tinta: a faixa nasce colada no header, e o palco é
   o preto que topo, barra e rodapé usam quando a conta liga o chassi escuro.
   Em tinta pura ela desenhava um segundo tom de preto a 1px do primeiro — dois
   pretos vizinhos e diferentes leem como erro de renderização, não como duas
   faixas. Na conta de chassi claro o valor é o mesmo de sempre: preto. */
.loja-banner {
  background: var(--loja-palco); color: #fff; padding: var(--e3) 0;
  font-size: var(--t-apoio); line-height: var(--lh-apoio);
  border-bottom: 3px solid var(--loja-cor);
}
.loja-banner__linha { display: flex; align-items: center; justify-content: center; gap: var(--e3); flex-wrap: wrap; text-align: center; }
/* O partial escreve titulo, corpo e cupom DENTRO de um <p> só (`__texto`): um
   parágrafo por faixa é o que mantém a leitura em voz alta na ordem certa. */
.loja-banner__texto { margin: 0; display: inline-flex; align-items: center; gap: var(--e2); flex-wrap: wrap; justify-content: center; }
/* O título sobe um degrau (16 contra os 14 da faixa) e para por aí: ele divide
   a linha com o corpo e com o cupom, e um terceiro tamanho ali dentro faria a
   faixa inteira parecer três avisos empilhados. */
.loja-banner__titulo {
  font-size: var(--t-corpo); line-height: var(--lh-corpo);
  font-weight: var(--pe-medio); margin: 0;
}
.loja-banner__corpo { margin: 0; opacity: .92; }
.loja-banner__cta {
  color: inherit; font-weight: var(--pe-medio); text-decoration: underline;
  min-height: var(--toque); display: inline-flex; align-items: center;
}
.loja-banner__cupom {
  /* Filete inteiro, não tracejado: o pontilhado imita cupom de papel picotado —
     é desenho de outra época em cima de um código que a pessoa vai digitar. O
     fio é `currentColor` pra acompanhar a tinta da faixa em qualquer paleta. */
  border: 1px solid currentColor; border-radius: var(--loja-raio-campo);
  /* Sem tracking próprio: os três usos de letter-spacing da loja estão
     nomeados na §1.2, e o código do cupom não é nenhum deles. */
  padding: var(--e1) var(--e2); font-weight: var(--pe-medio);
}

/* <dialog open>: fecha sem JS (form method="dialog"), e o JS só grava o cookie
   de "já vi isso". `border: 0` porque o default do navegador desenha moldura. */
.loja-popup {
  /* `position: relative` fazia o <dialog open> ficar NO FLUXO, e o layout o
     renderiza depois do </main>: medido, ele nascia em y=3435 numa página de
     3970px — o aviso que o dono configura, no fim da página, onde ninguém
     chega. Fixo e centralizado ele volta a ser aviso.
     `height: fit-content` porque, com `inset: 0` e altura automática, o
     posicionado absoluto ESTICA pra preencher os dois lados em vez de
     centralizar. */
  position: fixed; inset: 0; margin: auto; z-index: 70;
  height: fit-content; max-height: calc(100dvh - var(--e6)); overflow: auto;
  border: 1px solid var(--loja-borda); border-radius: var(--loja-raio);
  background: var(--loja-fundo); color: var(--loja-tinta);
  padding: var(--e5); max-width: 420px; width: calc(100% - 32px);
  box-shadow: var(--loja-sombra-2);
}
/* Só pinta com `showModal()`, que não usamos (o popup fecha sem JS, pelo
   `form method="dialog"`). Fica porque não custa nada e é a rede se um dia o
   controller passar a abrir modal. */
.loja-popup::backdrop { background: rgb(0 0 0 / .45); }
.loja-popup__titulo {
  margin: 0 var(--e6) var(--e2) 0;
  font-family: var(--loja-fonte-titulo);
  font-size: var(--t-titulo); line-height: var(--lh-titulo);
  letter-spacing: var(--ls-titulo); font-weight: var(--pe-forte);
}
.loja-popup__corpo { margin: 0 0 var(--e4); color: var(--loja-tinta-fraca); }
.loja-popup__cupom { display: inline-block; border: 1px dashed var(--loja-cor); border-radius: var(--loja-raio-campo); padding: var(--e2) var(--e3); font-weight: var(--pe-medio); margin-bottom: var(--e4); }
.loja-popup__acoes { display: flex; flex-direction: column; gap: var(--e2); }
/* A caixa é o proprio <form method="dialog"> — é ele que fecha o popup sem JS,
   entao ele tem que ser o container, nao um <div> por dentro. */
.loja-popup__caixa { display: block; margin: 0; }
.loja-popup__cta {
  display: inline-flex; align-items: center; justify-content: center;
  min-height: var(--toque); padding: 0 var(--e4);
  background: var(--loja-cor); color: #fff; border-radius: var(--loja-raio);
  font-weight: var(--pe-medio); text-decoration: none;
}
/* 44px de alvo: é o X que a pessoa aperta com o polegar, e errar o X num popup
   é a pessoa fechando a loja inteira. */
.loja-popup__fechar {
  position: absolute; top: var(--e2); right: var(--e2);
  min-width: var(--toque); min-height: var(--toque);
  background: none; border: none; cursor: pointer;
  font: inherit; font-size: var(--t-num); line-height: var(--lh-num); color: var(--loja-tinta-fraca);
}

/* Recado do rodapé: é informação, não anúncio — mesmo peso do resto do rodapé.
   O nome é `loja-rodape-aviso` (e não `loja-rodape__aviso`) porque o bloco é o
   AVISO, não um pedaço do rodapé: ele tem wrap próprio e vive dentro do
   <footer> sem herdar o espaçamento dele. */
/* O recado mora DENTRO do <footer>, então ele veste o par do topo junto com
   ele: em `var(--loja-tinta)` o título do recado ficava preto sobre o palco
   preto — sumia inteiro na única conta que ligou o chassi escuro. */
.loja-rodape-aviso { margin: 0 0 var(--e3); padding: var(--e3) 0; border-bottom: 1px solid var(--loja-topo-borda); }
.loja-rodape-aviso p { margin: 0; }
.loja-rodape-aviso strong { color: var(--loja-topo-tinta); }
/* O RECADO É A ÚLTIMA COISA DENTRO DO <footer>, ENTÃO O LINK DELE VESTE O
   PALCO. Em `var(--loja-cor)` ele era um vermelho escuro sobre o preto do
   chassi: o único elemento clicável do rodapé, ilegível justamente na conta que
   ligou o chassi (contraste medido abaixo de 1,5:1 na paleta da Dynamo). Em
   tinta do topo ele lê nos dois chassis, e continua sendo link pelo sublinhado
   e pelo peso — que é o que diz "aperte" sem gastar a cor de ação num rodapé. */
.loja-rodape-aviso__cta { color: var(--loja-topo-tinta); font-weight: var(--pe-medio); text-decoration: underline; }

/* CUPOM NO CARRINHO — o campo onde o cliente digita o código que o dono criou.
   `__morto` é a frase de quando o cupom aplicado deixou de valer: ela precisa
   ser visível, porque o total sobe sozinho e ninguém pode pagar sem ver. */
/* --loja-filete e não --loja-borda: este fio só separa a conta do cupom. A
   borda mais forte fica pro que é forma de CAMPO — o <input> do código, logo
   abaixo dele. */
.loja-cupom { border-top: 1px solid var(--loja-filete); margin-top: var(--e3); padding-top: var(--e3); }
.loja-cupom__label {
  display: block; font-size: var(--t-apoio); line-height: var(--lh-apoio);
  color: var(--loja-tinta-fraca); margin-bottom: var(--e2);
}
.loja-cupom__form { display: flex; gap: var(--e2); flex-wrap: wrap; align-items: center; }
/* SEM caixa alta forçada no campo: quem normaliza o código é o servidor, e
   uppercase no <input> é grito exatamente onde a pessoa está digitando. */
.loja-cupom__form .loja-input { width: auto; min-width: 140px; }
.loja-cupom__ativo { display: flex; gap: var(--e3); align-items: center; flex-wrap: wrap; font-weight: var(--pe-medio); }
.loja-cupom__morto {
  margin: var(--e2) 0 0; font-size: var(--t-apoio); line-height: var(--lh-apoio);
  color: var(--loja-destaque);
}

/* ============================================
   5. SÓ NO CELULAR

   A barra fica fixa no rodapé da tela, então o conteúdo precisa de chão embaixo
   — senão o último botão da página nasce debaixo dela e ninguém alcança.
   ============================================ */
body.loja {
  padding-bottom: calc(var(--loja-barra-altura) + env(safe-area-inset-bottom));
}

/* A PRIMEIRA DOBRA É DO PRODUTO. Medido a 390x844, o primeiro card nascia em
   y=605: 72% da primeira tela era cromo e do produto apareciam 239px.

   O `.loja-hero p { display: none }` que morava aqui SAIU junto com o hero, e a
   troca é o coração desta fatia: o que sumia no celular era o único texto da
   página que ninguém mais diz (a frase do dono), enquanto o h1 que repetia a
   logo continuava ocupando 40px logo abaixo dela. Agora o h1 é que é invisível
   (`.loja-sr`, para busca e leitor de tela) e a frase é a abertura. O corpo do
   banner continua saindo: ali é texto que o rodapé e a linha da promessa já
   dizem, e ele nasce ANTES do produto. */
@media (max-width: 699px) {
  .loja-banner__corpo { display: none; }

  /* O FAB SOME NO CELULAR — e isto devolve o botão de comprar.
     Medido em produção a 390x844: a pílula "Falar com a Bia" tem 209px de
     largura e mora fixa no canto direito, logo acima da barra. Qualquer fileira
     de cards que pare naquela altura tem o "Adicionar" da coluna da direita com
     o CENTRO interceptado por ela (document.elementFromPoint devolvia
     .loja-chat__fab) — o dedo do cliente não alcança o botão que mais converte.
     Encolher ou deslocar não resolve: a grade rola, sempre haverá um card ali.
     E o FAB é redundante aqui: `.loja-barra` já tem o item "Bia", que o
     loja_chat_controller adota como segundo gatilho do painel (connect(), pelo
     id loja_barra_bia) e que sem JS continua levando ao WhatsApp. Some o botão,
     fica a porta. No desktop não há barra, e lá o FAB continua sendo a única
     porta — por isso a regra é só deste bloco. */
  .loja-chat__fab { display: none; }
}

/* ============================================
   6. DESKTOP

   Mesmo HTML: a barra some, a grade abre, as folhas ancoram à direita e o chat
   vira cartão de canto. Não existe view de celular; existe este bloco.
   ============================================ */
@media (min-width: 700px) {
  /* O DESKTOP SÓ CRESCE OS DOIS DEGRAUS DE CIMA, e por redeclaração de token —
     sem clamp, sem breakpoint novo, sem uma segunda escala pra manter. O teto
     de 31px é do CELULAR; aqui a mesma regra do celular imprime maior sozinha. */
  body.loja { padding-bottom: 0; --t-titulo: 31px; --t-display: 39px; }

  .loja-barra { display: none; }

  /* `auto-fill` e não `repeat(4, 1fr)`: a grade fixa criava um precipício em
     700px (card de 328px virava 158px, MENOR que no celular). Agora o card
     nunca desce de 200px e a tela decide quantos cabem — 3 no tablet, 4 em
     1024, 5 em 1440 — sem inventar breakpoint. */
  .loja-grade {
    grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    column-gap: var(--e4); row-gap: var(--e8);
  }
  .loja-produto { grid-template-columns: 1fr 1fr; align-items: start; }
  .loja-trilho { grid-auto-columns: 24%; }

  /* A folha deixa de subir de baixo e vira painel de 400px à direita — mesmo
     markup, outro enquadramento. */
  .loja-folha__caixa {
    top: 0; left: auto; right: 0; width: 400px; max-width: 100%;
    max-height: 100%; border-radius: 0;
  }

  .loja-chat { right: var(--e5); bottom: var(--e5); }
  .loja-chat__painel {
    inset: auto var(--e5) var(--e5) auto;
    width: 380px; max-width: calc(100% - var(--e6));
    height: 560px; max-height: calc(100vh - var(--e6));
    border-radius: var(--loja-raio);
  }
  .loja-toast { bottom: var(--e5); }
}

/* A partir daqui a tela larga o bastante para a foto do produto parar de
   crescer: em `1fr 1fr` ela chegava a 590px e empurrava preço e botão para
   fora da primeira dobra. A coluna de decisão passa a ter largura fixa. */
@media (min-width: 1024px) {
  .loja-produto { grid-template-columns: minmax(0, 1fr) 420px; }
  /* A COLUNA DE DECISÃO ACOMPANHA A ROLAGEM. Com a foto em 1fr numa tela de
     1440px a placa passa dos 600px de altura: quem rola pra ler a descrição
     deixa preço e "Adicionar ao carrinho" pra trás, e aqui não há barra do
     polegar pra devolvê-los (ela é só do celular). Grudada, a decisão fica
     sempre à mão sem duplicar botão nenhum.
     `align-items: start` já vem do bloco de 700px — sem ele o item esticaria
     até a altura da linha e não sobraria curso pra grudar.
     O topo limpa o header, que é `position: sticky` e mede ~68px com a logo de
     32: --e8 + --e5 dá os 88px medidos, e sai de token porque altura nesta
     folha não se escreve em px cru. */
  .loja-produto__dados { position: sticky; top: calc(var(--e8) + var(--e5)); }
}
