/* ==========================================================================
   ⚠️ AS FONTES NÃO SE CARREGAM MAIS AQUI, E NÃO VOLTAM (lock #30, 19/09/2026).
   Esta linha era um `@import` de fonts.googleapis.com. Enquanto ele existiu,
   o IP de TODO visitante ia para o Google em TODA página, e a Política de
   Privacidade era obrigada a declarar a transferência. As três famílias agora
   são servidas do próprio servidor: `fonts/jana-fonts.css`, enfileirado pelo
   `functions.php` do tema filho ANTES desta folha.

   NÃO reintroduzir o `@import` (nem um `wp_enqueue_style` apontando para o
   Google) "para simplificar": isso devolve a transferência e torna FALSO o
   texto publicado da Privacy Policy, que deixou de citar o Google Fonts. Há
   checagem no verify-tokens.mjs ligando as duas coisas, nos dois sentidos.
   ========================================================================== */

/* ==========================================================================
   JANA GUARDANAPOS · EUROPE — custom.css
   Direção estética: Editorial quente / boutique refinada
   --------------------------------------------------------------------------
   ONDE INSTALAR: wp-content/themes/hello-elementor-child/custom.css (o
   functions.php do tema filho já o enfileira). É site-wide de propósito:
   algumas correções de marca (verde #61CE70) vivem no header/produto.

   ⚠️ AS FONTES SÃO OUTRO ARQUIVO, E ELE É OBRIGATÓRIO (lock #30). O JSON não
   declara `font_family` em widget nenhum (a escala vive aqui), então o
   Elementor NÃO enfileira as famílias sozinho — e desde 19/09/2026 ele nem
   carrega Google Fonts (interruptor desligado no painel). Quem carrega Amatic
   SC, Fraunces e Raleway é `fonts/jana-fonts.css`, servido do NOSSO servidor.
   Sem a pasta `fonts/` instalada no tema filho, o site inteiro cai em fonte de
   sistema, sem erro nenhum na tela.

   ⚠️ Consequência do fim do `@import`: colar esta folha em Site Settings →
   CSS personalizado passou a NÃO bastar. O caminho do tema filho é o único
   completo, porque os `url()` das fontes são relativos ao arquivo.

   --------------------------------------------------------------------------
   ⚠️ CONTRATO DE RENDERIZAÇÃO DO ELEMENTOR — a razão de quase todo seletor aqui.
   O Elementor põe `_css_classes` no WRAPPER EXTERNO do widget, nunca no
   elemento interno:
       <div class="elementor-element elementor-widget-heading  jn-display">
         <div class="elementor-widget-container">
           <h2 class="elementor-heading-title">…</h2>
   Por isso a tipografia é aplicada em `.jn-classe .elementor-heading-title`,
   e não em `.jn-classe`. Escrever `.jn-display { font-family: … }` estiliza a
   DIV, e a `<h2>` de dentro continua com a fonte do tema — foi exatamente
   assim que a Amatic SC sumiu sem avisar. NÃO "simplificar" estes seletores.

   ⚠️⚠️ REV.02 — ISTO AQUI ESTAVA ERRADO E CUSTOU A PÁGINA INTEIRA.
   O parágrafo que ficava neste ponto dizia que contêineres "recebem as classes
   jn-* diretamente". NÃO RECEBEM. Medido no DOM ao vivo (LocalWP, 10/08):

       _css_classes em elType:"widget"    → RENDERIZA
       _css_classes em elType:"container" → É DESCARTADO, silenciosamente

   Não é campo malformado, não é o experimento "Optimized Markup", não é o
   JSON: é este Elementor. Todo container sai do servidor como
       <div class="elementor-element elementor-element-<id>
                   e-con e-con-full e-flex e-parent|e-child">
   e NADA mais. Foram 33 classes mortas na home, 9 no arquivo e 7 no produto —
   incluindo `.jn-section`, que era o escopo da tipografia-base: por isso o
   site inteiro caía em fonte de sistema, sem caixa e sem banda de cor.

   O QUE SOBREVIVE, e é onde toda esta folha se ancora agora (ver §0):
     · classes de WIDGET  (`.jn-display`, `.jn-card__title`, `.jn-quote__text`…)
     · `_element_id`      (`#hero`, `#collections`… — MEDIDO, os 9 existem)
     · a estrutura nativa (`.e-parent` = container de topo, `.e-child`)

   --------------------------------------------------------------------------
   PONTOS TRAVADOS (correções já feitas — não reverter):
    1. Banda sálvia/pêssego força ink-deep no texto (§4)
    2. reduced-motion desfaz .elementor-invisible (§15)
    3. Amatic SC via `.jn-display .elementor-heading-title` (§6)
    4. Subgrid alinha os 4 diferenciais sem min-height (§9)
    5. Herói mobile limita a foto p/ não empurrar a headline (§7)
    6. Medida de leitura só no corpo, não em todo <p> (§5)
    7. Ícones em medida óptica 16/20/32 (§9, §14)
    8. Foto da Jana sem aspect-ratio forçado (§12)
    9. Card = UM link no DOM + anel no card (§10)
   10. Verde #61CE70 do Elementor neutralizado (§16)
   11. Nenhum hex fora do :root (validado por verify-tokens.mjs)
   12. Input de newsletter com borda visível (§13)
   13. Chevron do card em bordas CSS, sem texto injetado (§10)
   14. Título do arquivo casa as DUAS marcações, Woo e Pro (§18.1)
   15. Add-to-cart vence `.button.alt` do Woo por cadeia longa (§19.7)
   16. APOSENTADO — era o portão do fluxo sob encomenda, removido do site
       a pedido do operador. Número não reutilizado (ver §19.10).
   17. ZERO opacity em texto sobre sálvia/pêssego (§10)
   18. Ficha técnica sai da DESCRIÇÃO, não de atributos (§19.5 + snippets-specs)
   19. "Proceed to checkout" vence `.button.alt` por cadeia própria (§20.7).
       O #15 NÃO cobre este: está escopado em `.single_add_to_cart_button`.
   20. "Place order" (#place_order) vence `.button.alt` por cadeia própria
       (§21.8). Nem o #15 nem o #19 o alcançam, e o #19 não pode ser alargado:
       lá é um <a>, aqui é um <button>. E o campo de país é selectWoo, então a
       borda do #12 cai no <select> ESCONDIDO — quem precisa dela é o
       `.select2-selection--single` (§21.2c).
   21. NENHUM seletor desta folha depende de classe que só existia em
       CONTAINER do Elementor (§0). Elas não chegam ao DOM. Validado por
       verify-tokens.mjs, que reprova o arquivo se qualquer uma voltar.
   22. A re-ancoragem da Rev.02 PRESERVA A ESPECIFICIDADE ORIGINAL de todo
       seletor que era só ESCOPO, via `:where()` (§0). Não é preciosismo:
       sem isso o novo âncora de seção pesaria (0,3,0) e reverteria o ponto
       travado #14 e os contratos de título do carrinho (§20) e do
       checkout (§21), que foram calibrados contra (0,1,1).
   23. O card de produto vence o widget "WooCommerce Products" do Elementor e o
       loop do Woo core por PESO, nunca por ordem de carregamento (§18.12).
       Três folhas carregam ANTES desta e pesam mais, então §18.5–§18.8
       sozinhas nunca chegaram a valer: o card estava azul, torto e sem
       respiro no ar. As duas âncoras (W = widget, N = loop nativo) e a conta
       de cada regra estão em §18.12. Encurtar qualquer cadeia devolve o card
       ao azul do kit, e o preço antigo à opacity .7 (reprova AA).
   ========================================================================== */

/* --------------------------------------------------------------------------
   0. ÂNCORAS — o dicionário desta folha (Rev.02)
   --------------------------------------------------------------------------
   Como as classes de container não chegam ao DOM, cada peça de layout é
   identificada por uma destas três vias. Elas estão em ordem de preferência:
   quanto mais alto, menos suposição.

   (a) `_element_id` — para IDENTIDADE de seção. Medido: os 9 ids da home
       existem no DOM, todos em `.e-con.e-parent`. São também os alvos dos
       links âncora dos CTAs, ou seja, já eram carga semântica antes desta
       revisão. Mudar um id no Elementor quebra o link E o estilo juntos, o
       que é bom: o erro fica visível.
         #hero #proof #promise #why #collections #new-arrivals
         #testimonials #about #trust        (home)
         #shop (archive-eu)   #product (single-eu)

   (b) estrutura nativa — para as CAMADAS fixas do template:
         SEÇÃO  = `.e-con.e-parent[id]`              → (0,3,0)
         CASCA  = `.e-con.e-parent[id] > .e-con`     → (0,4,0)
       Medido: toda `.e-parent` da home tem id, e cada uma tem EXATAMENTE um
       filho (a casca). A casca é o que fazia o boxing de `.jn-shell`.

   (c) `:has()` sobre classe de WIDGET — para as peças que se REPETEM (card,
       depoimento, diferencial, estatística), onde não há id:
         CARD       = `.e-con:has(> .e-con > .jn-card__title)`
         CORPO CARD = `.e-con:has(> .jn-card__title)`
         FOTO CARD  = CARD `> .e-con:has(> .elementor-widget-image)`
         DEPOIMENTO = `.e-con:has(> .jn-quote__text)`
         TRILHO     = `.e-con:has(> .e-con > .jn-quote__text)`
         DIFERENCIAL= `.e-con:has(> .jn-feature__icon)`
         ESTATÍSTICA= `.e-con:has(> .jn-stat__n)`
         HERÓI/FOTO = `.e-con:has(> .jn-hero__slide)`
       O combinador filho (`>`) é OBRIGATÓRIO. `:has()` sem ele varre TODOS os
       descendentes, e como as seções são aninhadas (seção > casca > fila >
       card), `.e-con:has(.jn-card__title)` casaria as QUATRO camadas de uma
       vez. Foi o primeiro erro que cometi ao reescrever isto.

   ⚠️ ESPECIFICIDADE — a regra de ouro desta revisão (ponto travado #22).
   Quando a classe morta era o ALVO da regra (`.jn-shell { max-width }`), o
   novo seletor pode e deve pesar mais: ele precisa vencer o `.e-con` do
   próprio Elementor, que é (0,1,0).
   Quando a classe morta era só o ESCOPO (`.jn-section :is(h1..h4)`), o novo
   âncora vai dentro de `:where()`, que pesa ZERO, e a conta final é idêntica
   à de antes:
       .jn-section :is(h1,h2,h3,h4)                    → (0,1,1)
       .e-con:where(.e-parent[id]) :is(h1,h2,h3,h4)    → (0,1,1)   idem
   Um `.e-con` fica de fora do `:where()` justamente para repor a classe que
   `.jn-section` fornecia. NÃO "simplificar" para `:where(.e-con.e-parent[id])`
   — isso zera a conta e devolve o título do arquivo para a fonte do kit do
   Elementor (é o ponto travado #14 ao contrário).

   (d) ⚠️ REV.03 — o LOOP DE PRODUTO não usa nenhuma das três vias acima, e não
       pode usar. Ali a marcação não é do Elementor nem nossa: é do WooCommerce
       (`ul.products > li.product`), e o adversário são folhas de terceiros que
       carregam antes desta. As duas âncoras e a conta de peso de cada regra
       estão em §18.12 (ponto travado #23):
         W = `.elementor-products-grid.elementor-wc-products
              ul.products.elementor-grid li.product`               (0,5,2)
         N = `html .woocommerce ul.products li.product`            (0,3,4)
       A lição que vale para o resto da folha: aqui o inimigo não é a
       especificidade BAIXA demais do nosso seletor, é a ALTA demais do deles.
       O `:where()` de (c) faria exatamente o mal contrário.

   Suporte: `:has()` é baseline desde dez/2023. Confirmado no navegador do
   operador (`CSS.supports('selector(:has(a))')` → true).

   ⚠️ ARMADILHA PARA A PRÓXIMA RODADA — `footer-eu.json` (e um header, se vier).
   A âncora de seção é `.e-con.e-parent[id]`: "contêiner de topo COM id". Ela
   não é escopada por página, e hoje isso é seguro porque só existe UM root do
   Elementor no site (medido: nenhum template de header ou footer). No dia em
   que o rodapé entrar, se o contêiner de topo dele levar um `_element_id`, ele
   herda de uma vez os 112px de padding de seção, o `display: block` e o reset
   de padding nos descendentes — e vai parecer que "o rodapé veio torto".
   Duas saídas, ambas de uma linha: NÃO dar `_element_id` ao contêiner de topo
   do rodapé (os ids existem para os links âncora da home, o rodapé não
   precisa), ou trocar a âncora por uma lista explícita dos 11 ids do §0a.
   É o mesmo formato do bug de padding dobrado que o `:not(:has(.e-con))` do
   §17b já evita — e vale a pena resolver ANTES, não depois.
   -------------------------------------------------------------------------- */

/* --------------------------------------------------------------------------
   1. TOKENS
   -------------------------------------------------------------------------- */
:root {
  /* --- Paleta de marca — PRESERVADA (extraída do JSON do site BR) --- */
  --jn-creme:       #F9EEEA;  /* surface_canvas — fundo principal            */
  --jn-sage:        #B0D0BB;  /* verde-sálvia — banda de seção, acento       */
  --jn-peach:       #F1BDA6;  /* pêssego — acento suave, banda compacta      */
  --jn-ink:         #7F562B;  /* marrom quente — títulos                     */
  --jn-terracotta:  #8D4E2A;  /* terracota — CTA, preço, links de ação       */
  --jn-white:       #FFFFFF;

  /* --- Refinamento autorizado (brief §2): tom mais escuro para AA ---
     #7F562B sobre sálvia = 3,85:1 → REPROVA corpo (aprova só texto grande).
     #6B4620 sobre sálvia = 4,99:1 → aprova AA em qualquer fundo da marca.
     Mesma família cromática — não é troca de paleta, é degrau de contraste. */
  --jn-ink-deep:    #6B4620;  /* corpo de texto — AA em creme/sálvia/pêssego */

  /* --- Derivados (alpha do próprio marrom, não cinzas neutros) --- */
  --jn-line:        rgba(107, 70, 32, 0.16);
  --jn-line-strong: rgba(107, 70, 32, 0.32);
  --jn-shade:       rgba(107, 70, 32, 0.07);
  --jn-shadow:      rgba(107, 70, 32, 0.10);
  --jn-shadow-lift: rgba(107, 70, 32, 0.16);

  /* --- Papéis semânticos --- */
  --jn-bg:          var(--jn-creme);
  --jn-text:        var(--jn-ink-deep);
  --jn-heading:     var(--jn-ink);
  --jn-action:      var(--jn-terracotta);
  --jn-on-action:   var(--jn-white);

  /* --- Tipografia: 3 papéis, 3 famílias. Trocar UMA linha aqui troca a
         fonte na página inteira (H1, títulos de card, citações, números da
         faixa de prova e menu) de forma coerente. É o ponto de troca. ---

     --jn-font-display  Amatic SC  · assinatura, título de toda seção. FIXA.
     --jn-font-head     Fraunces   · H1, h3, citação, números, menu.
     --jn-font-body     Raleway    · eyebrow, corpo, botões, small. */
  --jn-font-display: "Amatic SC", "Bradley Hand", cursive;
  /* Fraunces (soft serif variável) substituiu Magra na Rev.04.
     Magra é uma sans humanista estreita: limpa, mas anônima, e não dizia
     nada sobre papel/artesanato. Serifa é o que sinaliza herança e ofício
     para o público europeu, e não briga com a Amatic (que é caixa-alta
     manuscrita, outro registro). Alternativas testadas no README. */
  --jn-font-head:    "Fraunces", "Georgia", "Times New Roman", serif;
  --jn-font-body:    "Raleway", "Helvetica Neue", Arial, sans-serif;

  /* Amatic SC é condensada e de x-height baixa: pede ~15% a mais de corpo
     que uma sans para pesar o mesmo na página. Escala já compensada. */
  --jn-t-display:  clamp(3rem, 1.9rem + 5.6vw, 5.25rem);
  --jn-t-h1:       clamp(2.1rem,  1.35rem + 3.4vw, 3.5rem);
  --jn-t-h3:       clamp(1.2rem,  1.1rem + 0.45vw, 1.4rem);
  --jn-t-lead:     clamp(1.05rem, 1rem + 0.28vw, 1.1875rem);
  --jn-t-body:     1rem;
  --jn-t-small:    0.875rem;
  --jn-t-eyebrow:  0.75rem;

  --jn-lh-tight:   1.12;
  --jn-lh-head:    1.22;
  --jn-lh-body:    1.65;
  --jn-ls-eyebrow: 0.16em;

  /* Espaçamento — grade de 8pt (múltiplos de 4) */
  --jn-s-1:   4px;   --jn-s-2:   8px;   --jn-s-3:  12px;  --jn-s-4:  16px;
  --jn-s-5:  24px;   --jn-s-6:  32px;   --jn-s-7:  48px;  --jn-s-8:  64px;
  --jn-s-9:  96px;   --jn-s-10: 128px;

  --jn-section-y:    clamp(56px, 7vw, 112px);
  --jn-section-y-sm: clamp(40px, 5vw, 72px);
  --jn-measure:      68ch;
  --jn-container:    1200px;
  --jn-gutter:       clamp(20px, 4vw, 48px);

  /* Raio — botão PILL preservado (alma da marca);
     card reduzido de 24px → 6px (REFERENCE §A.5: cantos datados) */
  --jn-r-card:   6px;
  --jn-r-media:  4px;
  --jn-r-pill:   999px;

  /* Motion — sóbrio; a marca é calma, não performática */
  --jn-ease:      cubic-bezier(0.16, 1, 0.30, 1);
  --jn-ease-soft: cubic-bezier(0.4, 0, 0.2, 1);
  --jn-d-fast:    180ms;
  --jn-d-base:    280ms;
  --jn-d-slow:    420ms;
}

/* --------------------------------------------------------------------------
   2. BASE
   Escopo é a SEÇÃO (§0b), não `.jn-page` (que só existe no preview — o <body>
   do WP não tem essa classe).

   ⚠️ Rev.02: era `.jn-section`, que NUNCA existiu no WordPress. Todo este
   bloco estava inerte no site real desde a primeira entrega — foi por isso
   que o <body> media fonte de sistema. O escopo novo é medido.

   ⚠️ Este escopo NÃO alcança carrinho e checkout, e isso está mantido de
   propósito: aquelas páginas não são Elementor (medido: 0 `.e-con` em
   /cart/ e /checkout/), a skin delas vive em §20/§21 com âncora própria em
   `body.woocommerce-*`, e alargar a base agora mudaria margens de <p> em
   páginas já auditadas sem que eu possa medir o resultado (a loja ainda não
   tem as páginas do Woo atribuídas). Ver README-REV02 → "o que ficou fora".
   -------------------------------------------------------------------------- */
.e-con.e-parent[id] {
  color: var(--jn-text);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  line-height: var(--jn-lh-body);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  /* iOS infla texto em blocos largos ao girar o aparelho; isto trava a escala
     que definimos. */
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  /* Palavra longa (URL, nome composto, alemão) não pode estourar a coluna. */
  overflow-wrap: break-word;
}
.jn-page { background: var(--jn-bg); }

/* Toque: retângulo cinza padrão do iOS/Android substituído por um realce
   da própria marca. */
.e-con:where(.e-parent[id]) :is(a, button) { -webkit-tap-highlight-color: rgba(141, 78, 42, 0.15); }

/* ⚠️ (0,1,1) EXATO — ver ponto travado #22. O `.e-con` fora do `:where()` é o
   que repõe o peso que `.jn-section` dava. Com `:where(.e-con.e-parent[id])`
   isto cairia para (0,0,1) e PERDERIA para o kit de fontes do Elementor
   (`.elementor-kit-N h1`, que é (0,1,1)); com `.e-con.e-parent[id]` subiria
   para (0,3,1) e passaria a VENCER o ponto travado #14 (§18.1) e os títulos
   de carrinho e checkout. Os dois lados quebram. É este meio-termo. */
.e-con:where(.e-parent[id]) :is(h1, h2, h3, h4) {
  font-family: var(--jn-font-head);
  color: var(--jn-heading);
  line-height: var(--jn-lh-head);
  font-weight: 400;
  margin: 0;
  /* Fraunces é variável com eixo opsz: deixa o desenho afinar sozinho em
     display e engrossar em corpo pequeno. */
  font-optical-sizing: auto;
}
.e-con:where(.e-parent[id]) p { margin: 0; }

/* Medida de leitura só no CORPO de texto. Aplicá-la a todo <p> estrangulava
   eyebrows, notas de formulário e legendas. */
.e-con:where(.e-parent[id]) .elementor-widget-text-editor p { max-width: var(--jn-measure); }

/* --------------------------------------------------------------------------
   3. LAYOUT PRIMITIVES
   `--display` é setado junto com `display` porque o .e-con do Elementor usa
   `display: var(--display)` com a MESMA especificidade — sem isso, quem vence
   depende da ordem de carregamento das folhas.
   -------------------------------------------------------------------------- */
/* ⚠️ REV.02 — ZERAR O PADDING DEFAULT DO CONTAINER. Medido no site: TODAS as
   73 `.e-con` da home renderizam com `padding: 10px`, que é o default do
   Elementor para container sem padding declarado. O preview antigo não
   reproduzia isso, então o desenho foi aprovado com zero — e no ar cada
   card ganhava 10px entre a borda e a foto, cada fila ganhava 10px de recuo
   que ninguém pediu, e a soma empurrava as grades.

   Esta regra vem PRIMEIRO de propósito: tudo nesta folha que declara padding
   (a seção logo abaixo, o corpo do card, o depoimento) vem depois e vence.
   Se alguém mover este bloco para o fim do arquivo, apaga os três.
   Peso (0,1,0)/(0,2,0) para empatar/vencer o `.e-con` do próprio Elementor. */
.e-con:where(.e-parent[id]),
.e-con:where(.e-parent[id]) .e-con { padding: 0; }

/* SEÇÃO — alvo, não escopo: precisa vencer o padding default do `.e-con`
   (10px, medido) e o `--padding-*` que o Elementor emite por elemento. */
.e-con.e-parent[id] {
  --display: block;
  display: block;
  padding-block: var(--jn-section-y);
  /* Safe-area: em iPhone com notch, no modo paisagem o conteúdo entraria
     debaixo do canto arredondado sem o env(). */
  padding-inline: max(var(--jn-gutter), env(safe-area-inset-left));
  padding-right: max(var(--jn-gutter), env(safe-area-inset-right));
}
/* era `.jn-section--tight`: as duas faixas finas da home */
#proof, #trust { padding-block: var(--jn-section-y-sm); }

/* CASCA — era `.jn-shell`. É o que devolve a caixa de 1200px que sumiu
   (as seções mediam 1520,8px de largura no site).

   ⚠️ POR QUE NÃO A "largura nativa" do container. Trocar as seções para
   content_width:boxed faria o Elementor aplicar `--content-width`, que aqui
   vale `min(100%, 1140px)` (medido). São 60px a menos que os 1200 do design
   aprovado, em toda a página, e a diferença sairia calada. Se o operador
   preferir a via nativa, o caminho é Site Settings → Layout → Content Width
   = 1200 E remover este bloco — as duas coisas juntas, nunca só uma. */
.e-con.e-parent[id] > .e-con {
  --display: block;
  display: block;
  width: 100%;
  max-width: var(--jn-container);
  margin-inline: auto;
}
/* FILA — era `.jn-row`. Envolto em `:where()` DE PROPÓSITO, para pesar
   (0,1,0): todas as filas com grade própria (herói, cards, diferenciais,
   depoimentos) são identificadas por `:has()` e pesam mais, então vencem
   esta sem precisar de ordem. Só sobra o `display:block` onde não há grade. */
:where(.e-con.e-parent[id] > .e-con) > .e-con { --display: block; display: block; width: 100%; }

/* era `.jn-shell--narrow` + `.jn-center`: a fila de texto do #promise, que é
   a única com medida estreita e centrada. Identificada pelo lead que ela
   contém — a irmã dela é o cabeçalho, que tem `.jn-display`. */
#promise > .e-con > .e-con:has(> .jn-lead) {
  max-width: 760px;
  margin-inline: auto;
  text-align: center;
}
#trust > .e-con > .e-con { text-align: center; }

/* --------------------------------------------------------------------------
   4. FUNDOS DE SEÇÃO — o ritmo alternado:
   creme → sálvia → creme → branco → creme → sálvia → branco → pêssego → creme
   -------------------------------------------------------------------------- */
/* Rev.02 — o ritmo passa a ser declarado por id de seção. As classes
   `.jn-bg-*` eram containers e não chegavam ao DOM: a página inteira ficava
   branca. Quem troca a cor de uma faixa hoje, troca AQUI, não no Elementor.
   `--jn-peach` continua no §1 e sem faixa própria: nenhum template usa. */
#hero, #proof, #why, #new-arrivals, #trust,
#shop, #product                        { background-color: var(--jn-creme); }
#collections, #about                   { background-color: var(--jn-white); }
#promise, #testimonials                { background-color: var(--jn-sage);  }

/* ⚠️ PONTO TRAVADO #1 — CONTRASTE. NÃO remover.
   Sálvia e pêssego têm luminância quase idêntica (razão 1,00 entre si) e ficam
   a 1,47:1 do creme. Sobre essas bandas, #7F562B cai para 3,85:1 — reprova AA
   em corpo. Todo texto sobre sálvia/pêssego usa ink-deep (4,99:1).
   O site BR atual põe texto BRANCO sobre sálvia = 1,67:1; é o bug que isto
   corrige. Nunca reintroduzir branco sobre sálvia/pêssego.

   ⚠️ Rev.02 — a conta de especificidade aqui é o que faz o ponto travado
   funcionar, e ela é (0,1,1), EMPATANDO com `:is(h1..h4)` do §2 e vencendo
   pela ORDEM (este bloco vem depois). Por isso o `:where()` em volta dos
   ids: `#promise :is(h1..p)` seria (1,0,1) e `:where(...) :is(h1..p)` seria
   (0,0,1). O primeiro vence coisas que não deveria, o segundo PERDE para o
   §2 e devolve o texto sálvia para --jn-heading = 3,85:1. */
.e-con:where(#promise, #testimonials),
.e-con:where(#promise, #testimonials) :is(h1, h2, h3, h4, p) {
  color: var(--jn-ink-deep);
}

/* --------------------------------------------------------------------------
   5. TIPOGRAFIA
   Sistema de 3 papéis: Amatic SC = título de seção · Magra = h1 + títulos de
   card · Raleway = eyebrow, corpo, botões.
   -------------------------------------------------------------------------- */

/* Eyebrow — <p>, nunca heading (senão o outline vira p→h2 quebrado) */
.jn-eyebrow .elementor-heading-title {
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-eyebrow);
  font-weight: 700;
  letter-spacing: var(--jn-ls-eyebrow);
  text-transform: uppercase;
  color: var(--jn-terracotta);
  line-height: 1.4;
}
.e-con:where(#promise, #testimonials) .jn-eyebrow .elementor-heading-title { color: var(--jn-ink-deep); }
.jn-eyebrow { margin-bottom: var(--jn-s-3); }

/* ⚠️ PONTO TRAVADO #3 — a ASSINATURA da marca.
   O seletor precisa do descendente `.elementor-heading-title`: a classe
   jn-display fica no WRAPPER do widget, e a <h2> de dentro herdaria a fonte do
   tema. Especificidade (0,2,0) vence `.jn-section :is(h1..h4)` (0,1,1). */
.jn-display .elementor-heading-title {
  font-family: var(--jn-font-display);
  font-weight: 700;
  font-size: var(--jn-t-display);
  line-height: var(--jn-lh-tight);
  letter-spacing: 0.01em;
  /* medida em ch de uma condensada — 28ch de Amatic é bem mais estreito
     que 28ch de Magra; por isso os números aqui são generosos */
  max-width: 28ch;
}
/* Cabeçalhos CENTRADOS — era `.jn-head--center`. As duas seções com esse
   tratamento são #promise e #testimonials, e em nenhuma das duas existe um
   segundo `.jn-display`, então o escopo da seção basta. (0,3,0) como antes. */
.e-con:where(#promise, #testimonials) .jn-display .elementor-heading-title {
  max-width: 34ch;
  margin-inline: auto;
}
/* O centro vai no CABEÇALHO, nunca na seção: em #testimonials a seção
   contém os depoimentos, e `text-align` herdaria para dentro dos cards. */
:is(#promise, #testimonials) > .e-con > .e-con:has(> .jn-display) { text-align: center; }

/* Regrinha decorativa sob o título de seção — pseudo-elemento, não widget
   divider: some da árvore de acessibilidade e mantém preview e JSON idênticos. */
.jn-display .elementor-heading-title::after {
  content: "";
  display: block;
  width: 56px; height: 2px;
  background: var(--jn-terracotta);
  border-radius: var(--jn-r-pill);
  margin-top: var(--jn-s-4);
}
.e-con:where(#promise, #testimonials) .jn-display .elementor-heading-title::after { margin-inline: auto; }

/* ⚠️ TRAVADO (Rev.01 §3.1) — O H1 usa UMA ÚNICA família de ponta a ponta.
   Houve aqui uma ênfase que trocava a família no meio da linha (Amatic SC na
   segunda metade). Tecnicamente funcionava, mas LIA COMO BUG de renderização:
   "duas fontes diferentes na mesma linha". Não reintroduzir troca de família,
   peso ou cor dentro do H1. Se precisar de ênfase, ela vai no kicker acima. */
.jn-h1 .elementor-heading-title {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h1);
  line-height: var(--jn-lh-tight);
  max-width: 20ch;
}

.jn-h3 .elementor-heading-title {
  font-size: var(--jn-t-h3);
  font-weight: 700;
}

.jn-lead p {
  font-size: var(--jn-t-lead);
  line-height: 1.6;
}
/* CABEÇALHO DE SEÇÃO — era `.jn-head`. É a fila que carrega o `.jn-display`,
   em #promise, #why, #collections, #new-arrivals e #testimonials. A cópia do
   #about também tem `.jn-display`, mas está um nível mais fundo (seção >
   casca > fila > coluna), por isso o `> .e-con > .e-con` não a alcança. */
.e-con.e-parent[id] > .e-con > .e-con:has(> .jn-display) .jn-lead { margin-top: var(--jn-s-5); }
.e-con.e-parent[id] > .e-con > .e-con:has(> .jn-display) { margin-bottom: var(--jn-s-7); }
/* ⚠️ Esta regra era `.jn-head--center .jn-lead p` e NUNCA casou nada: o único
   lead centrado da página (#promise) vive na FILA, não no cabeçalho. Com a
   medida de 68ch dentro de uma caixa de 760px, o bloco encostava à esquerda
   enquanto o texto ia centrado — visível a olho. Reancorada onde o desenho
   sempre quis que ela estivesse. */
#promise .jn-lead p { margin-inline: auto; }

/* --------------------------------------------------------------------------
   6. BOTÕES / CTA — um único estilo, estados visíveis
   (brief §5.5 + REFERENCE §A.3: eliminar o verde default e o pêssego fraco)
   -------------------------------------------------------------------------- */
.jn-btn .elementor-button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--jn-s-2);
  min-height: 48px;                    /* alvo de toque acima dos 44px */
  padding: 0 var(--jn-s-6);
  font-family: var(--jn-font-body);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  text-decoration: none;
  border-radius: var(--jn-r-pill);     /* pill — preservado do BR */
  border: 2px solid transparent;
  cursor: pointer;
  transition:
    background-color var(--jn-d-base) var(--jn-ease-soft),
    color            var(--jn-d-base) var(--jn-ease-soft),
    border-color     var(--jn-d-base) var(--jn-ease-soft),
    transform        var(--jn-d-fast) var(--jn-ease-soft),
    box-shadow       var(--jn-d-base) var(--jn-ease-soft);
}

/* Primário — terracota sólido. Branco sobre #8D4E2A = 6,45:1 */
.jn-btn--primary .elementor-button {
  background-color: var(--jn-terracotta);
  color: var(--jn-on-action);
  border-color: var(--jn-terracotta);
}
.jn-btn--primary .elementor-button:hover {
  background-color: var(--jn-ink-deep);
  border-color: var(--jn-ink-deep);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px var(--jn-shadow-lift);
}
.jn-btn--primary .elementor-button:active { transform: translateY(0); box-shadow: none; }

/* Secundário — contorno. Texto terracota sobre creme = 5,67:1 */
.jn-btn--ghost .elementor-button {
  background-color: transparent;
  color: var(--jn-terracotta);
  border-color: var(--jn-terracotta);
}
.jn-btn--ghost .elementor-button:hover {
  background-color: var(--jn-terracotta);
  color: var(--jn-on-action);
  transform: translateY(-2px);
}
/* Sobre banda sálvia o ghost precisa do marrom escuro (AA) */
.e-con:where(#promise, #testimonials) .jn-btn--ghost .elementor-button {
  color: var(--jn-ink-deep);
  border-color: var(--jn-ink-deep);
}
.e-con:where(#promise, #testimonials) .jn-btn--ghost .elementor-button:hover {
  background-color: var(--jn-ink-deep);
  color: var(--jn-on-action);
}

/* FILA DE BOTÕES — era `.jn-btn-row`. Identificada pelo botão que contém. */
.e-con:has(> .jn-btn) {
  --display: flex;
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  gap: var(--jn-s-4);
  margin-top: var(--jn-s-6);
  width: auto;
}
:where(#promise) .e-con:has(> .jn-btn) { justify-content: center; }

/* --- FOCO VISÍVEL — global, em tudo que é interativo --- */
/* ⚠️ Os quatro seletores separados, NÃO um `:is(a,button,input,[tabindex])`.
   Dentro de `:is()` todos herdariam o peso do mais forte (o atributo), e a
   linha do `a` subiria de (0,2,1) para (0,3,0) — empatando com
   `.jn-card__title a:focus-visible`, que precisa vencer para o anel aparecer
   no CARD e não no texto do título (ponto travado #9). */
.e-con:where(.e-parent[id]) a:focus-visible,
.e-con:where(.e-parent[id]) button:focus-visible,
.e-con:where(.e-parent[id]) input:focus-visible,
.e-con:where(.e-parent[id]) [tabindex]:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 3px;
  border-radius: var(--jn-r-media);
}
/* Sobre terracota/bandas o anel precisa inverter para ser visível */
.jn-btn--primary .elementor-button:focus-visible,
.e-con:where(#promise, #testimonials) a:focus-visible {
  outline-color: var(--jn-ink-deep);
}

/* Skip link. A partir da Rev.04 ele vive no `header-eu.json` e continua no
   preview, então precisa funcionar nos dois.

   ⚠️ `position: fixed`, e NÃO `absolute`. Com `absolute` o bloco que o
   posiciona é o ancestral posicionado mais próximo, que no cabeçalho é o
   wrapper do widget (`.elementor-widget` é `position: relative` no próprio
   Elementor). O `top: -100px` passa a ser "100px acima do widget", e basta
   existir qualquer coisa acima do cabeçalho para o link aparecer na tela sem
   ninguém ter dado foco nele. Medido no preview. Com `fixed` a conta é sempre
   contra a janela: escondido de verdade, e o foco sempre traz o link para o
   canto superior esquerdo, onde se espera encontrá-lo. */
.jn-skip {
  position: fixed; left: var(--jn-s-4); top: -100px;
  z-index: 999;
  background: var(--jn-terracotta); color: var(--jn-white);
  padding: var(--jn-s-3) var(--jn-s-5);
  border-radius: var(--jn-r-pill);
  font-family: var(--jn-font-body);
  font-weight: 700; text-decoration: none;
  transition: top var(--jn-d-fast) var(--jn-ease-soft);
}
.jn-skip:focus { top: var(--jn-s-4); }

/* --------------------------------------------------------------------------
   7. HERO
   -------------------------------------------------------------------------- */
/* MOBILE-FIRST: uma coluna é o estado BASE; duas colunas só a partir de 861px.
   A foto vem antes do texto no fluxo (order:-1) porque no celular a imagem é
   quem para o dedo. */
#hero > .e-con > .e-con {
  --display: grid;
  display: grid;
  grid-template-columns: 1fr;
  gap: clamp(24px, 5vw, 72px);
  align-items: center;
}
#hero > .e-con > .e-con > .e-con { min-width: 0; }
/* A coluna da FOTO é a que carrega os slides; a de texto carrega o H1. */
#hero > .e-con > .e-con > .e-con:has(.jn-hero__slide) { order: -1; }
#hero .jn-lead { margin-top: var(--jn-s-5); }

/* Carrossel do herói: crossfade, slides empilhados no mesmo espaço.
   A caixa mantém a proporção, então não há salto de layout na troca. */
.e-con:has(> .jn-hero__slide) {
  --display: block;
  display: block;
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 10;      /* mobile */
  border-radius: var(--jn-r-media);
  overflow: hidden;
  background: var(--jn-shade);
}
.jn-hero__slide {
  position: absolute;
  inset: 0;
  opacity: 0;
  transition: opacity var(--jn-d-slow) var(--jn-ease-soft);
}
.jn-hero__slide.is-active { opacity: 1; }
/* A altura tem de atravessar o wrapper do widget do Elementor
   (.jn-hero__slide > .elementor-widget-container > img), senão a `height:100%`
   da imagem resolve contra um pai de altura automática e a foto estoura a
   moldura. Mesmo problema já corrigido nos cards de coleção. */
.jn-hero__slide .elementor-widget-container { height: 100%; }
.jn-hero__slide img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
}

@media (min-width: 861px) {
  #hero > .e-con > .e-con { grid-template-columns: 1.05fr 0.95fr; }
  #hero > .e-con > .e-con > .e-con:has(.jn-hero__slide) { order: 0; }
  .e-con:has(> .jn-hero__slide) { aspect-ratio: 5 / 4; }
}

/* Micro-linha de confiança — texto puro com separador "·".
   Sem ícones de propósito: os únicos ícones da página são os 3 diferenciais,
   desenhados à mão. Meter ícones genéricos aqui quebraria essa consistência.
   Como string única, o tradutor recebe a linha inteira com contexto. */
.jn-microtrust {
  margin-top: var(--jn-s-6);
  border-top: 1px solid var(--jn-line);
  padding-top: var(--jn-s-5);
}
.jn-microtrust p {
  font-size: var(--jn-t-small);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--jn-text);
}

/* ⚠️ PONTO TRAVADO #5 — a foto lidera no mobile, mas não pode empurrar a
   headline para baixo da dobra. Teto em dvh (não vh): no celular, 100vh
   inclui a barra do navegador e a conta sai errada.
   Base = mobile; o teto some no desktop. */
.jn-h1 .elementor-heading-title { max-width: 100%; }
.e-con:has(> .jn-hero__slide) { max-height: 38dvh; }

@media (min-width: 861px) {
  /* 24ch, não 20: a Fraunces é mais larga que a Magra que estava aqui antes,
     e a 20ch a headline quebrava em 4 linhas contra 3 da foto ao lado. */
  .jn-h1 .elementor-heading-title { max-width: 24ch; }
  .e-con:has(> .jn-hero__slide) { max-height: none; }
}

/* --------------------------------------------------------------------------
   7c. CONTROLE PAUSAR/TOCAR dos carrosséis
   ⚠️ NÃO remover. É o que mantém a rotação automática em conformidade com a
   WCAG 2.2.2 (Pause, Stop, Hide): movimento automático que dura mais de 5s ao
   lado de outro conteúdo precisa de um mecanismo de pausa. O rótulo do botão
   vem de um nó de texto (.jn-i18n), então é traduzível pelo WPML.
   -------------------------------------------------------------------------- */
.jn-carousel-toggle {
  position: absolute;
  right: var(--jn-s-3);
  bottom: var(--jn-s-3);
  z-index: 2;
  width: 44px; height: 44px;      /* alvo de toque confortável */
  display: inline-flex;
  align-items: center; justify-content: center;
  border: none;
  border-radius: var(--jn-r-pill);
  background: var(--jn-terracotta);
  color: var(--jn-on-action);
  cursor: pointer;
  transition: background-color var(--jn-d-base) var(--jn-ease-soft);
}
.jn-carousel-toggle:hover { background: var(--jn-ink-deep); }
.jn-carousel-toggle:focus-visible {
  outline: 3px solid var(--jn-ink-deep);
  outline-offset: 3px;
}
/* Glifos em CSS puro: sem fonte de ícone, sem string na árvore de
   acessibilidade (o nome acessível vem do aria-label). */
.jn-carousel-toggle::before {
  content: "";
  display: block;
}
.jn-carousel-toggle[data-state="playing"]::before {   /* ícone de pausa */
  width: 12px; height: 14px;
  border-left: 4px solid currentColor;
  border-right: 4px solid currentColor;
}
.jn-carousel-toggle[data-state="paused"]::before {    /* ícone de play */
  width: 0; height: 0;
  margin-left: 3px;
  border-top: 7px solid transparent;
  border-bottom: 7px solid transparent;
  border-left: 12px solid currentColor;
}

/* O host do carrossel de depoimentos precisa ancorar o botão. */
.jn-carousel-host { position: relative; }
.jn-carousel-host .jn-carousel-toggle {
  top: calc(-1 * var(--jn-s-8));
  bottom: auto;
  right: 0;
  background: var(--jn-ink-deep);   /* sobre a banda sálvia */
}
.jn-carousel-host .jn-carousel-toggle:hover { background: var(--jn-terracotta); }

/* Rótulos traduzíveis dos botões: presentes no DOM, invisíveis na tela. */
.jn-i18n {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

/* --------------------------------------------------------------------------
   7b. FAIXA DE PROVA SOCIAL (Rev.01)
   Fica em creme como o herói: são números que SUSTENTAM o herói, não uma
   seção nova de assunto. Quem separa são as fiadas de 1px, não uma cor.
   Pêssego seria a alternativa, mas encostaria na banda sálvia seguinte, e
   sálvia/pêssego têm luminância idêntica (ver §4): não haveria fronteira.
   -------------------------------------------------------------------------- */
#proof {
  border-top: 1px solid var(--jn-line);
  border-bottom: 1px solid var(--jn-line);
}
#proof > .e-con > .e-con {
  --display: grid;
  display: grid;
  grid-template-columns: repeat(2, 1fr);   /* base: mobile */
  gap: clamp(20px, 3vw, 40px);
  text-align: center;
}
@media (min-width: 721px) { #proof > .e-con > .e-con { grid-template-columns: repeat(4, 1fr); } }
.e-con:has(> .jn-stat__n) { --display: block; display: block; }
/* Número em Magra: precisa ler como dado confiável. Amatic aqui soaria
   decorativo e enfraqueceria justamente a autoridade que a faixa existe pra dar. */
.jn-stat__n p {
  font-family: var(--jn-font-head);
  font-size: clamp(1.9rem, 1.2rem + 2.4vw, 2.75rem);
  line-height: 1.05;
  color: var(--jn-ink);
}
.jn-stat__l p {
  font-size: var(--jn-t-eyebrow);
  font-weight: 700;
  letter-spacing: var(--jn-ls-eyebrow);
  text-transform: uppercase;
  color: var(--jn-ink-deep);
  margin-top: var(--jn-s-2);
}
/* --------------------------------------------------------------------------
   8. DIFERENCIAIS (3 itens — Rev.01)
   -------------------------------------------------------------------------- */
.e-con:has(> .e-con > .jn-feature__icon) {
  --display: grid;
  display: grid;
  grid-template-columns: 1fr;              /* base: mobile */
  gap: clamp(24px, 3.4vw, 48px);
}
/* Com 3 itens, 3-up aguenta a partir de ~641px: as colunas ficam com
   190-280px e os títulos quebram em 2 linhas, que o subgrid realinha. */
@media (min-width: 641px) { .e-con:has(> .e-con > .jn-feature__icon) { grid-template-columns: repeat(3, 1fr); } }
/* ⚠️ PONTO TRAVADO #4 — alinha ícone/título/descrição entre as colunas mesmo
   com títulos de alturas diferentes. Subgrid em vez de min-height: min-height
   quebra na tradução (o alemão é mais longo), subgrid não. */
@supports (grid-template-rows: subgrid) {
  .e-con:has(> .e-con > .jn-feature__icon) { grid-template-rows: auto auto auto; }
  .e-con:has(> .e-con > .jn-feature__icon) > .e-con:has(> .jn-feature__icon) {
    --display: grid;
    display: grid;
    grid-row: span 3;
    grid-template-rows: subgrid;
    gap: 0;
  }
}
/* Ícones desenhados à mão, SVG inline via widget `html` (Rev.01 §3.2).
   Traço 1.1 e curvas orgânicas: os Font Awesome anteriores eram duros e
   geométricos demais para uma marca de artesanato. Herdam currentColor. */
.jn-feature__icon {
  margin-bottom: var(--jn-s-4);
  color: var(--jn-terracotta);
  line-height: 0;
}
/* 48px: linha de 1.2 num desenho delicado precisa de área para respirar.
   A 32px o traço some e o motivo vira borrão. */
.jn-feature__icon svg { width: 48px; height: 48px; display: block; }
.e-con:has(> .jn-feature__icon) .jn-h3 { margin-bottom: var(--jn-s-2); }
.e-con:has(> .jn-feature__icon) p { font-size: var(--jn-t-small); line-height: 1.6; }


/* --------------------------------------------------------------------------
   9. CARDS DE COLEÇÃO — foto domina, moldura recua
   (REFERENCE §A.5: cantos muito arredondados + borda colorida = datado)
   -------------------------------------------------------------------------- */
/* GRADE DE CARDS — era `.jn-card-grid`. São duas: a de #collections, que
   contém cards, e a de #new-arrivals, que contém só o placeholder `.jn-slot`
   à espera do widget de produtos. Por isso os dois seletores. */
.e-con:has(> .e-con > .e-con > .jn-card__title),
.e-con:has(> .jn-slot) {
  --display: grid;
  display: grid;
  grid-template-columns: 1fr;              /* base: mobile */
  gap: clamp(20px, 2.6vw, 32px);
}
@media (min-width: 541px) {
  .e-con:has(> .e-con > .e-con > .jn-card__title),
  .e-con:has(> .jn-slot) { grid-template-columns: repeat(2, 1fr); }
}
/* ⚠️ 3 e não 4. As coleções da home eram 4 e viraram 3 quando "Custom /
   Personalized" saiu do site. Em repeat(4,...) as três cartas deixariam uma
   coluna vazia à direita, que lê como erro de carregamento. Se um dia entrar
   uma 4ª coleção, voltar este número junto. */
@media (min-width: 981px) {
  .e-con:has(> .e-con > .e-con > .jn-card__title),
  .e-con:has(> .jn-slot) { grid-template-columns: repeat(3, 1fr); }
}
.e-con:has(> .e-con > .jn-card__title) {
  --display: flex;
  display: flex;
  flex-direction: column;
  position: relative;                 /* âncora do overlay do link */
  background: var(--jn-white);
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-card);
  overflow: hidden;
  color: inherit;
  transition:
    transform  var(--jn-d-base) var(--jn-ease),
    box-shadow var(--jn-d-base) var(--jn-ease);
}
/* ⚠️ CRÍTICO — NÃO remover. O overlay `inset:0` do link ancora no ancestral
   POSICIONADO mais próximo. Tanto `.e-con` (container) quanto o wrapper de
   widget do Elementor trazem `position: relative`; sem zerar TODO o caminho
   entre a <a> e o .jn-card, o overlay encolhe para a caixa do título e a FOTO
   deixa de ser clicável. Verificado com elementFromPoint no centro da foto.

   ⚠️ Rev.02 — este era o pior caso de todos. `.jn-card__media` e
   `.jn-card__body` eram containers, ou seja, o `position: static` NUNCA foi
   aplicado no site: o overlay do link encolhia para a caixa do título e a
   foto do card não era clicável. O ponto travado #9 estava quebrado no ar
   desde a primeira entrega, e nada na tela denunciava. */
.e-con:has(> .e-con > .jn-card__title) > .e-con,
.jn-card__title,
.jn-card__title .elementor-widget-container { position: static; }

/* FOTO DO CARD — o filho do card que carrega a imagem (o outro é o corpo). */
.e-con:has(> .e-con > .jn-card__title) > .e-con:has(> .elementor-widget-image) {
  aspect-ratio: 4 / 5;
  overflow: hidden;
  background: var(--jn-shade);
  width: 100%;
}
/* A altura tem de atravessar o wrapper do widget: <div .elementor-widget-image>
   > <div .elementor-widget-container> > <img>. Sem isto a `height:100%` da
   imagem resolve contra um pai de altura automática e a foto colapsa. */
.e-con:has(> .e-con > .jn-card__title) > .e-con > .elementor-widget-image,
.e-con:has(> .e-con > .jn-card__title) > .e-con > .elementor-widget-image > .elementor-widget-container { height: 100%; }
.e-con:has(> .e-con > .jn-card__title) img {
  width: 100%; height: 100%;
  object-fit: cover; display: block;
  transition: transform var(--jn-d-slow) var(--jn-ease);
}
/* CORPO DO CARD — o filho que carrega o título. */
.e-con:has(> .jn-card__title) {
  --display: flex;
  display: flex;
  flex-direction: column;
  padding: var(--jn-s-5);
  flex: 1;
}
.e-con:has(> .jn-card__title) .jn-h3 { margin-bottom: var(--jn-s-2); }
.e-con:has(> .jn-card__title) p { font-size: var(--jn-t-small); line-height: 1.6; }

/* ⚠️ PONTO TRAVADO #9 — card inteiro clicável com UM ÚNICO link no DOM.
   O <a> vive no <h3> e um pseudo-elemento cobre o card. Nada de link aninhado:
   o leitor de tela anuncia um item só, com o nome da coleção. */
.jn-card__title a {
  color: var(--jn-heading);
  text-decoration: none;
}
.jn-card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: var(--jn-r-card);
}
.jn-card__title a:hover { text-decoration: underline; text-underline-offset: 3px; }

/* "Shop napkins" é rótulo visual, não link — o card todo já é o alvo. */
.jn-card__more { margin-top: auto; padding-top: var(--jn-s-4); }
.jn-card__more p {
  display: inline-flex; align-items: center; gap: var(--jn-s-2);
  font-size: var(--jn-t-small); font-weight: 700;
  color: var(--jn-terracotta);
  margin: 0;
}
/* ⚠️ PONTO TRAVADO #13 — chevron decorativo em CSS puro: usa currentColor, não
   injeta texto na árvore de acessibilidade e não prende string traduzível. */
.jn-card__more p::after {
  content: "";
  width: 0.42em; height: 0.42em;
  border-right: 2px solid currentColor;
  border-top: 2px solid currentColor;
  transform: rotate(45deg);
  transition: transform var(--jn-d-base) var(--jn-ease);
}

.e-con:has(> .e-con > .jn-card__title):hover { transform: translateY(-4px); box-shadow: 0 12px 28px var(--jn-shadow); }
/* A única <img> dentro de um card é a da foto — não precisa reidentificar. */
.e-con:has(> .e-con > .jn-card__title):hover img { transform: scale(1.04); }
.e-con:has(> .e-con > .jn-card__title):hover .jn-card__more p::after { transform: rotate(45deg) translate(3px, -3px); }

/* Foco: o anel aparece no CARD, não no texto do h3 */
.e-con:has(> .e-con > .jn-card__title):focus-within { outline: 3px solid var(--jn-terracotta); outline-offset: 3px; }
.jn-card__title a:focus-visible { outline: none; }


/* Placeholder de bloco dinâmico — sai quando o Woo / Elementor Pro entra */
.jn-slot {
  grid-column: 1 / -1;
  border: 1px dashed var(--jn-line-strong);
  border-radius: var(--jn-r-card);
  min-height: 180px;
  display: grid; place-items: center;
  color: var(--jn-text);
  font-size: var(--jn-t-small);
  text-align: center;
  padding: var(--jn-s-6);
  background: var(--jn-shade);
}
.jn-slot p { margin: 0; max-width: 46ch; }

/* --------------------------------------------------------------------------
   10. DEPOIMENTOS — editorial, não balão
   -------------------------------------------------------------------------- */
/* CARROSSEL — scroll-snap nativo, sem plugin de slider e sem JS de animação.
   Por quê assim:
   · Elementor Pro (que traz o widget de carrossel) ainda não está instalado.
   · Rolagem nativa já funciona com toque, trackpad, barra e teclado.
   · Respeita prefers-reduced-motion de graça (nada se move sozinho).
   · Cada depoimento continua sendo um nó de texto próprio, traduzível.
   · SEM auto-rotação de propósito: carrossel que gira sozinho é anti-padrão
     (WCAG 2.2.2 Pause/Stop/Hide) e some com o conteúdo antes de ser lido.
   O card cortado na borda direita é a affordance de "tem mais para o lado". */
.e-con:has(> .e-con > .jn-quote__text) {
  --display: flex;
  display: flex;
  flex-direction: row;
  gap: clamp(20px, 2.6vw, 32px);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scroll-snap-type: x mandatory;
  scroll-padding-left: 0;
  padding-bottom: var(--jn-s-5);
  scrollbar-width: thin;
  scrollbar-color: var(--jn-ink-deep) transparent;
}
/* Base = mobile: o card ocupa 84% da largura, então o próximo espia e o
   gesto de arrastar fica óbvio sem precisar de setas. */
.e-con:has(> .e-con > .jn-quote__text) > .e-con {
  flex: 0 0 84%;
  scroll-snap-align: start;
}
@media (min-width: 561px) {
  .e-con:has(> .e-con > .jn-quote__text) > .e-con { flex-basis: clamp(268px, 31%, 380px); }
}
.e-con:has(> .e-con > .jn-quote__text)::-webkit-scrollbar { height: 6px; }
.e-con:has(> .e-con > .jn-quote__text)::-webkit-scrollbar-track {
  background: var(--jn-shade);
  border-radius: var(--jn-r-pill);
}
.e-con:has(> .e-con > .jn-quote__text)::-webkit-scrollbar-thumb {
  background: var(--jn-ink-deep);
  border-radius: var(--jn-r-pill);
}
/* O scroller recebe tabindex via jana-home.js; o anel tem de aparecer. */
.e-con:has(> .e-con > .jn-quote__text):focus-visible {
  outline: 3px solid var(--jn-ink-deep);
  outline-offset: 4px;
  border-radius: var(--jn-r-card);
}
/* O widget html que injeta os atributos não ocupa espaço. */
.jn-a11y-hook { display: none; }

/* DEPOIMENTO — era `.jn-quote`. */
.e-con:has(> .jn-quote__text) {
  --display: flex;
  display: flex;
  flex-direction: column;
  gap: var(--jn-s-5);
  margin: 0;
  background: var(--jn-white);
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-card);
  padding: clamp(28px, 3.4vw, 44px);
}
/* Sem aspa decorativa: em Amatic o glifo sai pequeno e lê como sujeira, não
   como ornamento. O <blockquote> + a atribuição já dizem que é depoimento.
   Evita também injetar `content` de texto na árvore de acessibilidade. */
.jn-quote__text blockquote {
  margin: 0;
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-lead);
  line-height: 1.55;
  color: var(--jn-ink-deep);
  max-width: none;
}
/* ASSINATURA — era `.jn-quote__by`: o container que leva o nome (dentro de
   `.jn-quote__id`) e, quando existe, o avatar. */
.e-con:has(> .e-con > .jn-quote__name) {
  --display: flex;
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: var(--jn-s-3);
  margin-top: auto;
  width: auto;
}
.jn-quote__avatar { flex: none; width: auto; }
.jn-quote__avatar img {
  width: 48px; height: 48px;
  border-radius: var(--jn-r-pill);
  object-fit: cover;
  background: var(--jn-sage);
  display: block;
}
.e-con:has(> .jn-quote__name) { --display: flex; display: flex; flex-direction: column; width: auto; }
.jn-quote__name p {
  font-weight: 700; font-size: var(--jn-t-small);
  color: var(--jn-ink-deep); margin: 0;
}
.jn-quote__role p {
  font-size: var(--jn-t-small);
  color: var(--jn-ink-deep);
  /* ⚠️ AQUI HAVIA `opacity: 0.8`, justificado por um cálculo SOBRE BRANCO
     (8,33:1 × 0,8 ≈ 5,8:1). O cálculo estava certo e a premissa errada: os
     depoimentos vivem na banda SÁLVIA, não em branco. Sobre sálvia o mesmo
     0,8 dá 3,46:1 e reprova em AA. Medido pelo axe.
     Regra do sistema: sobre --jn-sage e --jn-peach, texto em --jn-ink-deep
     CHEIO. A diferença de hierarquia vem do CORPO menor, que já basta. */
  margin: 0;
}

/* Lacuna de depoimento — marcada para ser IMPOSSÍVEL subir sem perceber.
   Conteúdo placeholder em produção é achado Crítico no design-auditor. */
/* LACUNA — era `.jn-quote--placeholder`. O que distingue um depoimento vazio
   de um real no DOM é a AUSÊNCIA de avatar: o modificador era classe de
   container e não chegava. (0,3,0) vence o depoimento normal (0,2,0). */
.e-con:has(> .jn-quote__text):not(:has(.jn-quote__avatar)) {
  background: transparent;
  border: 1px dashed var(--jn-line-strong);
  box-shadow: none;
}
/* ⚠️ SEM `opacity` AQUI. A versão anterior usava `opacity: .75` para marcar
   que é lacuna, e sobre a banda sálvia isso misturava o texto com o fundo:
   3,17:1, reprovado pelo axe. O placeholder já se anuncia pela borda
   tracejada e pelo avatar vazio; não precisa apagar a letra.
   Regra geral do sistema: sobre --jn-sage e --jn-peach, texto sempre em
   --jn-ink-deep CHEIO. Transparência é o caminho mais fácil de reprovar. */
.e-con:has(> .jn-quote__text):not(:has(.jn-quote__avatar)) .jn-quote__text blockquote,
.e-con:has(> .jn-quote__text):not(:has(.jn-quote__avatar)) .jn-quote__name p,
.e-con:has(> .jn-quote__text):not(:has(.jn-quote__avatar)) .jn-quote__role p { font-style: italic; }
.e-con:has(> .jn-quote__text):not(:has(.jn-quote__avatar)) .e-con:has(> .e-con > .jn-quote__name)::before {
  content: "";
  width: 48px; height: 48px; flex: none;
  border: 1px dashed var(--jn-line-strong);
  border-radius: var(--jn-r-pill);
}


/* --------------------------------------------------------------------------
   11. MEET JANA
   -------------------------------------------------------------------------- */
#about > .e-con > .e-con {
  --display: grid;
  display: grid;
  grid-template-columns: 1fr;              /* base: mobile */
  gap: clamp(24px, 5vw, 80px);
  align-items: center;
}
@media (min-width: 861px) { #about > .e-con > .e-con { grid-template-columns: 0.85fr 1.15fr; } }
#about > .e-con > .e-con > .e-con { min-width: 0; }
/* ⚠️ PONTO TRAVADO #8 — sem aspect-ratio forçado: a foto da Jana é retrato
   (0,69) e recortá-la em 4:5 corta o rosto. Proporção natural = qualquer foto
   que o Davi enviar depois entra sem recorte cego. */
.jn-about__photo img {
  width: 100%; height: auto; display: block;
  border-radius: var(--jn-r-media);
  max-height: 620px;
  object-fit: cover;
}
#about .jn-lead { margin-top: var(--jn-s-5); }
.jn-about__signature .elementor-heading-title {
  font-family: var(--jn-font-display);
  /* ⚠️ 700 EXPLÍCITO, e isso tem custo de rede atrás. Antes este peso era
     herdado (400), e era o ÚNICO lugar do site inteiro usando Amatic SC 400:
     um arquivo de fonte de 28 KB baixado por causa de uma assinatura. Medido.
     Com 700 a assinatura passa a usar o mesmo arquivo de todo o resto.
     Se um dia voltar para 400, TEM de vir junto um face de 400 em
     `fonts/jana-fonts.css` (hoje só existe o 700, e o gerador em
     `scratchpad/baixar-fontes.mjs` pede só o 700 ao Google). Sem isso a
     assinatura cai em fonte de sistema sem erro nenhum na tela. */
  font-weight: 700;
  font-size: 2.25rem;
  color: var(--jn-terracotta);
}
.jn-about__signature { margin-top: var(--jn-s-5); }


/* --------------------------------------------------------------------------
   12. CONTROLES DE FORMULÁRIO
   A seção Newsletter saiu da home na Rev.01 (sem ferramenta de e-mail ainda).
   Estes estilos FICAM de propósito: o custom.css é site-wide e a skin do
   checkout WooCommerce precisa exatamente disto (REFERENCE §D). Apagá-los
   perderia o ponto travado #12 e o bug voltaria no checkout.
   -------------------------------------------------------------------------- */

/* ⚠️ PONTO TRAVADO #12 — input com BORDA VISÍVEL. O bug do checkout BR
   (pílula pêssego sem borda, fundindo com o fundo) não pode se repetir:
   borda sólida de 2px, contraste bem acima de 3:1. */
.jn-input,
.woocommerce form .form-row input.input-text,
.woocommerce form .form-row textarea,
.woocommerce form .form-row select {
  min-height: 48px;
  padding: 0 var(--jn-s-4);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  color: var(--jn-ink-deep);
  background: var(--jn-white);
  border: 2px solid var(--jn-ink-deep);
  border-radius: var(--jn-r-pill);
}
/* .7 dava 3,84:1 sobre branco e reprovava AA. Ver a conta completa no §21.2:
   o piso é .765, e todos os placeholders do sistema usam .8. */
.jn-input::placeholder { color: var(--jn-ink-deep); opacity: 0.8; }
.jn-input:focus-visible,
.woocommerce form .form-row input.input-text:focus-visible {
  outline: 3px solid var(--jn-ink-deep);
  outline-offset: 2px;
}

/* --------------------------------------------------------------------------
   13. FAIXA DE CONFIANÇA (pé da home) — texto puro, mesmo critério da
   micro-linha do herói: os únicos ícones da página são os 3 diferenciais.
   -------------------------------------------------------------------------- */
.jn-trustbar p {
  font-size: var(--jn-t-small);
  font-weight: 600;
  letter-spacing: 0.03em;
  color: var(--jn-ink-deep);
  margin-inline: auto;
}

/* --------------------------------------------------------------------------
   14. MOTION — entrada sóbria de seção
   -------------------------------------------------------------------------- */
@keyframes jn-rise {
  from { opacity: 0; transform: translateY(14px); }
  to   { opacity: 1; transform: none; }
}
/* ⚠️⚠️ REV.02 — A ENTRADA DE SEÇÃO ESTÁ DESLIGADA, DE PROPÓSITO. NÃO RELIGAR
   sem ler isto inteiro.

   Primeiro: `.jn-reveal` existia SÓ no preview.html. Nunca esteve em nenhum
   dos três JSON, então este bloco NUNCA rodou no site — era motion aprovado
   em revisão e ausente no ar desde a primeira entrega.

   Ao reancorar tudo, reativei a regra na CASCA (não na seção: a seção pinta
   a faixa de cor, e animar `opacity` nela faria a banda sálvia piscar junto).
   Medi no navegador e o resultado foi este:

       a ÚLTIMA seção (#trust) fica em opacity 0 PARA SEMPRE.

   A causa é estrutural, não um ajuste de número: `view()` com
   `animation-range: entry 0% cover 22%` exige que o elemento percorra a
   janela. A última seção da página é curta e encosta no fim do documento —
   não há scroll restante para ela cumprir o intervalo, então o
   `animation-fill-mode: both` prende o estado inicial. Tirar o `both` não
   resolve: fora do intervalo a animação deixa de aplicar, o elemento nasce
   visível e PISCA para 0 ao entrar.

   Isso é exatamente o modo de falha do ponto travado #2 — seção
   permanentemente em branco — só que vindo do nosso lado, não do Elementor.
   Conteúdo invisível é pior do que ausência de motion, então fica desligado.

   Se o operador quiser a entrada de volta, o caminho é IntersectionObserver
   somando uma classe: o estado BASE é visível e a animação é o acréscimo,
   de modo que qualquer falha deixa o conteúdo na tela. Não é uma troca de
   linha aqui; é uma decisão de motion para uma rodada própria.

   Os @keyframes ficam: não custam nada e são a peça pronta para essa rodada.
*/

/* --------------------------------------------------------------------------
   15. PREFERS-REDUCED-MOTION
   ⚠️ PONTO TRAVADO #2 — ARMADILHA DO ELEMENTOR. NÃO remover o bloco
   .elementor-invisible. As animações de entrada do Elementor aplicam
   `.elementor-invisible { visibility: hidden }` e só revelam via JS no scroll.
   Se a animação for desligada SEM desfazer o hidden, o usuário com
   reduced-motion fica com seções PERMANENTEMENTE EM BRANCO.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .elementor-invisible { visibility: visible !important; }
  .animated,
  [class*="elementor-animation-"] {
    animation: none !important;
    transition: none !important;
  }
  .e-con:where(.e-parent[id]) *,
  .e-con:where(.e-parent[id]) *::before,
  .e-con:where(.e-parent[id]) *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
  /* Rede de segurança: a entrada de seção está desligada (§14), mas se
     alguém a religar, esta linha garante que reduced-motion não deixe
     nenhuma casca presa em opacity 0. */
  .e-con.e-parent[id] > .e-con { animation: none !important; opacity: 1 !important; transform: none !important; }
  /* Hover de deslocamento vira hover de cor — o estado continua legível */
  .e-con:has(> .e-con > .jn-card__title):hover,
  .jn-btn .elementor-button:hover { transform: none !important; }
}

/* --------------------------------------------------------------------------
   16. CORREÇÕES BRAND-WIDE (fora do JSON da home)
   ⚠️ PONTO TRAVADO #10 — o verde #61CE70 é estado default do Elementor e vive
   em header.json e single-product.json. Como este arquivo é site-wide,
   neutralizamos aqui. Fix definitivo = editar aqueles templates
   (ver README → Ações do operador).
   -------------------------------------------------------------------------- */
.elementor-nav-menu .elementor-item:hover,
.elementor-nav-menu .elementor-item:focus,
.elementor-nav-menu .elementor-item.elementor-item-active,
.elementor-nav-menu .elementor-item.highlighted {
  color: var(--jn-terracotta) !important;
}
.elementor-search-form__submit:hover {
  background-color: var(--jn-ink-deep) !important;
}
/* Menu em uma linha só (brief §3) + segue o token de título.
   Sem isto o menu continuaria em Magra (declarada no header.json) enquanto o
   resto do site usa Fraunces, e a troca de fonte ficaria pela metade. */
.elementor-nav-menu--main .elementor-nav-menu > li > a {
  white-space: nowrap;
  font-family: var(--jn-font-head);
}

/* Banner compacto em páginas internas (REFERENCE §A.1) — cheio só na home */
body:not(.home) .jn-brand-banner img { max-height: 96px; width: auto; }

/* --------------------------------------------------------------------------
   17. UTILITÁRIOS
   -------------------------------------------------------------------------- */
.jn-visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0 0 0 0);
  white-space: nowrap; border: 0;
}

/* ==========================================================================
   17b. BASE DAS PÁGINAS DO WOOCOMMERCE QUE NÃO SÃO ELEMENTOR  (Rev.02)
   --------------------------------------------------------------------------
   ⚠️ DEFEITO ANTIGO, ENCONTRADO AO REANCORAR — não é uma peça nova de design.

   §2 e §3 sempre estiveram escopados em `.jn-section`. Carrinho, checkout e
   (sem Elementor Pro) loja e produto NÃO são páginas do Elementor: medido,
   `/cart/` e `/checkout/` têm ZERO `.e-con` e zero `.jn-section`. Ou seja,
   nessas quatro páginas a tipografia-base do sistema NUNCA foi aplicada —
   o <body> real mede `-apple-system, …`, fonte do sistema, e não Raleway.
   Os previews de carrinho e checkout escondiam isso porque eles próprios
   envolviam tudo num `<main class="jn-section">` que o site não tem.

   Este bloco repõe, para essas páginas, EXATAMENTE as mesmas declarações que
   `.jn-section` e `.jn-shell` davam. Nada de novo, nada a mais: é o desenho
   que já tinha sido aprovado nos previews, chegando enfim onde deveria.

   §18 a §21 vêm DEPOIS e continuam mandando em tudo que é específico.

   O `main#content.site-main` do Hello Elementor já vem com max-width 1140px
   (medido). Os 1200px daqui são o token da marca, o mesmo da casca da home,
   para as páginas não trocarem de medida no meio do funil.
   ========================================================================== */
body:is(.woocommerce-cart, .woocommerce-checkout, .post-type-archive-product, .single-product) {
  color: var(--jn-text);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  line-height: var(--jn-lh-body);
  background: var(--jn-creme);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  overflow-wrap: break-word;
}
/* (0,1,1) como no §2 — mesma conta, mesmos contratos de §18.1/§20/§21. */
body:where(.woocommerce-cart, .woocommerce-checkout, .post-type-archive-product, .single-product) :is(h1, h2, h3, h4) {
  font-family: var(--jn-font-head);
  color: var(--jn-heading);
  line-height: var(--jn-lh-head);
  font-weight: 400;
  margin: 0;
  font-optical-sizing: auto;
}
body:where(.woocommerce-cart, .woocommerce-checkout, .post-type-archive-product, .single-product) p { margin: 0; }
body:where(.woocommerce-cart, .woocommerce-checkout, .post-type-archive-product, .single-product) :is(a, button) {
  -webkit-tap-highlight-color: rgba(141, 78, 42, 0.15);
}
/* ⚠️ DOIS <h1> NO CARRINHO E NO CHECKOUT — achado desta rodada.
   O comentário do `snippets-cart.php` diz "medido no site BR: a página do
   carrinho não tem NENHUM <h1>", e por isso ele injeta um. Verdade lá; falso
   AQUI. Medido em `/cart/` na instalação europeia: o Hello Elementor imprime
   `div.page-header > h1.entry-title` ("Cart") por conta própria. Com o snippet
   instalado a página passa a ter DOIS h1 — e o §17b acima, ao dar a fonte da
   marca aos headings, faria a duplicata parecer intencional.

   O `:has()` aqui é o que torna a regra segura nos dois sentidos: o título do
   tema só se apaga QUANDO o nosso existe. Se os snippets ainda não estiverem
   instalados, nada some e a página continua com exatamente um h1 — nunca
   nenhum, que seria trocar um defeito menor por um pior.

   Não é verificável ponta a ponta daqui: exige os snippets no ar e o carrinho
   trocado de blocos para shortcode (INSTALL, passo 7a). Ver README-REV02 §5.7. */
body.woocommerce-cart main:has(.jn-cart-title) .page-header,
body.woocommerce-cart main:has(.jn-empty-cart__title) .page-header,
body.woocommerce-checkout main:has(.jn-checkout-title) .page-header { display: none; }

/* Casca: era `.jn-shell` no preview, e no site é o wrapper do tema.
   ⚠️ O `:not(:has(.e-con))` NÃO é enfeite. Com o Elementor Pro, loja e
   produto passam a ter o template do Theme Builder DENTRO deste mesmo
   `main`, e aí quem dá caixa e respiro é a seção `#shop`/`#product` (§3).
   Sem esta guarda as duas camadas se somam e a página ganha padding dobrado
   no dia em que o Pro for instalado — um bug que só apareceria lá na frente,
   sem nada ligando uma coisa à outra. Medido nos dois previews. */
body:is(.woocommerce-cart, .woocommerce-checkout, .post-type-archive-product, .single-product) main#content:not(:has(.e-con)),
body:is(.woocommerce-cart, .woocommerce-checkout, .post-type-archive-product, .single-product) main#main:not(:has(.e-con)) {
  width: 100%;
  /* ⚠️ `container + 2 × gutter`, NÃO `container`. Na home são DUAS caixas: a
     seção põe o respiro lateral e a casca, POR DENTRO dela, limita o texto a
     1200px. Aqui só existe uma caixa, e com `box-sizing: border-box` o
     padding come a largura: `max-width: 1200px` deixaria 1104px de conteúdo.
     Medido — a coluna de produto do carrinho caiu de 236px para 172px, num
     layout que já tinha sido auditado a 1200. Esta conta devolve os 1200. */
  max-width: calc(var(--jn-container) + 2 * var(--jn-gutter));
  margin-inline: auto;
  padding-block: var(--jn-section-y);
  padding-inline: max(var(--jn-gutter), env(safe-area-inset-left));
  padding-right: max(var(--jn-gutter), env(safe-area-inset-right));
}

/* ==========================================================================
   18. WOOCOMMERCE — ARQUIVO DE CATEGORIA / LOJA
   --------------------------------------------------------------------------
   Skin sobre a marcação do Woo. NÃO reconstrói a listagem: estiliza o loop
   nativo, então filtros, paginação, ordenação e WPML continuam funcionando.

   ⚠️ CONTRATO DE RENDERIZAÇÃO — quem manda aqui é o WOOCOMMERCE, não o
   Elementor. O loop nativo produz:
       ul.products > li.product
         > a.woocommerce-loop-product__link  (img + h2 + .price)
         > a.add_to_cart_button              (SEGUNDO link, fora do primeiro)
   São 2 elementos interativos por card, e isso está CERTO numa loja: um leva
   ao produto, o outro compra. Não aplicar aqui o truque de overlay usado nos
   cards da home, que cobriria o botão de comprar.

   Funciona SEM Elementor Pro. Com Pro, o template de arquivo
   (archive-eu.json) só organiza o entorno; estas regras seguem valendo.
   ========================================================================== */

/* --- 18.0 Texto só para leitor de tela -----------------------------------
   ⚠️ NÃO remover achando que é redundante. O WooCommerce usa
   .screen-reader-text para carregar o sentido do preço em promoção
   ("Original price was: X", "Current price is: Y"), porque o <del>/<ins>
   visual é aria-hidden. Quem esconde essa classe normalmente é a folha de
   estilo do próprio Woo, MAS setups Hello + Elementor costumam desenfileirar
   o CSS do Woo para evitar conflito. Quando isso acontece, as frases vazam
   na tela, dentro do card, e o preço vira uma bagunça.
   Visto acontecer no preview. A skin declara por conta própria. */
.screen-reader-text {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- 18.1 Casca da loja -------------------------------------------------
   ⚠️ Rev.02 — as classes de container deste template morrem igual às da home
   (`jn-shop`, `jn-shop__header`, `jn-shop__layout`, `jn-shop__main`,
   `jn-shop-sidebar`, `jn-shop__toolbar`: 9 no total). Reancoradas em §0.
   A seção é `#shop` (`_element_id`, mesma via da home).
   Note que a sidebar por dentro NÃO precisa de `:has()`: o menu tem a classe
   de WIDGET `.jn-shop-sidebar__menu`, que sobrevive e pesa exatamente o
   mesmo que `.jn-shop-sidebar` pesava. */
.e-con:has(> .woocommerce-products-header__title) { margin-bottom: var(--jn-s-7); }

/* ⚠️ PONTO TRAVADO #14 — o título do arquivo é DINÂMICO e tem DUAS marcações.
   NÃO remover nenhum dos dois seletores, e NÃO "simplificar" para um só:
     1) sem Pro, o Woo imprime a própria
        <h1 class="woocommerce-products-header__title page-title">
        → casa o seletor de DUAS CLASSES;
     2) com Pro, o widget Archive Title põe essas classes no WRAPPER e o texto
        vive numa <h1 class="elementor-heading-title"> dois níveis abaixo
        → casa o seletor DESCENDENTE.
   Sem o descendente, o título muda de fonte no dia em que o Pro entrar, e
   ninguém liga uma coisa à outra (é o bug do ponto travado #3, na home).

   ⚠️ As DUAS CLASSES no primeiro seletor são obrigatórias, não estilo. Uma só
   dá (0,1,0) e PERDE para `.jn-section :is(h1,h2,h3,h4)` (0,1,1) lá em cima:
   foi exatamente assim que este título ficou renderizando Fraunces enquanto a
   regra pedia Amatic.

   ⚠️ O `body ` na frente também é obrigatório. Sem ele os dois seletores ficam
   em (0,2,0), EMPATANDO com o kit de fontes globais do Elementor
   (`.elementor-kit-N h1 { font-family: … }`), e num empate quem decide é a
   ORDEM DE CARREGAMENTO. Como este arquivo pode ser instalado no style.css do
   tema filho OU em Site Settings → Custom CSS, e essas duas posições caem em
   lados diferentes do kit, o resultado mudaria conforme o lugar da instalação.
   Com `body` vira (0,2,1) e ganha nos dois casos, sem mudar nada visualmente.

   FONTE: Fraunces (--jn-font-head), decisão deliberada. Amatic fica reservado
   ao "Categories" da sidebar; nome de categoria é rótulo funcional, muda a
   cada página e é traduzido pelo WPML. Duas peças em Amatic lado a lado
   embaralhariam a assinatura da marca.

   O texto NUNCA é escrito aqui nem no JSON: vem do nome do termo, por isso
   troca sozinho a cada categoria e o WPML traduz. */
body .woocommerce-products-header__title.page-title,
body .woocommerce-products-header__title .elementor-heading-title {
  font-family: var(--jn-font-head);
  /* peso 400 e entrelinha 1.22 são os valores que a página JÁ renderizava (vinham
     de `.jn-section :is(h1..h4)`, que vencia esta regra). Repetidos aqui de
     propósito: agora que a especificidade foi corrigida, escrever 700 mudaria o
     desenho do título que já foi aprovado. NÃO "ajustar" sem pedido. */
  font-weight: 400;
  font-size: var(--jn-t-display);
  line-height: var(--jn-lh-head);
  font-optical-sizing: auto;
  color: var(--jn-heading);
  margin: 0;
  /* nomes de categoria variam de "Seasonal" a "Brushes & Tools", e em busca o
     título vira uma frase inteira: quebra equilibrada em vez de órfã */
  text-wrap: balance;
}
.term-description p {
  margin-top: var(--jn-s-4);
  font-size: var(--jn-t-lead);
  line-height: 1.6;
  color: var(--jn-text);
  max-width: 60ch;
}

/* Grade da loja. MOBILE-FIRST: uma coluna; a sidebar vira uma faixa no topo.
   ⚠️ Sem Elementor Pro o tema não cria coluna lateral nenhuma. Se o template
   não existir, o grid simplesmente não casa e nada quebra. */
.e-con:has(> .e-con > .jn-shop__grid) { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--jn-s-7); }
@media (min-width: 901px) {
  .e-con:has(> .e-con > .jn-shop__grid) { grid-template-columns: 232px minmax(0, 1fr); gap: var(--jn-s-8); }
}
/* ⚠️ min-width:0 nos DOIS. Item de grid nasce com min-width:auto, ou seja,
   recusa encolher abaixo do próprio conteúdo. No mobile a sidebar vira uma
   fila de chips com `nowrap`: sem isto ela não rola, ela EMPURRA a página e
   cria overflow horizontal no site inteiro. Medido a 390px e 768px. */
.e-con:has(> .jn-shop__grid),
.e-con:has(> .jn-shop-sidebar__title) { min-width: 0; }

/* --- 18.2 Sidebar de categorias ----------------------------------------- */
/* Estiliza o widget de MENU do Elementor (foi assim que o operador montou a
   sidebar no BR: um menu do WP, não links soltos). */
.jn-shop-sidebar__title .elementor-heading-title {
  font-family: var(--jn-font-display);
  font-weight: 700;
  font-size: clamp(2rem, 1.4rem + 1.6vw, 2.5rem);
  line-height: 1.1;
  color: var(--jn-heading);
}
.jn-shop-sidebar__title { margin-bottom: var(--jn-s-4); }

.jn-shop-sidebar__menu .elementor-nav-menu {
  list-style: none;
  margin: 0; padding: 0;
  display: flex; flex-direction: column;
}
.jn-shop-sidebar__menu .elementor-nav-menu .elementor-item {
  display: block;
  padding: var(--jn-s-3) 0;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 600;
  color: var(--jn-ink-deep);
  text-decoration: none;
  border-bottom: 1px solid var(--jn-line);
  transition: color var(--jn-d-fast) var(--jn-ease-soft),
              padding-left var(--jn-d-base) var(--jn-ease);
}
.jn-shop-sidebar__menu .elementor-nav-menu .elementor-item:hover {
  color: var(--jn-terracotta);
  padding-left: var(--jn-s-2);
}
/* Categoria atual: peso + barra, nunca só cor (falha para daltônico) */
.jn-shop-sidebar__menu .elementor-nav-menu .elementor-item-active {
  color: var(--jn-terracotta);
  font-weight: 700;
  box-shadow: inset 3px 0 0 0 var(--jn-terracotta);
  padding-left: var(--jn-s-3);
}

/* Mobile: a lista de categorias vira faixa rolável horizontal, para não
   empurrar o grid de produtos para baixo da dobra. */
@media (max-width: 900px) {
  .jn-shop-sidebar__menu .elementor-nav-menu {
    flex-direction: row;
    flex-wrap: nowrap;
    gap: var(--jn-s-2);
    overflow-x: auto;
    scroll-snap-type: x proximity;
    padding-bottom: var(--jn-s-3);
    -webkit-overflow-scrolling: touch;
  }
  .jn-shop-sidebar__menu .elementor-nav-menu .elementor-item {
    white-space: nowrap;
    border: 1px solid var(--jn-line-strong);
    border-radius: var(--jn-r-pill);
    padding: var(--jn-s-2) var(--jn-s-4);
    scroll-snap-align: start;
  }
  .jn-shop-sidebar__menu .elementor-nav-menu .elementor-item-active {
    background: var(--jn-terracotta);
    color: var(--jn-on-action);
    box-shadow: none;
    padding-left: var(--jn-s-4);
  }
  .jn-shop-sidebar__menu .elementor-nav-menu .elementor-item:hover { padding-left: var(--jn-s-4); }
}

/* --- 18.3 Barra de contagem + ordenação --------------------------------- */
.e-con:has(> .jn-shop__count) {
  display: flex; flex-wrap: wrap;
  align-items: center; justify-content: space-between;
  gap: var(--jn-s-4);
  padding-bottom: var(--jn-s-5);
  margin-bottom: var(--jn-s-6);
  border-bottom: 1px solid var(--jn-line);
}
.woocommerce-result-count {
  margin: 0;
  font-size: var(--jn-t-small);
  color: var(--jn-ink-deep);
}
/* Chevron do select desenhado em bordas CSS: usa currentColor, não injeta
   texto na árvore de acessibilidade e não prende hex fora do :root. */
.woocommerce-ordering { position: relative; margin: 0; }
.woocommerce-ordering::after {
  content: "";
  position: absolute;
  right: var(--jn-s-4); top: 50%;
  width: .42em; height: .42em;
  margin-top: -.32em;
  border-right: 2px solid var(--jn-ink-deep);
  border-bottom: 2px solid var(--jn-ink-deep);
  transform: rotate(45deg);
  pointer-events: none;
}
.woocommerce-ordering select.orderby {
  appearance: none; -webkit-appearance: none;
  min-height: 48px;
  padding: 0 calc(var(--jn-s-7)) 0 var(--jn-s-4);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 600;
  color: var(--jn-ink-deep);
  background: var(--jn-white);
  border: 2px solid var(--jn-ink-deep);   /* borda visível: lição do checkout */
  border-radius: var(--jn-r-pill);
  cursor: pointer;
}
.woocommerce-ordering select.orderby:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 2px;
}

/* --- 18.4 Grid de produtos ---------------------------------------------- */
/* Sobrescreve as classes columns-N do Woo com grid próprio: assim a densidade
   é decidida aqui e não depende do Customizer. */
.woocommerce ul.products,
ul.products {
  list-style: none;
  margin: 0; padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));   /* base: mobile 2-up */
  gap: clamp(16px, 2.4vw, 32px);
}
/* ⚠️ Estes seletores precisam REPETIR o `.woocommerce` da regra base.
   O <body> do Woo tem a classe `woocommerce`, então a regra base casa por
   `.woocommerce ul.products` (0,1,1); uma media query escrita só como
   `ul.products` (0,0,1) PERDE e o grid trava em 2 colunas no desktop, sem
   erro nenhum. Não "simplificar" removendo o .woocommerce daqui. */
@media (min-width: 761px) {
  .woocommerce ul.products, ul.products { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 1101px) {
  .woocommerce ul.products, ul.products { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

/* --- 18.5 Card de produto ------------------------------------------------
   Mesma linguagem dos cards da home: superfície branca, fio de 1px, raio 6px,
   foto dominante. Substitui a borda `double 4px` pêssego + raio 35px do BR
   (REFERENCE §A.5: "cantos muito arredondados + borda colorida = datado"). */
.woocommerce ul.products li.product,
ul.products li.product {
  position: relative;
  display: flex; flex-direction: column;
  margin: 0; padding: 0;
  background: var(--jn-white);
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-card);
  overflow: hidden;
  text-align: left;
  transition: transform var(--jn-d-base) var(--jn-ease),
              box-shadow var(--jn-d-base) var(--jn-ease);
}
ul.products li.product:hover {
  transform: translateY(-4px);
  box-shadow: 0 12px 28px var(--jn-shadow);
}
ul.products li.product:focus-within {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 3px;
}

/* O link do Woo embrulha foto + título + preço. Ele precisa esticar até o fim
   do card para que o preço possa ancorar embaixo: sem isto, título de 1 linha
   e de 2 linhas deixam os preços em alturas diferentes na mesma fileira. */
ul.products li.product a.woocommerce-loop-product__link {
  display: flex; flex-direction: column;
  flex: 1;
  text-decoration: none;
  color: inherit;
}
ul.products li.product img {
  width: 100%; height: auto;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  display: block;
  margin: 0;
  border: 0;                       /* mata a borda double pêssego do BR */
  border-radius: 0;
  transition: transform var(--jn-d-slow) var(--jn-ease);
}
ul.products li.product:hover img { transform: scale(1.04); }

ul.products li.product .woocommerce-loop-product__title {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h3);
  font-weight: 700;
  line-height: 1.25;
  color: var(--jn-heading);
  margin: 0;
  padding: var(--jn-s-5) var(--jn-s-5) 0;
}

/* --- 18.6 Preço (conformidade Omnibus) -----------------------------------
   ⚠️ PADRÃO = PREÇO ÚNICO, SEM RISCADO. Na UE o "de/por" exige que o valor
   riscado seja o menor preço realmente praticado nos 30 dias anteriores E que
   a redução não fique exposta indefinidamente (REFERENCE §A.4). Como desconto
   de estoque parado costuma ficar meses no ar, é a DURAÇÃO que pega.
   O estilo do riscado existe para promoção genuína e temporária, e por isso
   é discreto: nada de vermelho ruidoso. */
ul.products li.product .price {
  display: flex; align-items: baseline; flex-wrap: wrap;
  gap: var(--jn-s-2);
  font-family: var(--jn-font-head);
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--jn-terracotta);
  /* `auto` no topo alinha o preço na base do link, então títulos de 1 e de 2
     linhas não desencontram os preços na mesma fileira.
     ⚠️ Tem de ser o shorthand: um `margin-top: auto` seguido de `margin: 0`
     é zerado pelo shorthand posterior. */
  margin: auto 0 0;
  padding: var(--jn-s-2) var(--jn-s-5) var(--jn-s-5);
}
ul.products li.product .price del {
  color: var(--jn-ink-deep);
  /* ⚠️ 0.80 é o PISO, não gosto pessoal. Medido sobre branco:
     0,65 = 3,42:1 (reprovou no axe) · 0,75 = 4,34:1 (ainda reprova)
     0,80 = 4,92:1 ✅ · 1,00 = 8,33:1
     O preço antigo é texto pequeno (~15px), então precisa de 4,5:1.
     Não baixar para "deixar mais discreto": quem discrimina o preço antigo é
     o risco + o peso menor, não o desbotamento. */
  opacity: .8;
  font-weight: 400;
  font-size: .9em;
  text-decoration-thickness: 1px;
}
ul.products li.product .price ins {
  text-decoration: none;
  color: var(--jn-terracotta);
  font-weight: 700;
}

/* --- 18.7 Selo de oferta -------------------------------------------------
   Retangular e discreto, no lugar do círculo de 55px do BR. Carrega TEXTO
   ("Sale"), então não depende só de cor — exigência do brief §6 e da WCAG
   1.4.1. Trocar a string é via filtro woocommerce_sale_flash / WPML,
   nunca por `content:` no CSS. */
ul.products li.product .onsale {
  position: absolute;
  top: var(--jn-s-3); left: var(--jn-s-3);
  z-index: 2;
  margin: 0;
  padding: var(--jn-s-1) var(--jn-s-3);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-eyebrow);
  font-weight: 700;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--jn-on-action);
  background: var(--jn-terracotta);
  border-radius: var(--jn-r-media);
  min-height: 0; min-width: 0; line-height: 1.6;
}

/* --- 18.8 Add to cart ----------------------------------------------------
   Um único estilo de CTA, igual ao da home. Elimina o pêssego fraco e o verde
   default. ⚠️ O problema do botão pêssego do BR não era o texto (preto sobre
   pêssego passa de sobra); era o BOTÃO sumir no fundo creme: 1,47:1, reprova
   a WCAG 1.4.11 para componente de interface. Terracota sobre branco dá
   5,67:1 e resolve os dois lados. */
ul.products li.product a.add_to_cart_button,
ul.products li.product a.button {
  display: inline-flex;
  align-items: center; justify-content: center;
  margin: auto var(--jn-s-5) var(--jn-s-5);
  min-height: 48px;
  padding: 0 var(--jn-s-5);
  font-family: var(--jn-font-body);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--jn-on-action);
  background: var(--jn-terracotta);
  border: 2px solid var(--jn-terracotta);
  border-radius: var(--jn-r-pill);
  transition: background-color var(--jn-d-base) var(--jn-ease-soft),
              border-color var(--jn-d-base) var(--jn-ease-soft);
}
ul.products li.product a.add_to_cart_button:hover,
ul.products li.product a.button:hover {
  background: var(--jn-ink-deep);
  border-color: var(--jn-ink-deep);
}
ul.products li.product a.add_to_cart_button:focus-visible {
  outline: 3px solid var(--jn-ink-deep);
  outline-offset: 3px;
}
/* Estado "adicionado" do Woo (ajax) */
ul.products li.product a.added_to_cart {
  display: block;
  margin: 0 var(--jn-s-5) var(--jn-s-5);
  font-size: var(--jn-t-small);
  font-weight: 700;
  color: var(--jn-terracotta);
}

/* --- 18.9 Paginação ------------------------------------------------------
   Mata o #CC3366 (magenta) que estava na borda da paginação do BR. */
.woocommerce-pagination { margin-top: var(--jn-s-8); }
.woocommerce-pagination ul.page-numbers {
  list-style: none;
  display: flex; flex-wrap: wrap; justify-content: center;
  gap: var(--jn-s-2);
  margin: 0; padding: 0;
  border: 0;
}
.woocommerce-pagination ul.page-numbers li { border: 0; margin: 0; }
.woocommerce-pagination .page-numbers {
  display: inline-flex;
  align-items: center; justify-content: center;
  min-width: 48px; min-height: 48px;
  padding: 0 var(--jn-s-3);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 700;
  color: var(--jn-ink-deep);
  text-decoration: none;
  background: transparent;
  border: 1px solid var(--jn-line-strong);
  border-radius: var(--jn-r-pill);
  transition: background-color var(--jn-d-fast) var(--jn-ease-soft),
              color var(--jn-d-fast) var(--jn-ease-soft);
}
.woocommerce-pagination a.page-numbers:hover {
  background: var(--jn-shade);
  color: var(--jn-terracotta);
}
.woocommerce-pagination .page-numbers.current {
  background: var(--jn-terracotta);
  border-color: var(--jn-terracotta);
  color: var(--jn-on-action);
}
.woocommerce-pagination a.page-numbers:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 2px;
}

/* --- 18.10 Avisos do Woo ------------------------------------------------- */
.woocommerce-notices-wrapper:empty { display: none; }

/* --- 18.11 Reduced motion ------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  ul.products li.product:hover { transform: none !important; }
  ul.products li.product:hover img { transform: none !important; }
  .jn-shop-sidebar__menu .elementor-nav-menu .elementor-item:hover { padding-left: 0 !important; }
}

/* ==========================================================================
   18.12 ⚠️ PONTO TRAVADO #23 — VENCER O LOOP DE PRODUTO (Rev.03)
   --------------------------------------------------------------------------
   ⚠️ NÃO REMOVER, NÃO "SIMPLIFICAR", NÃO ENCURTAR AS CADEIAS. Cada seletor
   deste bloco existe porque foi MEDIDO perdendo no DOM ao vivo (LocalWP,
   11/08). Encurtar qualquer um devolve o card ao azul do kit.

   O QUE ACONTECEU. As regras de §18.5–§18.8 estão certas e nunca chegaram a
   valer: o grid de produto é desenhado por TRÊS folhas que carregam ANTES
   desta e pesam MAIS. Medido em / , /shop/ e /product/<slug>/ :

     woocommerce.css                     (Woo core, o loop)
     widget-woocommerce-products.min.css (o widget do Elementor)
     post-<id>.css                       (as opções DO widget, por página)

   Resultado na tela: título e preço em #6EC1E4, título a 16px com padding
   `.5em 0`, `li.product` em display:block (o preço parava de ancorar embaixo),
   16px de sobra debaixo da foto, botão em inline-block com o texto à esquerda,
   preço antigo a opacity .7 e selo de oferta fora do canto.

   ⚠️ O AZUL É O **PRIMARY** DO KIT, NÃO O ACCENT. O relatório dizia Accent;
   a regra medida é `color: var(--e-global-color-primary)` e o valor do kit 16
   para primary é #6EC1E4. Quem for procurar o swatch, procure Primary.

   ⚠️ OS TOKENS RESOLVEM AQUI DENTRO — NÃO ACRESCENTAR FALLBACK.
   A hipótese de que `var(--jn-heading)` morria no escopo do widget foi
   MEDIDA E DESCARTADA: os 8 tokens usados neste bloco resolvem dentro de
   `li.product` nas três páginas (estão em `:root`, nesta folha). A prova
   estava na própria tela — fundo branco, botão terracota, Fraunces e os
   paddings de 24px já vinham dos tokens. Escrever `var(--jn-heading, #6b4620)`
   aqui só criaria um segundo lugar para a cor da marca morar, e esconderia
   uma falha de token real no dia em que ela existir (ponto travado #11).

   --------------------------------------------------------------------------
   AS DUAS ÂNCORAS. O loop de produto aparece em DOIS renderizadores, e cada um
   tem um adversário diferente. Por isso toda regra abaixo é escrita DUAS vezes.

   W = `.elementor-products-grid.elementor-wc-products
        ul.products.elementor-grid li.product`                        (0,5,2)
       O widget. ⚠️ NÃO ancorar no nome do widget: são no mínimo DOIS
       (`elementor-widget-woocommerce-products` na home e no arquivo,
       `elementor-widget-woocommerce-product-related` no produto — medido).
       As três classes acima são as que o PRÓPRIO Elementor usa nas regras
       que precisamos vencer, então valem para todos eles.

   N = `html .woocommerce ul.products li.product`                     (0,3,4)
       O loop nativo, sem widget: cross-sells do carrinho, `[products]`,
       e a loja inteira se o Pro sair do ar.
       ⚠️ O `html` na frente não é enfeite: é o único elemento que serve como
       ANCESTRAL nos dois casos medidos. `.woocommerce` fica ora no <body>
       (loja, produto), ora numa <div> embrulhando o loop (home). `body
       .woocommerce` falharia no primeiro caso, `body.woocommerce` no segundo.

   ⚠️ VENCER POR PESO, NUNCA POR ORDEM. Nenhuma conta abaixo empata com o
   adversário — é a mesma disciplina do ponto travado #14. Esta folha pode ser
   instalada no style.css do tema filho (carrega DEPOIS de post-<id>.css) ou em
   Site Settings → Custom CSS (que vira o CSS do KIT e carrega ANTES). Um
   empate mudaria de resultado conforme o lugar da instalação.

   ⚠️ NÃO ANCORAR EM `.elementor-<postid>` NEM EM `.elementor-element-<id>`.
   São os seletores que o Elementor gera para as opções do widget, e mudam
   sozinhos: o id do template troca ao duplicá-lo, e o arquivo post-<id>.css é
   REGERADO a cada "Regenerate CSS". Ver o portão de operador na README-REV03.

   --------------------------------------------------------------------------
   SÓ AS PROPRIEDADES DISPUTADAS. Este bloco NÃO repete o desenho do card: os
   valores continuam morando em §18.5–§18.8, que seguem sendo a fonte. Aqui
   entram apenas as declarações que foram medidas PERDENDO, cada uma com o
   adversário e o peso dele ao lado. verify-tokens.mjs compara os dois lugares
   e reprova se um valor divergir do outro.
   ========================================================================== */

/* 18.12a Card — o widget devolve `display:block`, e o loop nativo ainda traz
   o `float:left; width:22.05%` do layout de 2011. Com o card em block o
   `margin-top:auto` do preço para de funcionar e o preço sobe.
   adversários: `.elementor-products-grid ul.products.elementor-grid li.product` (0,4,2)
                `.woocommerce ul.products li.product` (0,3,2)  [woocommerce-layout] */
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product,
html .woocommerce ul.products li.product {
  display: flex;
  flex-direction: column;
  float: none;
  width: auto;
  /* ⚠️ `justify-content` NÃO é declarado aqui, e isso é decisão, não esquecimento.
     O widget escreve `justify-content: var(--button-align-justify)` neste mesmo
     elemento — é o controle de alinhamento do botão, do painel do operador.
     MEDIDO no site real: com o link em `flex: 1` (§18.12b) não sobra folga para
     distribuir, então os quatro valores do controle (flex-start, center,
     space-between, flex-end) dão layout IDÊNTICO ao pixel. Quem ancora o botão
     embaixo são as margens `auto` de §18.8, que consomem a folga ANTES do
     justify-content. Declarar aqui só sobrescreveria um controle do painel sem
     mudar nada na tela. */
}

/* 18.12b O link que embrulha foto + título + preço. Sem a coluna flex aqui o
   `margin: auto 0 0` do preço não tem eixo para empurrar, e títulos de 1 e de
   2 linhas desencontram os preços na mesma fileira (§18.5).
   adversários: `.elementor-products-grid ul.products.elementor-grid li.product a.woocommerce-loop-product__link` (0,5,3)
                `.woocommerce ul.products li.product a.woocommerce-loop-product__link` (0,3,3) */
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product a.woocommerce-loop-product__link,
html .woocommerce ul.products li.product a.woocommerce-loop-product__link {
  display: flex;
  flex-direction: column;
  flex: 1;
}

/* 18.12c Foto — o Woo pendura 1em de margem embaixo da imagem, que no desenho
   é colada no título (a foto é sangrada, o respiro vive no texto).
   adversário: `.woocommerce ul.products li.product a img` (0,3,4) */
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product a img,
html .woocommerce ul.products li.product a img {
  margin: 0;
}

/* 18.12d Título. As TRÊS propriedades abaixo são as disputadas; fonte, peso e
   entrelinha de §18.5 já venciam (o adversário lá é só o reset, (0,0,6)).
   adversários: `.<widget>.elementor-wc-products ul.products li.product .woocommerce-loop-product__title` (0,5,2)  ← a cor
                `.woocommerce ul.products li.product .woocommerce-loop-product__title` (0,4,2)  ← corpo e padding */
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product .woocommerce-loop-product__title,
html .woocommerce ul.products li.product .woocommerce-loop-product__title {
  color: var(--jn-heading);
  font-size: var(--jn-t-h3);
  padding: var(--jn-s-5) var(--jn-s-5) 0;
}

/* 18.12e Preço. `margin` entra na disputa porque o Woo escreve
   `margin-bottom: .5em`, e o shorthand de §18.5 (`auto 0 0`) é quem ancora o
   preço na base do card — perder o shorthand desalinha a fileira inteira.
   adversários: `.<widget>.elementor-wc-products ul.products li.product .price` (0,5,2)
                `.woocommerce ul.products li.product .price` (0,4,2) */
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product .price,
html .woocommerce ul.products li.product .price {
  color: var(--jn-terracotta);
  display: flex;
  font-size: 1.0625rem;
  font-weight: 600;
  margin: auto 0 0;
}

/* 18.12f Preço antigo (Omnibus). ⚠️ A `opacity` NÃO é gosto: o Woo impõe .7,
   e o comentário travado de §18.6 registra a medição — .7 fica ABAIXO do piso
   de 4,5:1 para texto pequeno sobre branco. Estava reprovando no ar.
   ⚠️ O `.amount` precisa de linha PRÓPRIA: o Elementor escreve a cor também no
   <span class="amount"> de dentro, num seletor mais pesado que o do <del>.
   adversários: `.<widget>… .price del` (0,5,3) · `.<widget>… .price del .amount` (0,6,3)
                `.woocommerce ul.products li.product .price del` (0,4,3) */
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product .price del,
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product .price del .amount,
html .woocommerce ul.products li.product .price del,
html .woocommerce ul.products li.product .price del .amount {
  color: var(--jn-ink-deep);
}
/* ⚠️ A `opacity` fica NUMA REGRA SÓ DELA, e SÓ no <del>. NÃO juntar com o
   seletor acima. `opacity` não é herdada: ela é aplicada a CADA elemento e as
   camadas se MULTIPLICAM. Com a mesma declaração no <del> e no `.amount` de
   dentro, .8 × .8 = .64 — medido, o axe reprovou o preço antigo por contraste
   (o piso de 4,5:1 está calculado para .8 exato, ver §18.6). */
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product .price del,
html .woocommerce ul.products li.product .price del {
  opacity: .8;
}
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product .price ins,
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product .price ins .amount,
html .woocommerce ul.products li.product .price ins,
html .woocommerce ul.products li.product .price ins .amount {
  color: var(--jn-terracotta);
}

/* 18.12g Selo de oferta — POSIÇÃO apenas.
   ⚠️ `display` FICA DE FORA DE PROPÓSITO. Esconder ou mostrar o selo é um
   CONTROLE DO OPERADOR ("Show Sale Flash" no widget): o padrão do Elementor é
   `display:none`, e quando o operador liga, o Elementor escreve `display:block`
   num seletor com o id da página, mais pesado que qualquer coisa que se possa
   escrever aqui sem depender desse id. Medido: LIGADO na home, DESLIGADO na
   loja — inconsistência de configuração, não de CSS (ver README-REV03 §portões).
   Declarar `display` aqui só brigaria com o painel e perderia na home.
   adversário: `.woocommerce ul.products li.product .onsale` (0,4,2) */
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product .onsale,
html .woocommerce ul.products li.product .onsale {
  top: var(--jn-s-3);
  right: auto;
  left: var(--jn-s-3);
  margin: 0;
}

/* 18.12h Add to cart — a causa do "texto torto" do relatório. O Woo devolve
   `display:inline-block`, e aí `justify-content:center` de §18.8 vira inerte:
   o <a> fica mais largo que o texto e o texto encosta à esquerda.
   `text-align:center` é cinto e suspensório — com inline-flex já centraliza,
   mas rótulo que quebra em duas linhas ("Add to cart" traduzido) só fica
   centrado com ele.
   adversário: `.woocommerce ul.products li.product .button` (0,4,2) */
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product a.button,
.elementor-products-grid.elementor-wc-products ul.products.elementor-grid li.product a.add_to_cart_button,
html .woocommerce ul.products li.product a.button,
html .woocommerce ul.products li.product a.add_to_cart_button {
  display: inline-flex;
  margin: auto var(--jn-s-5) var(--jn-s-5);
  text-align: center;
}

/* ==========================================================================
   19. WOOCOMMERCE — PRODUTO INDIVIDUAL (single-product)
   --------------------------------------------------------------------------
   Skin sobre a marcação nativa do Woo. NÃO reconstrói a página: o form de
   compra, a galeria, as abas e os relacionados continuam sendo os do Woo, e
   por isso carrinho, variações, estoque e WPML seguem funcionando.

   ⚠️ CONTRATO DE RENDERIZAÇÃO — três camadas mandam aqui, nesta ordem:
     1. o PHP do Woo, que imprime div.product > (galeria | summary | tabs);
     2. o single-product.js do Woo, que roda o FLEXSLIDER na galeria e
        escreve estilos INLINE (width, float, transform) nos slides;
     3. só então esta folha.
   Estilo inline ganha de qualquer seletor. Por isso a galeria abaixo é
   desenhada para CONVIVER com o flexslider, nunca para brigar com ele: o
   enquadramento vem de um contêiner de proporção, não de largura de slide.

   O que NÃO é nativo e chega por snippet/template (ver README-PRODUCT.md):
   fileira de confiança, estimador de frete, bloco sob encomenda e barra fixa
   de compra. As classes jn-* abaixo esperam essa marcação.
   ========================================================================== */

/* --- 19.0 Casca ---------------------------------------------------------- */
/* ⚠️ Rev.02 — 7 classes de container mortas neste template (`jn-single`,
   `jn-single__layout`, `jn-single__media`, `jn-single__buy`, `jn-section`,
   `jn-shell`, `jn-bg-creme`). A seção é `#product`; as colunas se
   identificam pelos widgets de galeria e de add-to-cart, que sobrevivem. */
#product { padding-block: var(--jn-section-y-sm) var(--jn-section-y); }

/* --- 19.1 Breadcrumb -----------------------------------------------------
   REFERENCE §A.1: o produto tem de nascer perto do topo. O breadcrumb é a
   única coisa acima dele, e é discreto de propósito. */
.woocommerce-breadcrumb {
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  color: var(--jn-text);
  margin: 0 0 var(--jn-s-5);
  display: flex; flex-wrap: wrap; align-items: center;
  gap: var(--jn-s-2);
}
.woocommerce-breadcrumb a {
  color: var(--jn-text);
  text-decoration: underline;
  text-underline-offset: 3px;
  text-decoration-color: var(--jn-line-strong);
}
.woocommerce-breadcrumb a:hover { color: var(--jn-terracotta); text-decoration-color: currentColor; }
.jn-crumb-sep { color: var(--jn-line-strong); }
.woocommerce-breadcrumb [aria-current="page"] { font-weight: 600; color: var(--jn-heading); }

/* --- 19.2 Layout: galeria + buy box --------------------------------------
   MOBILE-FIRST: uma coluna. O BR usava duas colunas de 41,7% / 58,3% num
   contêiner de 898px, ou seja, a FOTO era a coluna estreita. Numa loja que
   vende desenho, isso está invertido: aqui a galeria é a coluna larga. */
.woocommerce div.product,
div.product {
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--jn-s-7);
  margin: 0;
}
.woocommerce div.product > .woocommerce-tabs,
.woocommerce div.product > .related.products { grid-column: 1 / -1; }

/* ⚠️ PONTE PARA O ELEMENTOR PRO. Sem Pro, quem faz as colunas é o próprio
   `div.product`, cujos filhos diretos são a galeria e o .summary (regra
   acima). COM Pro, o template do Theme Builder põe contêineres do Elementor
   (.e-con) dentro do div.product, e aí a galeria e o resumo deixam de ser
   filhos diretos: a grade acima passaria a distribuir os contêineres do
   Elementor em duas colunas e o layout viraria outra coisa.
   Então: se houver .e-con como filho direto, o div.product volta a ser um
   bloco simples e quem organiza as colunas é o .jn-single__layout do template.
   Uma folha, dois mundos, sem depender de qual deles está no ar. */
.woocommerce div.product:has(> .e-con),
div.product:has(> .e-con) { display: block; }

.e-con:has(> .e-con > .jn-single__gallery) { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--jn-s-7); }
.e-con:has(> .jn-single__gallery),
.e-con:has(> .jn-single__cart) { min-width: 0; }
@media (min-width: 861px) {
  .e-con:has(> .e-con > .jn-single__gallery) {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: var(--jn-s-8);
    align-items: start;
  }
}

@media (min-width: 861px) {
  .woocommerce div.product,
  div.product {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 1fr);
    gap: var(--jn-s-8);
    align-items: start;
  }
}

/* ⚠️ NÃO PÔR `position: sticky` NO BUY BOX. Foi tentado e reprovou na medição.
   O brief pede uma coluna de compra DENSA (título, preço, specs, estoque,
   quantidade, botão, fileira de confiança, estimador de frete, meta): isso dá
   ~900px, mais alto que a viewport. Um elemento sticky mais alto que a tela
   nunca termina de rolar, então o rodapé dele (frete e meta) fica inalcançável
   E o bloco desce por cima da faixa de abas. Visto no preview.
   Quem resolve "CTA sempre à mão" aqui é a barra fixa do §19.14, no mobile,
   que é onde o problema realmente existe. */

/* --- 19.3 Galeria --------------------------------------------------------
   ⚠️ A moldura é o CONTÊINER, não o slide. O flexslider escreve width e
   float inline em cada .woocommerce-product-gallery__image; qualquer tentativa
   de dimensionar o slide daqui perde para o inline e quebra o arrasto.
   Enquadro pelo elemento de fora (.flex-viewport, que o flexslider cria) e
   deixo o miolo com ele. */
.woocommerce-product-gallery { position: relative; margin: 0; }

.woocommerce-product-gallery .flex-viewport,
.woocommerce-product-gallery__wrapper {
  border-radius: var(--jn-r-card);
  overflow: hidden;
}
.woocommerce-product-gallery .flex-viewport {
  border: 1px solid var(--jn-line);
  background: var(--jn-white);
}

/* `contain` e não `cover`: aqui a imagem É o produto. Recortar um desenho de
   decoupage para encher a caixa corta justamente o que a cliente compra. O
   fundo branco atrás faz a sobra desaparecer.

   ⚠️ `height: auto` NÃO é decorativo. As <img> do Woo vêm com os atributos
   width/height do arquivo (ex.: 800x1000). Sem `height:auto`, a altura é
   USADA a partir do atributo e o `aspect-ratio` abaixo é simplesmente
   ignorado, porque aspect-ratio só entra quando uma das dimensões é auto.
   Medido: a caixa vinha 606x1000 em vez de 606x757. Não remover.

   Proporção 1/1 escolhida por MEDIÇÃO das fotos reais do catálogo, não por
   gosto: 600x600, 1080x1080, 1024x1024 e 600x834. Três das quatro são
   exatamente quadradas; num quadro 4/5 elas ficariam com tarja em cima e
   embaixo em quase toda a loja. */
.woocommerce-product-gallery__image img,
.woocommerce-product-gallery__wrapper img {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 1 / 1;
  object-fit: contain;
  background: var(--jn-white);
  margin: 0;
  border: 0;                    /* mata a borda 1px #F1BDA7 + raio 15px do BR */
  border-radius: 0;
}
.woocommerce-product-gallery__image a { display: block; }

/* ⚠️ FALLBACK sem flexslider. Se o JS do Woo não rodar (ou o tema não declarar
   suporte a wc-product-gallery-slider), NÃO existe .flex-viewport e os slides
   não são flutuados: as 4 imagens empilham uma sob a outra e a página fica com
   cara de quebrada. O flexslider põe a classe `flexslider` no contêiner, então
   dá para detectar a ausência dele e mostrar só a foto principal.
   Degradação prevista, não acidente. */
.woocommerce-product-gallery:not(.flexslider) .woocommerce-product-gallery__image:not(:first-child) {
  display: none;
}

/* Lupa. O Woo injeta um <a> cujo conteúdo é um emoji de lupa; escondo o
   glifo e desenho o ícone em CSS, para não depender de fonte de emoji nem
   deixar "🔍" virar o nome acessível (o snippet põe aria-label de verdade).
   Mesma técnica do ponto travado #13: forma em borda, sem texto injetado. */
.woocommerce-product-gallery__trigger {
  position: absolute;
  z-index: 3;
  top: var(--jn-s-4); right: var(--jn-s-4);
  width: 44px; height: 44px;
  border-radius: var(--jn-r-pill);
  background: var(--jn-white);
  border: 1px solid var(--jn-line-strong);
  box-shadow: 0 2px 10px var(--jn-shadow);
  font-size: 0;                                   /* esconde o emoji */
  color: transparent;
  text-decoration: none;
  transition: background-color var(--jn-d-base) var(--jn-ease-soft),
              transform var(--jn-d-fast) var(--jn-ease-soft);
}
.woocommerce-product-gallery__trigger::before {   /* lente */
  content: "";
  position: absolute;
  top: 13px; left: 13px;
  width: 14px; height: 14px;
  border: 1.6px solid var(--jn-terracotta);
  border-radius: 50%;
}
.woocommerce-product-gallery__trigger::after {    /* cabo */
  content: "";
  position: absolute;
  top: 26px; left: 25px;
  width: 7px; height: 1.6px;
  background: var(--jn-terracotta);
  border-radius: 2px;
  transform: rotate(45deg);
  transform-origin: left center;
}
.woocommerce-product-gallery__trigger:hover { background: var(--jn-creme); transform: scale(1.06); }
.woocommerce-product-gallery__trigger:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 3px;
}

/* Miniaturas. O flexslider entrega <ol><li><img>, sem botão e sem foco:
   o snippet do README põe role/tabindex/aria-label em cada <li>, e é por isso
   que o estado de foco abaixo mira o <li> e não o <img>. */
.woocommerce-product-gallery .flex-control-thumbs {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: var(--jn-s-3);
  list-style: none;
  margin: var(--jn-s-3) 0 0;
  padding: 0;
}
.woocommerce-product-gallery .flex-control-thumbs li {
  margin: 0;
  border-radius: var(--jn-r-media);
  overflow: hidden;
}
/* ⚠️ A miniatura é um <button> DE VERDADE, criado pelo snippet ao redor da
   <img> que o flexslider solta dentro do <li>. Não trocar por role="button"
   no <li>: o axe reprova duas vezes (aria-allowed-role e list). O botão aqui
   é só uma casca sem estilo próprio; quem desenha é a <img> de dentro. */
.jn-thumb-btn {
  display: block;
  width: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
  border-radius: var(--jn-r-media);
}
.woocommerce-product-gallery .flex-control-thumbs img {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  object-fit: cover;
  background: var(--jn-white);
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-media);
  opacity: 0.72;
  transition: opacity var(--jn-d-base) var(--jn-ease-soft),
              border-color var(--jn-d-base) var(--jn-ease-soft);
}
.woocommerce-product-gallery .flex-control-thumbs img:hover { opacity: 1; }
/* Ativa: opacidade cheia MAIS borda terracota de 2px. Nunca só opacidade,
   que sozinha não passa em 1.4.11 nem é perceptível em monitor ruim. */
.woocommerce-product-gallery .flex-control-thumbs img.flex-active {
  opacity: 1;
  border-width: 2px;
  border-color: var(--jn-terracotta);
}
.woocommerce-product-gallery .flex-control-thumbs .jn-thumb-btn:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 3px;
}

/* --- 19.4 Título e preço ------------------------------------------------- */
.woocommerce div.product .product_title {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h1);
  font-weight: 400;
  line-height: var(--jn-lh-head);
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-3);
  text-wrap: balance;
}

/* ⚠️ Preço: mesma disciplina Omnibus do §18.6. Padrão é PREÇO ÚNICO, sem
   riscado. O BR pintava o preço de #000000 e o promocional de #61CE70 (o
   verde default do Elementor); o riscado dos relacionados era #FE0000. Nada
   disso volta: preço é terracota, riscado é o marrom do corpo. */
/* ⚠️ SÓ `p.price`, NUNCA `span.price`. O preço do produto é uma <p class="price">;
   o preço dos CARDS (relacionados, upsell) é uma <span class="price">, e os
   cards vivem DENTRO deste mesmo div.product. Incluir o span aqui faz esta
   regra (0,3,2) atropelar o card do §18.6 (0,2,2): o preço dos relacionados
   saía no corpo gigante do produto e perdia o `margin: auto 0 0` que o encosta
   na base do card, desalinhando os preços da fileira. Medido e corrigido.
   Regressão silenciosa: nada quebra, só fica errado. */
.woocommerce div.product p.price {
  font-family: var(--jn-font-head);
  font-size: clamp(1.5rem, 1.2rem + 1.1vw, 2rem);
  font-weight: 600;
  line-height: 1.1;
  color: var(--jn-terracotta);
  margin: 0 0 var(--jn-s-5);
}
.woocommerce div.product p.price del {
  color: var(--jn-ink-deep);
  opacity: .8;                 /* 4,92:1. Em .65 dava 3,42:1 e reprovava */
  font-size: 0.7em;
  font-weight: 400;
  margin-right: var(--jn-s-2);
  text-decoration-thickness: 1px;
}
.woocommerce div.product p.price ins {
  text-decoration: none;
  background: none;
  color: var(--jn-terracotta);
}

/* --- 19.5 Especificações curtas ------------------------------------------
   Par rótulo/valor. Texto de verdade nos dois lados: o WPML traduz e o
   Google lê. Nada disto pode virar imagem.

   ⚠️ PONTO TRAVADO #18 — DE ONDE VÊM ESTES DADOS. Não são atributos do
   WooCommerce. A loja tem 965 produtos com a ficha (medida, marca, "preço
   referente a 1 folha", material) escrita SOLTA dentro da descrição, e passar
   isso para atributos à mão não é viável. Quem monta esta lista é o
   snippets-specs.php, que lê a descrição na hora de renderizar usando os
   padrões de specs-rules.json.
   Consequências que não devem ser esquecidas:
     · o bloco pode não existir (produto sem ficha reconhecível). O CSS não
       pode assumir que ele está lá;
     · a quantidade de linhas varia de 1 a 5, então nada de altura fixa;
     · o valor é texto vindo do catálogo, de comprimento imprevisível
       (por isso a coluna de valor é minmax(0,1fr) e não largura fixa). */
.woocommerce-product-details__short-description {
  font-size: var(--jn-t-body);
  line-height: var(--jn-lh-body);
  color: var(--jn-text);
  margin: 0 0 var(--jn-s-5);
  max-width: 46ch;
}
.jn-specs {
  list-style: none;
  margin: 0 0 var(--jn-s-5);
  padding: var(--jn-s-4) 0;
  border-top: 1px solid var(--jn-line);
  border-bottom: 1px solid var(--jn-line);
  display: grid;
  gap: var(--jn-s-2);
}
.jn-specs__item {
  display: grid;
  grid-template-columns: minmax(0, 8.5rem) minmax(0, 1fr);
  gap: var(--jn-s-3);
  font-size: var(--jn-t-small);
  line-height: 1.5;
}
.jn-specs__k {
  color: var(--jn-text);
  opacity: .85;
  letter-spacing: 0.02em;
}
.jn-specs__v { color: var(--jn-heading); font-weight: 600; }

/* --- 19.6 Estoque --------------------------------------------------------
   ⚠️ WCAG 1.4.1: o estado NÃO pode depender da cor. O ponto é decorativo; a
   frase ("Only 5 left", "Out of stock") é quem informa. Se um dia alguém
   trocar o texto por um ícone, este comentário é o aviso. */
.woocommerce div.product p.stock {
  display: inline-flex;
  align-items: center;
  gap: var(--jn-s-2);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 600;
  margin: 0 0 var(--jn-s-4);
  padding: 0;
}
.woocommerce div.product p.stock::before {
  content: "";
  width: 8px; height: 8px;
  border-radius: 50%;
  background: currentColor;
  flex: none;
}
.woocommerce div.product p.stock.in-stock { color: var(--jn-ink-deep); }
.woocommerce div.product p.stock.out-of-stock { color: var(--jn-terracotta); }

/* --- 19.7 Form de compra -------------------------------------------------
   ⚠️ ESPECIFICIDADE OBRIGATÓRIA. O botão nasce com `.button.alt`, e o Woo
   estiliza `.woocommerce button.button.alt` (0,2,1). Um seletor mais curto
   PERDE e o botão volta a ser o roxo/verde padrão. A cadeia abaixo é (0,4,2).
   É o mesmo verde #61CE70 que o ponto travado #10 já neutraliza no resto do
   site: aqui ele chega por outro caminho e precisa da mesma trava. */
.woocommerce div.product form.cart {
  display: flex;
  flex-wrap: wrap;
  align-items: stretch;
  gap: var(--jn-s-3);
  margin: 0 0 var(--jn-s-5);
}
.woocommerce div.product form.cart .quantity { margin: 0; flex: none; }
.woocommerce div.product form.cart .quantity input.qty {
  width: 96px;
  min-height: 52px;
  padding: 0 var(--jn-s-3);
  text-align: center;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  font-weight: 600;
  color: var(--jn-ink-deep);
  background: var(--jn-white);
  /* Ponto travado #12: borda VISÍVEL. O checkout BR tinha pílula pêssego sem
     borda, fundindo com o fundo. Não repetir aqui. */
  border: 2px solid var(--jn-ink-deep);
  border-radius: var(--jn-r-pill);
}
.woocommerce div.product form.cart .quantity input.qty:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 2px;
}
.woocommerce div.product form.cart button.single_add_to_cart_button,
.woocommerce div.product form.cart .single_add_to_cart_button.button.alt {
  flex: 1 1 12rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 0 var(--jn-s-6);
  font-family: var(--jn-font-body);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--jn-on-action);            /* branco sobre terracota = 6,45:1 */
  background-color: var(--jn-terracotta);
  border: 2px solid var(--jn-terracotta);
  border-radius: var(--jn-r-pill);
  cursor: pointer;
  transition: background-color var(--jn-d-base) var(--jn-ease-soft),
              border-color var(--jn-d-base) var(--jn-ease-soft),
              transform var(--jn-d-fast) var(--jn-ease-soft),
              box-shadow var(--jn-d-base) var(--jn-ease-soft);
}
.woocommerce div.product form.cart button.single_add_to_cart_button:hover {
  background-color: var(--jn-ink-deep);
  border-color: var(--jn-ink-deep);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px var(--jn-shadow-lift);
}
.woocommerce div.product form.cart button.single_add_to_cart_button:active { transform: translateY(0); box-shadow: none; }
.woocommerce div.product form.cart button.single_add_to_cart_button:focus-visible {
  outline: 3px solid var(--jn-ink-deep);
  outline-offset: 3px;
}

/* --- 19.8 Fileira de confiança -------------------------------------------
   Ícones a 20px: medida óptica já sancionada pelo ponto travado #7 (16/20/32).
   O ícone é decorativo (aria-hidden no SVG); quem informa é o texto ao lado,
   que continua sendo texto e por isso o WPML traduz. */
.jn-trust {
  list-style: none;
  margin: 0 0 var(--jn-s-5);
  padding: var(--jn-s-4) var(--jn-s-5);
  background: var(--jn-shade);
  border-radius: var(--jn-r-card);
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: var(--jn-s-3);
}
@media (min-width: 480px) {
  .jn-trust { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: var(--jn-s-3) var(--jn-s-4); }
}
.jn-trust__item {
  display: flex;
  align-items: center;
  gap: var(--jn-s-3);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  line-height: 1.35;
  color: var(--jn-ink-deep);
}
.jn-trust__icon { flex: none; width: 20px; height: 20px; color: var(--jn-terracotta); }
.jn-trust__icon svg { width: 100%; height: 100%; display: block; }

/* ⚠️ A linha "14-day returns" vale para TODO o catálogo enquanto a loja
   vender só produto próprio, que é a decisão de lançamento na Europa.
   Existia aqui uma regra escondendo essa linha nos itens sob encomenda,
   porque o direito de arrependimento na UE tem exceção expressa para bens
   feitos segundo especificação do consumidor (Diretiva 2011/83/UE, art. 16-c).
   Saiu junto com o fluxo sob encomenda. Se a linha personalizada voltar, ESTA
   é a regra a restaurar primeiro: prometer devolução onde a lei não obriga é
   promessa que a loja passa a dever. */

/* --- 19.9 Estimador de frete (skin pronta para o Sendcloud) --------------
   REFERENCE §C: hoje é "uma caixa cinza sem identidade". Aqui vira um bloco
   da marca. A integração real substitui só o miolo; a casca fica. */
.jn-ship {
  margin: 0 0 var(--jn-s-5);
  padding: var(--jn-s-5);
  background: var(--jn-white);
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-card);
}
.jn-ship__title {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h3);
  font-weight: 600;
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-5);
}
.jn-ship__row { display: grid; grid-template-columns: minmax(0, 1fr); gap: var(--jn-s-4); }
@media (min-width: 560px) {
  .jn-ship__row { grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); }
  .jn-ship__submit { grid-column: 1 / -1; }
}
.jn-field { display: grid; gap: var(--jn-s-2); min-width: 0; }
.jn-field__label {
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-eyebrow);
  font-weight: 700;
  letter-spacing: var(--jn-ls-eyebrow);
  text-transform: uppercase;
  color: var(--jn-ink-deep);
}
/* Ponto travado #12 de novo: borda de 2px, nunca campo sem contorno. */
.jn-field__control {
  width: 100%;
  min-height: 48px;
  padding: 0 var(--jn-s-4);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  color: var(--jn-ink-deep);
  background: var(--jn-white);
  border: 2px solid var(--jn-ink-deep);
  border-radius: var(--jn-r-pill);
}
/* .62 dava 3,19:1 sobre branco e reprovava AA. Ver a conta completa no §21.2:
   o piso é .765, e todos os placeholders do sistema usam .8. */
.jn-field__control::placeholder { color: var(--jn-ink-deep); opacity: 0.8; }
.jn-field__control:focus-visible { outline: 3px solid var(--jn-terracotta); outline-offset: 2px; }
/* Chevron em bordas CSS, sem texto injetado (ponto travado #13). */
select.jn-field__control {
  appearance: none;
  padding-right: var(--jn-s-8);
  background-image:
    linear-gradient(45deg, transparent 50%, var(--jn-ink-deep) 50%),
    linear-gradient(135deg, var(--jn-ink-deep) 50%, transparent 50%);
  background-position: calc(100% - 22px) calc(50% + 1px), calc(100% - 16px) calc(50% + 1px);
  background-size: 6px 6px, 6px 6px;
  background-repeat: no-repeat;
}
/* Secundário de propósito: o CTA da página é UM só, e é o Add to cart. */
.jn-ship__submit {
  min-height: 48px;
  padding: 0 var(--jn-s-5);
  font-family: var(--jn-font-body);
  font-size: 0.875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--jn-terracotta);              /* 5,67:1 sobre branco */
  background: transparent;
  border: 2px solid var(--jn-terracotta);
  border-radius: var(--jn-r-pill);
  cursor: pointer;
  transition: background-color var(--jn-d-base) var(--jn-ease-soft),
              color var(--jn-d-base) var(--jn-ease-soft);
}
.jn-ship__submit:hover { background: var(--jn-terracotta); color: var(--jn-on-action); }
.jn-ship__submit:focus-visible { outline: 3px solid var(--jn-terracotta); outline-offset: 3px; }
.jn-ship__note {
  margin: var(--jn-s-3) 0 0;
  font-size: var(--jn-t-small);
  color: var(--jn-text);
  opacity: .85;
}

/* --- 19.10 (aposentado) --------------------------------------------------
   Aqui vivia o fluxo SOB ENCOMENDA (bloco "Made to order", troca por classe
   de categoria, supressão do "14-day returns"). REMOVIDO a pedido do
   operador: no lançamento europeu a loja trabalha só com produto próprio, sem
   encomenda personalizada, por causa da dificuldade de fabricação em outro
   continente.
   O ponto travado #16 (portão de estado com !important) fica APOSENTADO, não
   reaproveitado: o número não volta a ser usado, para que ninguém confunda o
   histórico. Se a linha personalizada voltar um dia, a peça inteira precisa
   ser reconstruída E revalidada, principalmente a exceção legal de devolução
   (Diretiva 2011/83/UE, art. 16-c), que era o detalhe fácil de esquecer.
   -------------------------------------------------------------------------- */

/* --- 19.11 Meta do produto ----------------------------------------------- */
.woocommerce div.product .product_meta {
  display: grid;
  gap: var(--jn-s-2);
  padding-top: var(--jn-s-4);
  border-top: 1px solid var(--jn-line);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  color: var(--jn-text);
}
.woocommerce div.product .product_meta a {
  color: var(--jn-terracotta);
  text-decoration: underline;
  text-underline-offset: 3px;
}

/* --- 19.12 Abas ----------------------------------------------------------
   ⚠️ O JS do Woo esconde os painéis com STYLE INLINE (display:none) e marca a
   aba ativa com a classe .active no <li>. Não tentar controlar visibilidade
   de painel daqui: o inline ganha, e o resultado seria uma aba que "não
   fecha". Esta folha só desenha. */
.woocommerce-tabs { margin-top: var(--jn-s-8); }
.woocommerce-tabs ul.tabs {
  display: flex;
  flex-wrap: wrap;
  gap: var(--jn-s-5);
  list-style: none;
  margin: 0 0 var(--jn-s-5);
  padding: 0 0 var(--jn-s-1);
  /* ⚠️ Rev.04 — `overflow: visible` é DECLARAÇÃO, não default herdado. O Woo
     escreve `overflow: hidden` nesta mesma <ul>, e com ele o `margin-bottom`
     negativo do <a> abaixo é CORTADO: o sublinhado da aba ativa existe no
     computed style e nunca aparece na tela. Ver §19.16. */
  overflow: visible;
  border-bottom: 1px solid var(--jn-line);
}
.woocommerce-tabs ul.tabs li { margin: 0; padding: 0; background: none; border: 0; border-radius: 0; }
.woocommerce-tabs ul.tabs li a {
  display: inline-block;
  padding: var(--jn-s-2) 0 var(--jn-s-3);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 700;
  /* ⚠️ Rev.04 — declarada porque o widget do Elementor impõe `line-height: 1`
     nesta mesma âncora. Sem esta linha o preview mede 23px de caixa e o site
     mede 14px: o preview mentiria em 9px. Ver §19.16. */
  line-height: var(--jn-lh-head);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--jn-text);
  border-bottom: 2px solid transparent;
  margin-bottom: -5px;
  transition: color var(--jn-d-base) var(--jn-ease-soft),
              border-color var(--jn-d-base) var(--jn-ease-soft);
}
.woocommerce-tabs ul.tabs li a:hover { color: var(--jn-terracotta); }
/* Ativa por PESO + sublinhado, nunca só por cor. */
.woocommerce-tabs ul.tabs li.active a,
.woocommerce-tabs ul.tabs li a[aria-selected="true"] {
  color: var(--jn-heading);
  border-bottom-color: var(--jn-terracotta);
}
.woocommerce-tabs ul.tabs li a:focus-visible { outline: 3px solid var(--jn-terracotta); outline-offset: 3px; }

.woocommerce-Tabs-panel { color: var(--jn-text); line-height: var(--jn-lh-body); }
.woocommerce-Tabs-panel > h2 {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h3);
  font-weight: 600;
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-4);
}
.woocommerce-Tabs-panel p { max-width: var(--jn-measure); margin: 0 0 var(--jn-s-4); }

/* Tabela de atributos. Estruturado ganha de texto solto: é filtrável,
   traduzível pelo WPML campo a campo, e o Google entende. */
.woocommerce-product-attributes {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--jn-t-small);
}
.woocommerce-product-attributes th,
.woocommerce-product-attributes td {
  text-align: left;
  vertical-align: top;
  padding: var(--jn-s-3) var(--jn-s-4);
  border-bottom: 1px solid var(--jn-line);
}
.woocommerce-product-attributes th { width: 40%; font-weight: 600; color: var(--jn-heading); }
.woocommerce-product-attributes td { color: var(--jn-text); }
.woocommerce-product-attributes td p { margin: 0; }

/* --- 19.13 Relacionados --------------------------------------------------
   NÃO redefine o card: reusa inteiro o §18.5 (o Woo emite o MESMO
   ul.products > li.product da categoria). Aqui só o cabeçalho e o respiro. */
.related.products { margin-top: var(--jn-s-9); }
.related.products > h2 {
  font-family: var(--jn-font-display);
  font-size: var(--jn-t-h1);
  font-weight: 700;
  line-height: var(--jn-lh-tight);
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-6);
}

/* --- 19.14 Barra fixa de compra (mobile) ---------------------------------
   ⚠️ `position: sticky` NÃO serve aqui: o bloco pai é a coluna do resumo, e
   assim que ela sai da tela o elemento descola. Barra fixa mesmo, com um JS
   mínimo que a mostra só quando o botão real saiu de vista.

   ⚠️ O botão da barra é `tabindex="-1"` dentro de um contêiner
   `aria-hidden="true"` DE PROPÓSITO: não é um segundo controle, é um atalho
   de ponteiro que dispara o form real. Duplicar um botão focável com o mesmo
   nome acessível só poluiria a ordem de leitura. Não "corrigir" isso. */
.jn-buybar {
  position: fixed;
  z-index: 40;
  left: 0; right: 0; bottom: 0;
  padding: var(--jn-s-3) var(--jn-gutter);
  padding-bottom: calc(var(--jn-s-3) + env(safe-area-inset-bottom, 0px));
  background: var(--jn-white);
  border-top: 1px solid var(--jn-line);
  box-shadow: 0 -6px 20px var(--jn-shadow);
  transform: translateY(110%);
  transition: transform var(--jn-d-base) var(--jn-ease);
}
.jn-buybar.is-visible { transform: translateY(0); }
.jn-buybar__inner {
  display: flex; align-items: center; gap: var(--jn-s-4);
  max-width: var(--jn-container); margin-inline: auto;
}
.jn-buybar__price {
  font-family: var(--jn-font-head);
  font-size: 1.25rem; font-weight: 600;
  color: var(--jn-terracotta);
  white-space: nowrap;
}
.jn-buybar__btn {
  flex: 1;
  min-height: 48px;
  font-family: var(--jn-font-body);
  font-size: 0.9375rem; font-weight: 700;
  letter-spacing: 0.04em; text-transform: uppercase;
  color: var(--jn-on-action);
  background: var(--jn-terracotta);
  border: 2px solid var(--jn-terracotta);
  border-radius: var(--jn-r-pill);
  cursor: pointer;
}
/* Só no celular. No desktop a coluna de compra já fica visível o bastante. */
@media (min-width: 861px) { .jn-buybar { display: none; } }

/* --- 19.15 Reduced motion ------------------------------------------------ */
@media (prefers-reduced-motion: reduce) {
  .woocommerce-product-gallery__wrapper { transition: none !important; }
  .woocommerce-product-gallery__trigger:hover { transform: none !important; }
  .woocommerce div.product form.cart button.single_add_to_cart_button:hover { transform: none !important; }
  .jn-buybar { transition: none !important; }
}

/* --- 19.16 Abas: vencer o CSS de terceiros (Rev.04) -----------------------
   ⚠️ PONTO TRAVADO #24. NÃO encurtar nenhuma cadeia de seletor deste bloco.

   É §18.12 outra vez, no mesmo formato e pelo mesmo motivo. As regras do
   §19.12 estavam escritas certas desde a entrega da página de produto e
   NUNCA chegaram a valer: as abas nascem do WooCommerce, e três folhas de
   TERCEIROS carregam antes do `custom.css` e pesam mais.

     woocommerce.css .......................... cor, chip do <li>, pseudos
     woocommerce-layout.css ................... clearfix da <ul>
     widget-woocommerce-product-data-tabs.min.css   padding e line-height do <a>

   MEDIDO em /product/produto-1/ (DOM real, não preview):

   | alvo        | quem vence hoje                                    | peso   | nosso  |
   |-------------|----------------------------------------------------|--------|--------|
   | a           | .woocommerce div.product .woocommerce-tabs ul.tabs li a | (0,4,4) | (0,2,3) |
   | a (padding) | .woocommerce div.product.elementor ul.tabs li a    | (0,4,4) | (0,2,3) |
   | a ativa     | ... ul.tabs li.active a { color: inherit }          | (0,5,4) | (0,3,3) |
   | li          | ... ul.tabs li                                     | (0,4,3) | (0,2,2) |
   | li ativa    | ... ul.tabs li.active                              | (0,5,3) | (nenhum) |
   | ul          | ... ul.tabs { padding; margin; overflow }           | (0,4,2) | (0,2,2) |

   O que se via na tela: aba cinza #515151 dentro de um CHIP cinza-lilás
   (fundo #E9E6ED, borda 1px #CFC8D8, cantos 4px) que ainda se SOBREPÕE ao
   vizinho por `margin: 0 -5px`. A aba ativa também cinza, porque o Woo
   manda `color: inherit` e o <li> herda #515151. E o sublinhado terracota
   da ativa EXISTIA no computed style e não aparecia: o `overflow: hidden`
   da <ul> corta o `margin-bottom: -5px` do <a>. Três defeitos, uma causa.

   ÂNCORA ÚNICA, e isto é decisão medida, não economia:
     T = html .woocommerce div.product .woocommerce-tabs ul.tabs     (0,4,3)
   TODA regra adversária do Woo exige `.woocommerce-tabs`. Onde essa classe
   não existe, o adversário também não existe, e o §19.12 basta sozinho. Por
   isso aqui não cabe o par W/N do §18.12: lá havia dois contextos MEDIDOS
   (widget e loop nativo), aqui há um só. O `html` na frente não é enfeite:
   é o degrau de elemento que transforma cada empate em vitória.

   ⚠️ VENCER POR PESO, NUNCA POR ORDEM. Nenhuma conta deste bloco empata com
   o adversário; todas ganham por contagem de ELEMENTO com a mesma contagem
   de classe. Empate mudaria de resultado conforme esta folha ser instalada
   no tema filho (carrega depois) ou em Site Settings (carrega antes). É o
   mesmo motivo dos pontos travados #14 e #23.

   ⚠️ NÃO ancorar em `.elementor-<postid>` nem `.elementor-element-<id>`. O
   `post-<id>.css` é REGERADO e o id troca ao duplicar o template. Medido
   hoje: nenhuma regra de `post-*.css` casa as abas, porque a aba Style do
   widget "Product Data Tabs" nunca foi aberta. Isso é PORTÃO DE OPERADOR,
   está em INSTALL.md e no README-REV04.

   Este bloco declara SÓ as propriedades disputadas. Os valores continuam
   morando no §19.12; o `verify-tokens.mjs` reprova se os dois divergirem. */

/* 19.16a A <ul>. `overflow` é o que devolve o sublinhado da aba ativa. */
html .woocommerce div.product .woocommerce-tabs ul.tabs {
  margin: 0 0 var(--jn-s-5);
  padding: 0 0 var(--jn-s-1);
  overflow: visible;
}

/* 19.16b Os pseudo-elementos do Woo. Dois desenham a "aba de navegador"
   (as ombreiras curvas do <li>, feitas com box-shadow) e dois são o
   clearfix da <ul>.

   ⚠️ Hoje três dos quatro já estão neutralizados pelo widget do Elementor,
   mas por EMPATE resolvido na ordem de carregamento, não por peso. Basta a
   página de produto não usar o widget (template nativo do Woo) para as
   ombreiras voltarem. Declarar aqui tira a dependência.

   O quarto, `ul.tabs::after`, o widget não cobre: é `content:" "` com
   `display:table`, e como a nossa <ul> é FLEX ele vira um item de flex de
   largura zero que ainda assim consome um `gap` inteiro no fim da fila. */
html .woocommerce div.product .woocommerce-tabs ul.tabs::before,
html .woocommerce div.product .woocommerce-tabs ul.tabs::after,
html .woocommerce div.product .woocommerce-tabs ul.tabs li::before,
html .woocommerce div.product .woocommerce-tabs ul.tabs li::after {
  content: none;
}

/* 19.16c O <li>. É o "design genérico" do relatório: o chip cinza. */
html .woocommerce div.product .woocommerce-tabs ul.tabs li {
  margin: 0;
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
}

/* 19.16d O <li> ativo. Precisa de regra PRÓPRIA: o Woo o pinta em
   `.active` (0,5,3), e o 19.16c, com (0,4,4), perde na contagem de classe.

   ⚠️ `border-bottom-color` fica de fora de propósito. O Woo põe branco ali,
   mas o 19.16c zera a LARGURA da borda de todo <li> vencendo o Woo, então
   a cor não tem o que pintar. Se um dia o 19.16c cair, a borda branca volta
   junto e o sintoma aponta para a causa certa. */
html .woocommerce div.product .woocommerce-tabs ul.tabs li.active {
  background: none;
}

/* 19.16e O <a>. `line-height` entra porque o widget impõe 1. */
html .woocommerce div.product .woocommerce-tabs ul.tabs li a {
  padding: var(--jn-s-2) 0 var(--jn-s-3);
  line-height: var(--jn-lh-head);
  color: var(--jn-text);
}

/* 19.16f O <a> ativo. O Woo manda `color: inherit` e o <li> entrega
   #515151: sem esta regra a aba ativa fica cinza igual às outras, e a
   única diferença entre ativa e inativa passa a ser o sublinhado.

   ⚠️ SÃO DOIS SELETORES e os dois são necessários. MEDIDO no DOM: o Woo
   marca a aba ativa em DOIS lugares ao mesmo tempo, `class="active"` no
   <li> e `aria-selected="true"` no <a>. O §19.12 já cobria os dois; se
   aqui só viesse o `.active`, a diferença entre ativa e inativa passaria a
   depender de qual dos dois sinais o tema mexeu. O de aria pesa (0,5,5)
   igual ao de classe, então nenhum dos dois empata com o Woo (0,5,4). */
html .woocommerce div.product .woocommerce-tabs ul.tabs li.active a,
html .woocommerce div.product .woocommerce-tabs ul.tabs li a[aria-selected="true"] {
  color: var(--jn-heading);
}

/* ==========================================================================
   20. WOOCOMMERCE — CARRINHO
   --------------------------------------------------------------------------
   ⚠️ ESTA SEÇÃO SÓ VALE PARA O CARRINHO CLÁSSICO — o shortcode
   [woocommerce_cart]. Instalação nova de WooCommerce cria a página de
   Carrinho em BLOCOS (`wp-block-woocommerce-cart`), que tem outra marcação e
   NÃO é atingida por nada aqui. Verificação de 5 segundos, feita no site: ver
   o código-fonte da página do carrinho e procurar `wp-block-woocommerce-cart`.
   Se aparecer, trocar o conteúdo da página pelo shortcode (INSTALL passo 6).
   O carrinho do site BR é clássico (WooCommerce 9.1.6, cart.js enfileirado) —
   foi por isso que o clássico é o alvo, e não por preferência.

   ⚠️ O CARRINHO SE REDESENHA SOZINHO POR AJAX. O cart.js do Woo substitui
   `.woocommerce-cart-form` e `.cart_totals` INTEIROS pela resposta do
   servidor a cada mudança de quantidade, cupom ou remoção, e então dispara
   `updated_wc_div`. Três consequências que mandam no desenho:
     1. Marcação injetada por JS SOME. Por isso o stepper, a fileira de
        confiança e o botão do cupom nascem em PHP (snippets-cart.php): o
        servidor re-renderiza e eles voltam. O que é JS aqui é só COMPORTAMENTO,
        sempre delegado no document.
     2. O FOCO É DESTRUÍDO. Quem clicou em "+" perde o foco para o body.
        O axe não vê isso (ele mede o DOM depois da troca). Quem devolve o
        foco é o JS do snippet, casando a chave do item em `cart[<chave>][qty]`.
     3. Ninguém anuncia o novo subtotal. O Woo não emite região viva; a região
        `.jn-cart-live` é impressa ANTES do form (fora do trecho substituído)
        justamente para sobreviver à troca.

   ⚠️ NÃO existe .json para esta página. Carrinho é conteúdo de uma página
   comum com o shortcode; não passa pelo Theme Builder. Quem prova esta
   entrega são os guardas de §20 no verify-tokens.mjs, lendo o CSS e as
   strings do PHP — não um template.
   ========================================================================== */

/* --- 20.1 Casca e grade ---------------------------------------------------
   Duas colunas: itens à esquerda, resumo à direita. O `:has(> form…)` mira o
   wrapper que realmente contém o carrinho — `.woocommerce` é classe genérica
   demais para virar alvo solto. Sem suporte a `:has()` a página cai numa
   coluna só, que continua legível: degradação, não quebra. */
body.woocommerce-cart .woocommerce > .woocommerce-notices-wrapper { grid-column: 1 / -1; }

/* ⚠️ O H1 DA PÁGINA VEM DO SNIPPET, e isso não é capricho: medido no site BR,
   a página do carrinho não tem NENHUM h1 — o template
   `elementor_header_footer` não imprime o título e o Woo não põe título
   próprio. Um documento sem h1 quebra a navegação por cabeçalho de quem usa
   leitor de tela. Como cada estado do carrinho tem o seu (cheio: "Cart";
   vazio: "Your cart is empty"), há SEMPRE exatamente um.
   ⚠️ Consequência para o operador: não acrescentar um widget de título
   "Cart" na página, senão viram dois h1. */
/* ⚠️ O PREFIXO `body` NÃO É ENFEITE — é a mesma armadilha do ponto travado
   #14. `.jn-cart-title` sozinha é (0,1,0) e PERDE para
   `.jn-section :is(h1,h2,h3,h4)`, que é (0,1,1): o título ignorava família e
   peso declarados aqui e caía em Fraunces 400 sem erro nenhum na tela.
   Medido no preview antes de existir esta linha.
   E a fonte É Fraunces de propósito, agora dito em voz alta: é o mesmo
   tratamento do título de arquivo (§18.1) porque é o mesmo tipo de objeto,
   um cabeçalho utilitário de página do Woo. A Amatic SC fica para os títulos
   de seção da marca, não para "Cart". */
body .jn-cart-title {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h1);
  font-weight: 600;
  line-height: var(--jn-lh-tight);
  font-optical-sizing: auto;
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-6);
}
/* Notice que ficou sem texto (ex.: aviso do Cart Stock Reducer silenciado
   porque a linha de reserva o substitui) não pode virar caixa vazia na tela. */
body.woocommerce-cart .woocommerce-info:empty,
body.woocommerce-cart .woocommerce-message:empty { display: none; }

@media (min-width: 861px) {
  body.woocommerce-cart .woocommerce:has(> form.woocommerce-cart-form) {
    display: grid;
    grid-template-columns: minmax(0, 2fr) minmax(300px, 1fr);
    column-gap: var(--jn-s-7);
    /* Sem row-gap: o ritmo vertical já vem das margens de cada bloco
       (título, reserva, tabela). Somar os dois criava um vão duplo. */
    row-gap: 0;
  }
  /* ⚠️ CATCH-ALL DE ESPECIFICIDADE ZERO, e ele é obrigatório. Só DOIS filhos
     de `.woocommerce` são colunas: o form e o resumo. Todo o resto (título,
     região viva, linha de reserva, avisos, e o que qualquer plugin imprima em
     `woocommerce_before_cart`) atravessa a largura inteira. Sem esta regra o
     h1 caía na coluna 1 e a linha de reserva ia parar na coluna 2, ao lado
     dele — foi exatamente o que aconteceu no preview.
     O `:where()` zera a especificidade de propósito: assim as duas regras
     seguintes vencem sem disputa, e uma regra futura também vence. */
  :where(body.woocommerce-cart .woocommerce:has(> form.woocommerce-cart-form)) > * { grid-column: 1 / -1; }
  body.woocommerce-cart .woocommerce > form.woocommerce-cart-form { grid-column: 1 / 2; }
  body.woocommerce-cart .woocommerce > .cart-collaterals { grid-column: 2 / 3; }
  /* Cross-sells foi re-hookado para fora da coluna do resumo (ver
     snippets-cart.php). Aqui ele ocupa a largura toda, abaixo das duas. */
  body.woocommerce-cart .woocommerce > .cross-sells { grid-column: 1 / -1; }
  /* ⚠️ QUEM GRUDA É O `.cart_totals`, NUNCA O `.cart-collaterals`.
     Tentei o contêiner primeiro e o resultado foi o cartão de totais passando
     POR CIMA dos cross-sells: o retângulo que limita um elemento sticky é o do
     bloco que o contém, e no Chrome um item de grade sticky é limitado pelo
     CONTÊINER da grade inteiro, não pela própria linha. Como a grade vai até o
     fim da página, o cartão descia junto com a rolagem sem parar nunca.
     Aqui o `.cart-collaterals` fica normal e ESTICADO na linha (por isso não
     há `align-items: start` na grade): ele vira a moldura, e o `.cart_totals`
     gruda dentro dela. O deslocamento fica limitado por construção.
     ⚠️ E é por isso que no produto (§19.14) não deu: lá a buy box tinha 970px,
     mais alta que a janela, e elemento mais alto que a tela nunca gruda. */
  body.woocommerce-cart .cart_totals {
    position: sticky;
    top: var(--jn-s-5);
  }
}
body.woocommerce-cart .cart-collaterals { width: auto; }
body.woocommerce-cart .cart-collaterals::before,
body.woocommerce-cart .cart-collaterals::after { content: none; }

/* --- 20.2 Tabela de itens (desktop) --------------------------------------
   Continua sendo table: a lista de compras É tabular, e trocar por divs só
   para "modernizar" tiraria o cabeçalho de coluna de quem usa leitor de tela. */
body.woocommerce-cart table.cart.shop_table {
  width: 100%;
  margin: 0 0 var(--jn-s-6);
  border: 0;
  border-collapse: collapse;
  border-radius: 0;
  /* ⚠️ `fixed` É OBRIGATÓRIO, não é preferência de renderização. Com o padrão
     `auto` o navegador distribui as colunas pelo conteúdo e IGNORA os `width`
     daqui (eles viram sugestão): medido no preview, a coluna do × ficou com
     214px, a do nome com 106px, e a tabela inteira esticou para 825px dentro
     de uma coluna de grade de 709px — ou seja, passou POR BAIXO do cartão de
     totais. Com `fixed`, os width abaixo mandam e o 100% é exato.
     Regra prática: tabela dentro de coluna de grade precisa de table-layout
     fixed, senão o conteúdo decide a largura e a grade não tem como segurar. */
  table-layout: fixed;
}
/* As larguras vivem na PRIMEIRA linha porque é ela que o `fixed` lê. A coluna
   do nome fica sem width de propósito: ela recebe o que sobrar. */
body.woocommerce-cart table.cart.shop_table th.product-remove    { width: 56px; }
body.woocommerce-cart table.cart.shop_table th.product-thumbnail { width: 104px; }
body.woocommerce-cart table.cart.shop_table th.product-price     { width: 92px; }
body.woocommerce-cart table.cart.shop_table th.product-quantity  { width: 176px; }
body.woocommerce-cart table.cart.shop_table th.product-subtotal  { width: 104px; }
body.woocommerce-cart table.cart.shop_table thead th {
  padding: 0 var(--jn-s-3) var(--jn-s-3);
  text-align: left;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-eyebrow);
  font-weight: 700;
  letter-spacing: var(--jn-ls-eyebrow);
  text-transform: uppercase;
  color: var(--jn-ink-deep);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--jn-line-strong);
}
body.woocommerce-cart table.cart.shop_table th.product-subtotal,
body.woocommerce-cart table.cart.shop_table td.product-subtotal { text-align: right; }
body.woocommerce-cart table.cart.shop_table tr.cart_item td {
  padding: var(--jn-s-5) var(--jn-s-3);
  vertical-align: middle;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--jn-line);
}
body.woocommerce-cart td.product-thumbnail img {
  width: 88px;
  height: 88px;
  /* Foto de produto do catálogo é quadrada; `contain` evita cortar a estampa,
     que é justamente o que a cliente está comprando. */
  object-fit: contain;
  background: var(--jn-white);
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-media);
  display: block;
}
body.woocommerce-cart td.product-name a {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h3);
  line-height: var(--jn-lh-head);
  font-optical-sizing: auto;
  color: var(--jn-heading);
  text-decoration: none;
}
body.woocommerce-cart td.product-name a:hover { color: var(--jn-terracotta); text-decoration: underline; }
body.woocommerce-cart td.product-name dl.variation {
  margin: var(--jn-s-2) 0 0;
  font-size: var(--jn-t-small);
  color: var(--jn-ink-deep);
}
body.woocommerce-cart td.product-price,
body.woocommerce-cart td.product-subtotal {
  font-family: var(--jn-font-head);
  font-size: 1.0625rem;
  font-weight: 600;
  color: var(--jn-terracotta);
  white-space: nowrap;
}
/* Conformidade Omnibus, mesmo critério do §18.6, com UMA diferença que não é
   detalhe: aqui NÃO existe opacidade.
   ⚠️ PONTO TRAVADO #17 OUTRA VEZ, por outro caminho. O `opacity: .8` do §18.6
   foi medido sobre BRANCO (4,92:1) porque lá o preço vive dentro do cartão
   branco do produto. Na tabela do carrinho o fundo é o creme da página, e a
   MESMA opacidade dá 4,49:1 — reprovou no axe, medido neste preview.
   Opacidade só pode ser justificada contra o fundo em que o texto realmente
   cai. Aqui o preço antigo se distingue pelo risco e pelo corpo menor, que é
   o que deveria distingui-lo desde o começo. NÃO reintroduzir opacity. */
body.woocommerce-cart .product-price del,
body.woocommerce-cart .product-subtotal del {
  color: var(--jn-ink-deep);
  font-weight: 400;
  font-size: .9em;
  text-decoration-thickness: 1px;
}
body.woocommerce-cart .product-price ins,
body.woocommerce-cart .product-subtotal ins {
  text-decoration: none;
  color: var(--jn-terracotta);
  font-weight: 700;
}

/* Remover item — o Woo emite um <a class="remove"> vermelho (#a00). Vira um
   controle neutro da marca. Alvo de toque de 44px em qualquer tela: é o menor
   controle da página e o mais fácil de errar com o polegar. */
body.woocommerce-cart td.product-remove a.remove,
body.woocommerce-cart td.product-remove a.remove:hover {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px;
  height: 44px;
  font-size: 1.375rem;
  line-height: 1;
  font-weight: 400;
  text-decoration: none;
  color: var(--jn-ink-deep);
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-pill);
  transition: color var(--jn-d-fast) var(--jn-ease-soft),
              border-color var(--jn-d-fast) var(--jn-ease-soft),
              background-color var(--jn-d-fast) var(--jn-ease-soft);
}
body.woocommerce-cart td.product-remove a.remove { background: transparent; }
body.woocommerce-cart td.product-remove a.remove:hover {
  color: var(--jn-terracotta);
  border-color: var(--jn-terracotta);
  background: var(--jn-shade);
}
body.woocommerce-cart td.product-remove a.remove:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 2px;
}

/* --- 20.3 Itens empilhados (celular) --------------------------------------
   ⚠️ ESTE BLOCO NÃO É OPCIONAL E NÃO PODE SER DELEGADO AO WOO. O empilhamento
   nativo vem de `shop_table_responsive` + `td::before { content: attr(data-title) }`,
   que moram no woocommerce-layout.css. Setups Hello + Elementor costumam
   desenfileirar o CSS do Woo — é exatamente o que já mordeu o
   `.screen-reader-text` no §18.0. Se acontecer aqui e nós tivermos confiado no
   Woo, o celular recebe uma tabela de 6 colunas espremida E sem rótulo nenhum.
   A skin declara por conta própria. */
@media (max-width: 860px) {
  body.woocommerce-cart table.cart.shop_table thead {
    position: absolute;
    width: 1px; height: 1px;
    margin: -1px; padding: 0;
    overflow: hidden;
    clip: rect(0 0 0 0);
    white-space: nowrap;
    border: 0;
  }
  /* Tudo vira bloco de uma vez, inclusive a linha do cupom e a de ações.
     ⚠️ Sem incluir `tr` e `td` aqui, essas duas linhas continuariam sendo
     table-row dentro de uma tabela já em `display: block`, e o navegador as
     embrulharia numa tabela anônima com largura de conteúdo. Não usar `:has()`
     nesta cascata de propósito: é a base do layout no celular e não pode
     depender de suporte a seletor. */
  body.woocommerce-cart table.cart.shop_table,
  body.woocommerce-cart table.cart.shop_table tbody,
  body.woocommerce-cart table.cart.shop_table tbody > tr,
  body.woocommerce-cart table.cart.shop_table tbody > tr > td { display: block; }
  body.woocommerce-cart table.cart.shop_table tr.cart_item {
    display: grid;
    grid-template-columns: 84px minmax(0, 1fr) 44px;
    grid-template-areas:
      "thumb name  remove"
      "thumb price price"
      "thumb qty   subtotal";
    gap: var(--jn-s-2) var(--jn-s-4);
    align-items: center;
    padding: var(--jn-s-5) 0;
    border-bottom: 1px solid var(--jn-line);
  }
  body.woocommerce-cart table.cart.shop_table tr.cart_item > td {
    display: block;
    width: auto;
    padding: 0;
    border: 0;
  }
  body.woocommerce-cart td.product-thumbnail { grid-area: thumb; align-self: start; }
  body.woocommerce-cart td.product-name      { grid-area: name; }
  body.woocommerce-cart td.product-remove    { grid-area: remove; justify-self: end; align-self: start; }
  body.woocommerce-cart td.product-price     { grid-area: price; }
  body.woocommerce-cart td.product-quantity  { grid-area: qty; }
  body.woocommerce-cart td.product-subtotal  { grid-area: subtotal; justify-self: end; }
  body.woocommerce-cart td.product-thumbnail img { width: 84px; height: 84px; }
  /* O rótulo vem do atributo data-title, que o Woo já imprime traduzido — por
     isso o WPML continua mandando no texto mesmo saindo de um ::before.
     Escrever a palavra "Price" aqui dentro seria justamente o erro. */
  body.woocommerce-cart td.product-price::before,
  body.woocommerce-cart td.product-subtotal::before {
    content: attr(data-title);
    display: block;
    font-family: var(--jn-font-body);
    font-size: var(--jn-t-eyebrow);
    font-weight: 700;
    letter-spacing: var(--jn-ls-eyebrow);
    text-transform: uppercase;
    color: var(--jn-ink-deep);
  }
  body.woocommerce-cart td.product-price {
    font-size: var(--jn-t-small);
    font-weight: 400;
    color: var(--jn-ink-deep);
  }
  body.woocommerce-cart td.product-name a { font-size: 1.0625rem; }
}

/* --- 20.4 Stepper de quantidade -------------------------------------------
   ⚠️ Os sinais + e − são desenhados em BORDA CSS, sem texto injetado — mesma
   decisão do ponto travado #13 (chevron do card). Botão com glifo em
   `content:` não é traduzível, não é selecionável, e alguns leitores de tela
   leem o caractere solto. Aqui o nome acessível vem do aria-label, em PHP.

   ⚠️ O PISO É 1, NÃO 0. No carrinho o Woo põe `min="0"` de propósito: zero
   REMOVE o item. Um "−" que caminha até zero apaga o produto sem nenhum aviso,
   e a cliente só descobre depois. Quem remove é o ×, que é explícito. O clamp
   vive no JS do snippet; esta nota existe para ninguém "soltar" o mínimo
   achando que é limitação boba. */
body.woocommerce-cart td.product-quantity .quantity {
  display: inline-flex;
  align-items: stretch;
  background: var(--jn-white);
  border: 2px solid var(--jn-ink-deep);
  border-radius: var(--jn-r-pill);
  overflow: hidden;
}
body.woocommerce-cart td.product-quantity .quantity input.qty {
  width: 56px;
  min-height: 44px;
  padding: 0;
  text-align: center;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  font-weight: 600;
  color: var(--jn-ink-deep);
  background: transparent;
  border: 0;
  border-radius: 0;
  /* As setas nativas do input number brigam com o stepper: dois controles
     para a mesma coisa, em 2px de largura. */
  -moz-appearance: textfield;
  appearance: textfield;
}
body.woocommerce-cart td.product-quantity .quantity input.qty::-webkit-outer-spin-button,
body.woocommerce-cart td.product-quantity .quantity input.qty::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}
body.woocommerce-cart td.product-quantity .quantity input.qty:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: -3px;
}
.jn-qty-btn {
  position: relative;
  flex: none;
  width: 44px;
  min-height: 44px;
  padding: 0;
  font-size: 0;
  color: var(--jn-ink-deep);
  background: transparent;
  border: 0;
  cursor: pointer;
  transition: background-color var(--jn-d-fast) var(--jn-ease-soft),
              color var(--jn-d-fast) var(--jn-ease-soft);
}
.jn-qty-btn:hover { background: var(--jn-shade); color: var(--jn-terracotta); }
.jn-qty-btn:focus-visible { outline: 3px solid var(--jn-terracotta); outline-offset: -3px; }
.jn-qty-btn[disabled] { opacity: .35; cursor: default; }
.jn-qty-btn[disabled]:hover { background: transparent; color: var(--jn-ink-deep); }
/* Barra horizontal: comum aos dois. */
.jn-qty-btn::before {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 13px; height: 0;
  border-top: 2px solid currentColor;
  transform: translate(-50%, -50%);
}
/* Barra vertical: só no "mais". */
.jn-qty-btn--plus::after {
  content: "";
  position: absolute;
  top: 50%; left: 50%;
  width: 0; height: 13px;
  border-left: 2px solid currentColor;
  transform: translate(-50%, -50%);
}

/* --- 20.5 Linha de ações: cupom de-enfatizado + atualizar -----------------
   ⚠️ NÃO PÔR `display: flex` (nem grid) NO `<td class="actions">`. Parece a
   solução óbvia para reordenar, e destrói a tabela: um `<td>` com display de
   flex deixa de ser célula, o navegador o embrulha numa célula anônima SEM o
   `colspan="6"`, e ele passa a valer a largura da PRIMEIRA coluna. Medido no
   preview: a linha inteira de ações encolheu para 56px. Com `table-layout:
   fixed` (§20.2) o estrago é garantido; com layout automático só é mascarado.

   Por isso a ordem visual não vem de `order`, e sim do DOM: o botão
   "Have a coupon?" é impresso pelo snippet em `woocommerce_cart_contents`,
   como uma LINHA PRÓPRIA antes da linha de ações. É o único hook dentro do
   <tbody> que roda antes dela, e assim tudo continua sendo tabela de verdade.

   O cupom fica discreto de propósito: campo de cupom em destaque manda a
   cliente para o Google atrás de código, e ela costuma não voltar. */
body.woocommerce-cart tr.jn-coupon-row td {
  padding: var(--jn-s-5) 0 0;
  border: 0;
  background: transparent;
}
body.woocommerce-cart td.actions {
  padding: var(--jn-s-3) 0 0;
  border: 0;
  background: transparent;
  /* Alinha o "Update cart" à direita. `margin-left: auto` num <button> não
     serve: o Chrome resolve a largura de botão em bloco como fit-content e as
     margens automáticas não absorvem a sobra (testado no preview, o botão
     ficou colado à esquerda). O alinhamento de texto funciona porque o botão
     continua sendo inline-flex. */
  text-align: right;
}
body.woocommerce-cart td.actions .coupon {
  display: flex;
  flex-wrap: wrap;
  gap: var(--jn-s-2);
  margin: 0 0 var(--jn-s-3);
  text-align: left;
}
/* ⚠️ OBRIGATÓRIO. O atributo `hidden` vale por uma regra do navegador
   (`[hidden] { display: none }`) que QUALQUER regra de classe vence. Sem esta
   linha o JS marca `hidden`, o aria-expanded vira "false", e o campo continua
   na tela: estado mentindo para quem enxerga e para quem ouve ao mesmo tempo.
   Visto acontecer no preview. */
body.woocommerce-cart td.actions .coupon[hidden] { display: none; }

.jn-coupon-toggle {
  white-space: nowrap;
  display: inline-flex;
  align-items: center;
  gap: var(--jn-s-2);
  min-height: 44px;
  padding: 0 var(--jn-s-2);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 600;
  color: var(--jn-ink-deep);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--jn-line-strong);
  border-radius: 0;
  cursor: pointer;
}
.jn-coupon-toggle:hover { color: var(--jn-terracotta); border-bottom-color: var(--jn-terracotta); }
.jn-coupon-toggle:focus-visible { outline: 3px solid var(--jn-terracotta); outline-offset: 3px; }
/* Seta em borda CSS (ponto travado #13), girando ao abrir. */
.jn-coupon-toggle::after {
  content: "";
  width: 7px; height: 7px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg) translate(-2px, -2px);
  transition: transform var(--jn-d-fast) var(--jn-ease-soft);
}
.jn-coupon-toggle[aria-expanded="true"]::after { transform: rotate(-135deg) translate(-2px, -2px); }

body.woocommerce-cart td.actions .coupon input#coupon_code {
  flex: 1 1 12rem;
  min-height: 48px;
  padding: 0 var(--jn-s-4);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  color: var(--jn-ink-deep);
  background: var(--jn-white);
  /* Ponto travado #12: borda visível. O checkout BR tinha pílula sem borda. */
  border: 2px solid var(--jn-ink-deep);
  border-radius: var(--jn-r-pill);
}
body.woocommerce-cart td.actions .coupon input#coupon_code:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 2px;
}
/* ⚠️ Cadeia longa DE PROPÓSITO. Estes botões nascem `.button` e o Woo estiliza
   `.woocommerce table.cart td.actions .button` — um seletor curto perde e o
   botão volta ao cinza/verde default. Esta é (0,5,4). Mesma lição do ponto
   travado #15, por outro caminho. */
body.woocommerce-cart .woocommerce table.cart td.actions button.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 var(--jn-s-5);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--jn-ink-deep);
  background: transparent;
  border: 2px solid var(--jn-line-strong);
  border-radius: var(--jn-r-pill);
  cursor: pointer;
  transition: border-color var(--jn-d-fast) var(--jn-ease-soft),
              color var(--jn-d-fast) var(--jn-ease-soft);
}
body.woocommerce-cart .woocommerce table.cart td.actions button.button:hover {
  color: var(--jn-terracotta);
  border-color: var(--jn-terracotta);
  background: transparent;
}
body.woocommerce-cart .woocommerce table.cart td.actions button.button:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 3px;
}
body.woocommerce-cart .woocommerce table.cart td.actions button.button[disabled] {
  opacity: .45;
  cursor: default;
}
/* Cupom aplicado vira linha nos totais, com um × para remover. */
body.woocommerce-cart .cart-discount a.woocommerce-remove-coupon {
  margin-left: var(--jn-s-2);
  font-size: var(--jn-t-small);
  color: var(--jn-ink-deep);
}
body.woocommerce-cart .cart-discount a.woocommerce-remove-coupon:hover { color: var(--jn-terracotta); }

/* --- 20.6 Resumo do pedido — COMPONENTE COMPARTILHADO COM O CHECKOUT ------
   ⚠️ A lista de seletores desta subseção é o contrato do brief §1
   ("componente reusável no checkout"). `.woocommerce-checkout-review-order` JÁ
   está em cada regra, e é assim que o reuso vira real em vez de intenção:
   quando o brief do checkout chegar, o bloco de totais já está desenhado e NÃO
   deve ser reescrito lá. Nenhuma marcação de checkout foi construída aqui — só
   a skin do bloco que as duas páginas emitem. */
.cart_totals,
.woocommerce-checkout-review-order {
  padding: var(--jn-s-6);
  background: var(--jn-white);
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-card);
}
.cart_totals > h2,
.woocommerce-checkout-review-order > h2 {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h3);
  font-weight: 600;
  line-height: var(--jn-lh-head);
  font-optical-sizing: auto;
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-4);
}
.cart_totals table.shop_table,
.woocommerce-checkout-review-order table.shop_table {
  width: 100%;
  margin: 0;
  border: 0;
  border-collapse: collapse;
  border-radius: 0;
}
.cart_totals table.shop_table th,
.cart_totals table.shop_table td,
.woocommerce-checkout-review-order table.shop_table th,
.woocommerce-checkout-review-order table.shop_table td {
  padding: var(--jn-s-3) 0;
  vertical-align: top;
  text-align: left;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--jn-line);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 400;
  color: var(--jn-ink-deep);
}
.cart_totals table.shop_table th,
.woocommerce-checkout-review-order table.shop_table th { font-weight: 600; width: 45%; }
.cart_totals table.shop_table td,
.woocommerce-checkout-review-order table.shop_table td { text-align: right; }
.cart_totals tr.order-total th,
.cart_totals tr.order-total td,
.woocommerce-checkout-review-order tr.order-total th,
.woocommerce-checkout-review-order tr.order-total td {
  padding-top: var(--jn-s-4);
  border-bottom: 0;
  font-family: var(--jn-font-head);
  font-size: 1.25rem;
  font-weight: 700;
  color: var(--jn-heading);
}
.cart_totals tr.order-total td,
.woocommerce-checkout-review-order tr.order-total td { color: var(--jn-terracotta); }
.cart_totals .includes_tax,
.woocommerce-checkout-review-order .includes_tax {
  display: block;
  font-size: var(--jn-t-eyebrow);
  font-weight: 400;
  color: var(--jn-ink-deep);
}
/* Frete: lista de métodos + calculadora nativa.
   ⚠️ NENHUMA regra de `display` em `.shipping-calculator-form`. O Woo abre e
   fecha esse bloco com slideToggle do jQuery, que escreve `display` e `height`
   INLINE — estilo inline vence folha, e o nosso perderia de qualquer jeito;
   pior, uma regra nossa conflita com a animação e o painel pisca. Mesma
   classe de armadilha do flexslider na galeria do produto (§19.3). */
.cart_totals ul#shipping_method,
.cart_totals ul.woocommerce-shipping-methods {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--jn-s-2);
  text-align: left;
}
.cart_totals ul#shipping_method li { margin: 0; }
.cart_totals ul#shipping_method label { font-size: var(--jn-t-small); color: var(--jn-ink-deep); }
.cart_totals .woocommerce-shipping-destination {
  margin: var(--jn-s-2) 0 0;
  font-size: var(--jn-t-eyebrow);
  color: var(--jn-ink-deep);
  text-align: left;
}
.cart_totals a.shipping-calculator-button {
  display: inline-block;
  margin-top: var(--jn-s-2);
  font-size: var(--jn-t-small);
  font-weight: 600;
  color: var(--jn-terracotta);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.cart_totals a.shipping-calculator-button:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 2px;
}
.cart_totals .shipping-calculator-form { text-align: left; padding-top: var(--jn-s-3); }
.cart_totals .shipping-calculator-form p { margin: 0 0 var(--jn-s-3); }
/* Nota impressa pelo snippet quando o Woo ainda não tem como calcular o frete
   (sem zona configurada, sem endereço). Sem ela o resumo mostra só subtotal e
   total, e a cliente conclui que o frete está incluído. */
/* ⚠️ A CADEIA PRECISA DO `.cart_totals`. `.jn-ship-note td` sozinho é (0,1,1)
   e perde para `.cart_totals table.shop_table td`, que é (0,2,2) e está logo
   acima: a linha herdaria o corpo das outras e o tamanho declarado aqui não
   valeria nada. Mesma classe de engano dos dois h1 e do `[hidden]` do cupom. */
.cart_totals tr.jn-ship-note td {
  font-size: var(--jn-t-eyebrow);
  color: var(--jn-ink-deep);
}

/* --- 20.7 CTA primário, confiança e "continue shopping" -------------------
   ⚠️ PONTO TRAVADO #19 — CADEIA LONGA OBRIGATÓRIA NO "PROCEED TO CHECKOUT".
   O botão nasce `class="checkout-button button alt wc-forward"`. É o mesmo
   `.button.alt` do ponto travado #15, mas #15 está escopado em
   `.single_add_to_cart_button` e NÃO alcança este. Sem a cadeia abaixo, o
   elemento mais importante da página volta ao verde/roxo default do Elementor
   — exatamente o defeito que o REFERENCE §A.3 mandou eliminar. Esta é (0,6,2)
   contra os (0,3,1) de `.woocommerce a.button.alt`. NÃO encurtar. */
body.woocommerce-cart .woocommerce .wc-proceed-to-checkout { padding: var(--jn-s-5) 0 0; }
body.woocommerce-cart .woocommerce .wc-proceed-to-checkout a.checkout-button.button.alt {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 56px;
  margin: 0;
  padding: 0 var(--jn-s-5);
  font-family: var(--jn-font-body);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--jn-on-action);              /* branco sobre terracota = 6,45:1 */
  background-color: var(--jn-terracotta);
  border: 2px solid var(--jn-terracotta);
  border-radius: var(--jn-r-pill);
  transition: background-color var(--jn-d-base) var(--jn-ease-soft),
              border-color var(--jn-d-base) var(--jn-ease-soft),
              transform var(--jn-d-fast) var(--jn-ease-soft),
              box-shadow var(--jn-d-base) var(--jn-ease-soft);
}
body.woocommerce-cart .woocommerce .wc-proceed-to-checkout a.checkout-button.button.alt:hover {
  background-color: var(--jn-ink-deep);
  border-color: var(--jn-ink-deep);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px var(--jn-shadow-lift);
}
body.woocommerce-cart .woocommerce .wc-proceed-to-checkout a.checkout-button.button.alt:active {
  transform: translateY(0);
  box-shadow: none;
}
body.woocommerce-cart .woocommerce .wc-proceed-to-checkout a.checkout-button.button.alt:focus-visible {
  outline: 3px solid var(--jn-ink-deep);
  outline-offset: 3px;
}
/* A fileira de confiança REUSA o componente do §19.8 — mesmas classes, mesma
   escala de ícone. Só o entorno muda, porque a coluna do resumo é estreita. */
body.woocommerce-cart .wc-proceed-to-checkout .jn-trust {
  margin: var(--jn-s-4) 0 0;
  padding: var(--jn-s-4);
  grid-template-columns: minmax(0, 1fr);
}
.jn-continue-shopping {
  display: block;
  margin: var(--jn-s-4) 0 0;
  text-align: center;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 600;
  color: var(--jn-ink-deep);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.jn-continue-shopping:hover { color: var(--jn-terracotta); }
.jn-continue-shopping:focus-visible { outline: 3px solid var(--jn-terracotta); outline-offset: 3px; }

/* --- 20.8 Linha de reserva (Cart Stock Reducer) ---------------------------
   ⚠️ ENQUADRAMENTO É RESERVA, NÃO AMEAÇA (REFERENCE §A.5). O mecanismo é real
   — o plugin segura estoque e devolve depois de um tempo — e por isso a
   informação é honesta. O que não pode voltar é o tom de "ou seu item expira".
   ⚠️ E a linha só existe quando há reserva de verdade: o snippet a imprime
   somente se os itens do carrinho tiverem `csr_expire_time`. Cronômetro
   aparecendo sem nada reservado seria um dark pattern inventado por nós. */
.jn-reserve {
  display: flex;
  align-items: center;
  gap: var(--jn-s-3);
  margin: 0 0 var(--jn-s-5);
  padding: var(--jn-s-3) var(--jn-s-4);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  color: var(--jn-ink-deep);
  background: var(--jn-shade);
  border-radius: var(--jn-r-card);
}
.jn-reserve__icon { flex: none; width: 20px; height: 20px; color: var(--jn-terracotta); }
.jn-reserve__icon svg { width: 100%; height: 100%; display: block; }
.jn-reserve__clock {
  font-family: var(--jn-font-head);
  font-weight: 700;
  color: var(--jn-heading);
  /* Dígito de largura fixa: sem isto o texto ao lado dança a cada segundo. */
  font-variant-numeric: tabular-nums;
}

/* --- 20.9 Carrinho vazio --------------------------------------------------
   O Woo entrega `<p class="cart-empty woocommerce-info">` mais um botão
   "Return to shop". Nada aqui reconstrói isso: o texto vira "Your cart is
   empty" pelo filtro `wc_empty_cart_message`, e o rótulo do botão pelo
   `woocommerce_return_to_shop_text` (ambos no snippet). A skin só tira a cara
   de aviso de sistema e dá um bloco acolhedor. */
body.woocommerce-cart .woocommerce:has(> .jn-empty-cart) { display: block; }
.jn-empty-cart {
  max-width: 34rem;
  margin: 0 auto;
  padding: var(--jn-s-8) var(--jn-s-5) var(--jn-s-4);
  text-align: center;
}
/* Mesmo `body` do `.jn-cart-title`, pela mesma razão, e a MESMA fonte de
   propósito: os dois são o h1 da mesma página, em estados diferentes. Trocar
   a família só num deles faria o carrinho mudar de cara ao esvaziar. O calor
   deste estado vem da ilustração e da frase, não de um segundo tipo. */
body .jn-empty-cart__title {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h1);
  font-weight: 600;
  line-height: var(--jn-lh-tight);
  font-optical-sizing: auto;
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-3);
}
.jn-empty-cart__text {
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-lead);
  line-height: var(--jn-lh-body);
  color: var(--jn-ink-deep);
  margin: 0;
}
.jn-empty-cart__mark {
  display: block;
  width: 72px; height: 72px;
  margin: 0 auto var(--jn-s-5);
  color: var(--jn-terracotta);
}
.jn-empty-cart__mark svg { width: 100%; height: 100%; display: block; }
/* A mensagem nativa some da tela mas CONTINUA no DOM para leitor de tela: é
   ela que o Woo re-renderiza e que anuncia a mudança de estado.
   ⚠️ Os DOIS seletores são necessários: até o WooCommerce 8 a mensagem era um
   `<p class="cart-empty">` solto; do 9 em diante ela vem embrulhada num
   `<div class="wc-empty-cart-message">` (confirmado no site BR, Woo 9.1.6).
   Manter só um esconde a metade errada dependendo da versão. */
body.woocommerce-cart .wc-empty-cart-message,
body.woocommerce-cart p.cart-empty {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}
body.woocommerce-cart .return-to-shop { text-align: center; margin: 0 0 var(--jn-s-9); }
body.woocommerce-cart .woocommerce .return-to-shop a.button.wc-backward {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 52px;
  padding: 0 var(--jn-s-6);
  font-family: var(--jn-font-body);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  text-decoration: none;
  color: var(--jn-on-action);
  background-color: var(--jn-terracotta);
  border: 2px solid var(--jn-terracotta);
  border-radius: var(--jn-r-pill);
}
body.woocommerce-cart .woocommerce .return-to-shop a.button.wc-backward:hover {
  background-color: var(--jn-ink-deep);
  border-color: var(--jn-ink-deep);
}
body.woocommerce-cart .woocommerce .return-to-shop a.button.wc-backward:focus-visible {
  outline: 3px solid var(--jn-ink-deep);
  outline-offset: 3px;
}

/* --- 20.10 Cross-sells ----------------------------------------------------
   NÃO redefine o card: o Woo emite o MESMO `ul.products > li.product` da
   categoria, então o §18.5 já vale. Aqui só o cabeçalho e o respiro — e
   mantido MENOR que o resto da página de propósito: quem chegou ao carrinho
   está a um clique de comprar, e vitrine competindo com o CTA custa pedido.
   Três itens, uma fileira (limites no snippet). */
body.woocommerce-cart .cross-sells {
  width: auto;
  margin-top: var(--jn-s-8);
  padding-top: var(--jn-s-6);
  border-top: 1px solid var(--jn-line);
}
body.woocommerce-cart .cross-sells > h2 {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h3);
  font-weight: 600;
  line-height: var(--jn-lh-head);
  font-optical-sizing: auto;
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-5);
}

/* --- 20.11 Barra fixa do carrinho (celular) -------------------------------
   REUSA `.jn-buybar` do §19.14 — mesma peça, mesma altura, mesma sombra.
   ⚠️ Mesma decisão de acessibilidade do produto: o contêiner é
   `aria-hidden="true"` e o link tem `tabindex="-1"` DE PROPÓSITO. Não é um
   segundo caminho para o checkout, é um atalho de ponteiro para o link que já
   está na página. Duplicar um link focável com o mesmo nome só encheria a
   ordem de leitura. Não "corrigir" isso. */
a.jn-buybar__btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  padding: 0 var(--jn-s-4);
}
.jn-buybar__total { display: flex; flex-direction: column; gap: 2px; line-height: 1.15; }
.jn-buybar__label {
  display: block;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-eyebrow);
  font-weight: 700;
  letter-spacing: var(--jn-ls-eyebrow);
  text-transform: uppercase;
  color: var(--jn-ink-deep);
}

/* --- 20.12 Estado de atualização e região viva ----------------------------
   O Woo põe `.processing` no bloco enquanto o AJAX corre. Sem tratamento o
   subtotal troca de valor num piscar seco. Aqui ele esmaece e volta — a
   "atualização suave" do brief §5, sem nada chamativo. */
body.woocommerce-cart .woocommerce-cart-form.processing,
body.woocommerce-cart .cart_totals.processing {
  /* ⚠️ 0.75, e não algo mais dramático, porque ISTO SE SOMA a outra camada:
     o `block()` do Woo já joga um véu branco a 0.6 por cima do mesmo bloco.
     Empilhar dois esmaecimentos deixa a atualização turva em vez de discreta. */
  opacity: .75;
  pointer-events: none;
  transition: opacity var(--jn-d-base) var(--jn-ease-soft);
}
/* ⚠️ NÃO remover achando que é div vazia. É a única coisa que anuncia o novo
   total para leitor de tela: o Woo troca `.cart_totals` inteiro por AJAX e não
   emite região viva nenhuma. É impressa ANTES do form justamente para ficar
   fora do trecho substituído — se entrar dentro, o nó é trocado e o anúncio
   nunca sai. */
.jn-cart-live {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- 20.13 Reduced motion -------------------------------------------------
   O que é desligado aqui é o que MEXE: o levantar do CTA, o giro da seta do
   cupom e o esmaecer durante o AJAX. As transições de COR que sobram (stepper,
   ×, "Update cart") ficam de propósito: `prefers-reduced-motion` existe para
   desconforto vestibular, e trocar cor não desloca nada. Tirá-las devolveria
   estados de foco e hover em corte seco, que é pior para todo mundo — inclusive
   para quem pediu menos movimento. Ausência deliberada, não esquecimento. */
@media (prefers-reduced-motion: reduce) {
  body.woocommerce-cart .woocommerce .wc-proceed-to-checkout a.checkout-button.button.alt:hover {
    transform: none !important;
    box-shadow: none !important;
  }
  body.woocommerce-cart .woocommerce-cart-form.processing,
  body.woocommerce-cart .cart_totals.processing { transition: none !important; }
  .jn-coupon-toggle::after { transition: none !important; }
}

/* ==========================================================================
   21. WOOCOMMERCE — CHECKOUT
   --------------------------------------------------------------------------
   ⚠️ CHECKOUT CLÁSSICO — o shortcode [woocommerce_checkout]. Mesma ressalva do
   §20: instalação nova do WooCommerce cria a página em BLOCOS
   (`wp-block-woocommerce-checkout`), que tem outra marcação e não é atingida
   por nada aqui. Conferência no INSTALL, passo 7.

   ⚠️ O CHECKOUT SE REDESENHA MUITO MAIS QUE O CARRINHO, E TROCA COISA
   DIFERENTE. Lido no `client/legacy/js/frontend/checkout.js` e no
   `WC_AJAX::update_order_review()` da 9.1.0: a resposta traz DOIS fragmentos e
   só eles são substituídos —

       '.woocommerce-checkout-review-order-table'  → a tabela inteira
       '.woocommerce-checkout-payment'             → o #payment inteiro
                                                     (métodos, termos, botão)

   O `#order_review` que os contém NÃO é trocado; `#customer_details` e os
   campos também não. E o gatilho não é só clicar: `update_checkout` dispara na
   troca de país/estado, na de método de entrega, na de método de pagamento e
   1s DEPOIS DE PARAR DE DIGITAR num campo de endereço. Ou seja: enquanto a
   cliente preenche a morada, o bloco de pagamento é substituído embaixo dela.

   Três consequências que mandam no desenho:
     1. O que precisa SOBREVIVER (região viva, alerta, linha de reserva) é
        impresso ANTES do <form>, fora dos dois fragmentos.
     2. O que precisa VOLTAR depois da troca (fileira de confiança, nota de
        arrependimento) é impresso DENTRO do fragmento de pagamento, por hook
        de servidor. Injetar por JS seria apagado no primeiro redesenho.
     3. O FOCO É DESTRUÍDO dentro do #payment. Quem clica num rádio de
        pagamento dispara o AJAX que substitui esse mesmo rádio: o foco vai
        para o <body>. Nos campos de endereço isso não acontece, porque eles
        ficam fora do fragmento. Quem devolve o foco é o JS do snippet.

   ⚠️ ESTA SEÇÃO NÃO REESCREVE O BLOCO DE TOTAIS. Ele já foi definido no §20.6
   como componente compartilhado e `.woocommerce-checkout-review-order` já está
   em cada regra de lá. O que existe aqui são as partes que o carrinho não
   tem: a tabela de ITENS, o bloco de pagamento, os termos e o CTA.

   ⚠️ NÃO existe .json para esta página, como no carrinho. Quem prova a entrega
   são os guardas de §21 no verify-tokens.mjs.
   ========================================================================== */

/* --- 21.1 Casca e grade ---------------------------------------------------
   ⚠️ A COLUNA DA DIREITA É EMBRULHADA EM PHP (`.jn-review-col`), e isso não é
   preferência: o form entrega TRÊS filhos irmãos — `#customer_details`, o
   `<h3 id="order_review_heading">` e `#order_review`. Numa grade de duas
   colunas, mandar o h3 e o resumo para a coluna 2 exigiria fixar linha
   explícita nos três (`grid-row`), e aí qualquer filho novo (o grupo de avisos
   que o Woo PREPENDA no form em caso de erro, ou o que um plugin imprima nos
   hooks de customer details) cai fora do fluxo e some para o fim da página, ou
   pior, se sobrepõe. Com o embrulho são DOIS filhos posicionados e todo o
   resto atravessa a largura inteira na ordem do DOM, que é onde o aviso de
   erro precisa estar: em cima. O embrulho abre em
   `woocommerce_checkout_before_order_review_heading` e fecha em
   `woocommerce_checkout_after_order_review` (snippets-checkout.php §3). */
body .jn-checkout-title {
  /* Mesmo prefixo `body` e mesma fonte do §20.1, pela mesma razão: sem ele
     `.jn-checkout-title` é (0,1,0) e perde para `.jn-section :is(h1,h2,h3,h4)`,
     que é (0,1,1). O h1 sairia em Fraunces 400 sem erro na tela. */
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h1);
  font-weight: 600;
  line-height: var(--jn-lh-tight);
  font-optical-sizing: auto;
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-6);
}

@media (min-width: 861px) {
  body.woocommerce-checkout .woocommerce form.checkout:has(> .jn-review-col) {
    display: grid;
    grid-template-columns: minmax(0, 1.35fr) minmax(320px, 1fr);
    column-gap: var(--jn-s-7);
    row-gap: 0;
    align-items: start;
  }
  /* Catch-all de especificidade zero, igual ao §20.1: só os dois blocos abaixo
     são colunas; avisos e qualquer coisa de plugin atravessam. */
  :where(body.woocommerce-checkout .woocommerce form.checkout:has(> .jn-review-col)) > * { grid-column: 1 / -1; }
  body.woocommerce-checkout form.checkout > #customer_details { grid-column: 1 / 2; }
  body.woocommerce-checkout form.checkout > .jn-review-col     { grid-column: 2 / 3; }
  /* ⚠️ Quem gruda é o `#order_review`, DENTRO da coluna — nunca a coluna.
     Item de grade com `position: sticky` é limitado pelo contêiner da grade
     inteiro, não pela própria linha (medido no §20.1, onde o cartão de totais
     passou por cima dos cross-sells). Aqui a `.jn-review-col` fica normal e
     esticada, e o resumo gruda dentro dela. */
  body.woocommerce-checkout #order_review { position: sticky; top: var(--jn-s-5); }
}
/* As duas colunas nativas de dentro do #customer_details (col-1 = cobrança,
   col-2 = entrega) empilham: a coluna esquerda é estreita demais para dois
   formulários lado a lado, e endereço em duas colunas é justamente onde a
   cliente erra de campo. */
body.woocommerce-checkout #customer_details.col2-set,
body.woocommerce-checkout #customer_details .col-1,
body.woocommerce-checkout #customer_details .col-2 { width: auto; float: none; }
body.woocommerce-checkout #customer_details .col-2 { margin-top: var(--jn-s-6); }
/* O Woo emite `<div class="clear">` em vários pontos; com grade/flex eles
   viram linha vazia com altura. */
body.woocommerce-checkout .woocommerce form.checkout .clear { display: none; }

/* --- 21.2 Campos ----------------------------------------------------------
   ⚠️ PRIORIDADE 1 DO REFERENCE §D: no checkout BR os campos são pílulas
   pêssego SEM BORDA, que se fundem ao fundo. O ponto travado #12 (§12) já
   resolve `input.input-text`, `textarea` e `select` — o que ele NÃO alcança
   está aqui embaixo. */
body.woocommerce-checkout .woocommerce form .form-row {
  display: block;
  margin: 0;
  padding: 0;
}
body.woocommerce-checkout .woocommerce-billing-fields__field-wrapper,
body.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper,
body.woocommerce-checkout .woocommerce-additional-fields__field-wrapper,
body.woocommerce-checkout .woocommerce-account-fields .create-account {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--jn-s-4) var(--jn-s-4);
}
/* Largura padrão é a linha inteira; só `form-row-first`/`form-row-last`
   dividem. Declarado por nós porque o float que faz isso mora no
   woocommerce-layout.css, que setups Hello + Elementor costumam desenfileirar
   (§20.3, §18.0). Se sumir, o formulário continua legível, empilhado. */
body.woocommerce-checkout .woocommerce-billing-fields__field-wrapper > .form-row,
body.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper > .form-row,
body.woocommerce-checkout .woocommerce-additional-fields__field-wrapper > .form-row { grid-column: 1 / -1; }
body.woocommerce-checkout .woocommerce-billing-fields__field-wrapper > .form-row-first,
body.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper > .form-row-first { grid-column: 1 / 2; }
body.woocommerce-checkout .woocommerce-billing-fields__field-wrapper > .form-row-last,
body.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper > .form-row-last { grid-column: 2 / 3; }
@media (max-width: 560px) {
  body.woocommerce-checkout .woocommerce-billing-fields__field-wrapper,
  body.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper,
  body.woocommerce-checkout .woocommerce-additional-fields__field-wrapper { grid-template-columns: minmax(0, 1fr); }
  body.woocommerce-checkout .woocommerce-billing-fields__field-wrapper > .form-row-first,
  body.woocommerce-checkout .woocommerce-billing-fields__field-wrapper > .form-row-last,
  body.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper > .form-row-first,
  body.woocommerce-checkout .woocommerce-shipping-fields__field-wrapper > .form-row-last { grid-column: 1 / -1; }
}
/* ⚠️ RÓTULO VISÍVEL SEMPRE, nunca substituído por placeholder. Placeholder
   some quando a pessoa começa a digitar, e é aí que ela precisa conferir em
   que campo está. O Woo já emite <label for>; o que falta é fazê-lo parecer
   rótulo. */
/* ⚠️ O `:not(.checkbox)` NÃO É REFINAMENTO. O Woo usa a MESMA classe
   `.form-row` para campo de texto e para caixa de seleção, e no segundo caso o
   `<label>` embrulha o controle e a frase inteira. Sem a exclusão, "Create an
   account?" e o aceite dos termos saíam em VERSALETE de rótulo de campo, que é
   registro de etiqueta, não de frase — visto no preview. */
body.woocommerce-checkout .woocommerce form .form-row > label:not(.checkbox) {
  display: block;
  margin: 0 0 var(--jn-s-2);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-eyebrow);
  font-weight: 700;
  letter-spacing: var(--jn-ls-eyebrow);
  text-transform: uppercase;
  color: var(--jn-ink-deep);
}
/* Rótulo de caixa de seleção: frase normal, controle e texto na mesma linha,
   alinhados pelo topo (a frase pode ter duas linhas). Vale para "Create an
   account?", para o aceite dos termos e para qualquer caixa que um plugin
   acrescente. */
body.woocommerce-checkout .woocommerce form .form-row > label.checkbox {
  display: flex;
  align-items: flex-start;
  gap: var(--jn-s-3);
  margin: 0;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  line-height: var(--jn-lh-body);
  color: var(--jn-ink-deep);
  cursor: pointer;
}
/* `.woocommerce-input-wrapper` é um <span>: sem isto o campo não ocupa a
   linha. */
body.woocommerce-checkout .woocommerce form .form-row .woocommerce-input-wrapper { display: block; }
body.woocommerce-checkout .woocommerce form .form-row input.input-text,
body.woocommerce-checkout .woocommerce form .form-row textarea,
body.woocommerce-checkout .woocommerce form .form-row select { width: 100%; }
body.woocommerce-checkout .woocommerce form .form-row textarea {
  min-height: 112px;
  padding: var(--jn-s-3) var(--jn-s-4);
  border-radius: var(--jn-r-card);
  line-height: var(--jn-lh-body);
}
/* ⚠️ PONTO TRAVADO #17 PELA QUARTA VEZ, e desta vez em texto que o axe não
   mede: ele não avalia contraste de `::placeholder` com opacidade, então a
   folha passa verde e a tela reprova. Calculado, não estimado:
       #6B4620 a .62 sobre branco = rgb(163,140,117) = 3,19:1  → REPROVA
       #6B4620 a .70 sobre branco = rgb(151,126, 99) = 3,84:1  → REPROVA
       #6B4620 a .80 sobre branco = rgb(137,107, 77) = 4,92:1  → passa
   O piso real é .765. O .8 fica acima dele e ainda deixa o texto de dica
   visivelmente mais claro que um valor digitado, que é a única função da
   opacidade aqui. E o placeholder NUNCA carrega informação exclusiva: o rótulo
   está sempre visível (§21.2), então mesmo ilegível ele não esconderia nada.
   Não baixar este número. */
body.woocommerce-checkout .woocommerce form .form-row input.input-text::placeholder,
body.woocommerce-checkout .woocommerce form .form-row textarea::placeholder {
  color: var(--jn-ink-deep);
  opacity: 0.8;
}
body.woocommerce-checkout .woocommerce form .form-row input.input-text:focus-visible,
body.woocommerce-checkout .woocommerce form .form-row textarea:focus-visible,
body.woocommerce-checkout .woocommerce form .form-row select:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 2px;
}
/* O asterisco de obrigatório e o "(optional)" — os dois são texto do Woo, e o
   `title="required"` do <abbr> é o que o leitor de tela lê. Só o tamanho e a
   cor mudam. */
body.woocommerce-checkout .woocommerce form .form-row label .required {
  color: var(--jn-terracotta);
  text-decoration: none;
  border: 0;
}
body.woocommerce-checkout .woocommerce form .form-row label .optional {
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  opacity: 1;
}
/* Descrição do campo. O Woo a emite com `aria-hidden="true"`, então ela é
   ajuda visual e nada mais — não pode carregar informação exclusiva. */
body.woocommerce-checkout .woocommerce form .form-row .description {
  display: block;
  margin-top: var(--jn-s-2);
  font-size: var(--jn-t-eyebrow);
  color: var(--jn-ink-deep);
}

/* --- 21.2b Estado inválido ------------------------------------------------
   ⚠️ O ESTADO É `.woocommerce-invalid` NA LINHA (`p.form-row`), posto pelo JS
   do Woo no blur — não é `:invalid` do navegador, que nunca casaria aqui.
   ⚠️ NUNCA SÓ POR COR (WCAG 1.4.1). O sinal visual é triplo: borda mais
   grossa, marca "!" desenhada em BORDA CSS (ponto travado #13, nada de texto
   injetado que não seria traduzível) e o rótulo em negrito. O sinal para
   leitor de tela é o `aria-invalid` que o JS do snippet põe no campo, mais a
   lista de erros que o Woo imprime no topo do form.
   ⚠️ A COR É A TERRACOTA DA MARCA, e é decisão, não descuido: o REFERENCE §B
   reprova o "vermelho ruidoso" do site BR, e a paleta não tem vermelho. Como o
   estado não depende de cor para ser percebido, usar o próprio acento mantém a
   página inteira numa família cromática só. */
body.woocommerce-checkout .woocommerce form .form-row.woocommerce-invalid input.input-text,
body.woocommerce-checkout .woocommerce form .form-row.woocommerce-invalid textarea,
body.woocommerce-checkout .woocommerce form .form-row.woocommerce-invalid select,
body.woocommerce-checkout .woocommerce form .form-row.woocommerce-invalid .select2-selection {
  border-width: 3px;
  border-color: var(--jn-terracotta);
}
body.woocommerce-checkout .woocommerce form .form-row.woocommerce-invalid > label { position: relative; }
body.woocommerce-checkout .woocommerce form .form-row.woocommerce-invalid > label::after {
  content: "";
  display: inline-block;
  width: 14px;
  height: 14px;
  margin-left: var(--jn-s-2);
  vertical-align: -2px;
  border: 2px solid var(--jn-terracotta);
  border-radius: 50%;
  /* O traço e o ponto do "!" são as duas bordas de uma caixa interna: mesma
     técnica do + e do − do stepper (§20.4). */
  background:
    linear-gradient(var(--jn-terracotta), var(--jn-terracotta)) center 2px / 2px 5px no-repeat,
    linear-gradient(var(--jn-terracotta), var(--jn-terracotta)) center 8px / 2px 2px no-repeat;
}
body.woocommerce-checkout .woocommerce form .form-row.woocommerce-validated input.input-text,
body.woocommerce-checkout .woocommerce form .form-row.woocommerce-validated select { border-color: var(--jn-ink-deep); }
/* ⚠️ A CAIXA DE TERMOS PRECISA DE TRATAMENTO PRÓPRIO, e ela é o campo que mais
   barra pedido no checkout inteiro. Um checkbox não tem borda para engrossar
   (o `accent-color` desenha o controle), então o sinal aqui é o anel — e ele é
   espessura, não cor sozinha. O `aria-invalid` vem do JS do snippet. */
body.woocommerce-checkout .woocommerce form .form-row.woocommerce-invalid input[type="checkbox"] {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 2px;
}
/* O rótulo de checkbox é `display: flex`, então a marca "!" entra como ITEM DE
   FLEX no fim da linha em vez de ficar colada ao texto. Centralizada, ela lê
   como um selo ao lado da frase; sem isto, encosta no topo de um texto de duas
   linhas. */
body.woocommerce-checkout .woocommerce form .form-row.woocommerce-invalid > label.checkbox::after { align-self: center; }

/* --- 21.2c O campo de país, que quase nunca é um <select> -----------------
   ⚠️ ARMADILHA DIRETA DA PRIORIDADE 1. O Woo transforma `select.country_select`
   e `select.state_select` em selectWoo (o fork do select2 que ele empacota): o
   <select> real vira `.select2-hidden-accessible` e o que a cliente VÊ é uma
   div. Nossa borda do ponto travado #12 fica no elemento escondido e o campo
   visível continua sem contorno — exatamente o defeito que o REFERENCE §D
   manda corrigir, agora invisível para quem só olhou a folha.
   O site BR já carrega select2 (conferido no HTML da home: 21 regras de
   `.select2-*` no CSS da página), então este é o caminho real, não hipótese.
   As regras abaixo cobrem os DOIS casos: se um dia o selectWoo não carregar, o
   <select> nativo do §12 aparece já estilizado.
   ⚠️ O MENU ABERTO É FILHO DO <body>, não do form. Por isso as regras do menu
   NÃO podem ser escritas como descendentes de `.woocommerce` — só o prefixo
   `body.woocommerce-checkout` alcança. */
body.woocommerce-checkout .select2-container--default .select2-selection--single {
  height: 48px;
  padding: 0 var(--jn-s-4);
  display: flex;
  align-items: center;
  background: var(--jn-white);
  border: 2px solid var(--jn-ink-deep);
  border-radius: var(--jn-r-pill);
}
body.woocommerce-checkout .select2-container--default .select2-selection--single .select2-selection__rendered {
  padding: 0;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  line-height: 1.4;
  color: var(--jn-ink-deep);
}
body.woocommerce-checkout .select2-container--default .select2-selection--single .select2-selection__placeholder { color: var(--jn-ink-deep); }
body.woocommerce-checkout .select2-container--default .select2-selection--single .select2-selection__arrow { height: 46px; right: var(--jn-s-3); }
/* Seta em borda CSS (ponto travado #13): o `b` que o select2 usa é um
   triângulo por border-color, e ele fica com a cor default se não for trocado. */
body.woocommerce-checkout .select2-container--default .select2-selection--single .select2-selection__arrow b {
  border-color: var(--jn-ink-deep) transparent transparent transparent;
}
body.woocommerce-checkout .select2-container--default.select2-container--open .select2-selection--single .select2-selection__arrow b {
  border-color: transparent transparent var(--jn-ink-deep) transparent;
}
body.woocommerce-checkout .select2-container--default.select2-container--focus .select2-selection--single,
body.woocommerce-checkout .select2-container--default.select2-container--open .select2-selection--single {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 2px;
  border-color: var(--jn-ink-deep);
}
body.woocommerce-checkout .select2-dropdown {
  background: var(--jn-white);
  border: 2px solid var(--jn-ink-deep);
  border-radius: var(--jn-r-card);
  overflow: hidden;
}
body.woocommerce-checkout .select2-container--default .select2-search--dropdown .select2-search__field {
  min-height: 44px;
  padding: 0 var(--jn-s-3);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  color: var(--jn-ink-deep);
  border: 2px solid var(--jn-line-strong);
  border-radius: var(--jn-r-card);
}
body.woocommerce-checkout .select2-container--default .select2-results__option {
  padding: var(--jn-s-3) var(--jn-s-4);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  color: var(--jn-ink-deep);
}
/* Realce do item sob o teclado/ponteiro: branco sobre terracota = 6,45:1, o
   mesmo par do CTA. O default do select2 é um azul que não é da marca. */
body.woocommerce-checkout .select2-container--default .select2-results__option--highlighted[aria-selected],
body.woocommerce-checkout .select2-container--default .select2-results__option--highlighted[data-selected] {
  background: var(--jn-terracotta);
  color: var(--jn-on-action);
}

/* --- 21.3 Cabeçalhos das seções do formulário ----------------------------- */
body.woocommerce-checkout .woocommerce-billing-fields > h3,
body.woocommerce-checkout .woocommerce-shipping-fields > h3,
body.woocommerce-checkout .woocommerce-additional-fields > h3,
body.woocommerce-checkout #order_review_heading {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h3);
  font-weight: 600;
  line-height: var(--jn-lh-head);
  font-optical-sizing: auto;
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-4);
}
/* ⚠️ O <h3> da entrega É o rótulo do checkbox: o Woo emite
   `<h3 id="ship-to-different-address"><label…><input type="checkbox">…</label></h3>`.
   Estilizar como título e esquecer que tem um controle dentro é como o campo
   vira inalcançável. Aqui ele vira uma linha com caixa + texto. */
body.woocommerce-checkout #ship-to-different-address {
  display: block;
  margin: 0 0 var(--jn-s-4);
}
body.woocommerce-checkout #ship-to-different-address label {
  display: inline-flex;
  align-items: center;
  gap: var(--jn-s-3);
  cursor: pointer;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  color: var(--jn-ink-deep);
}
/* ⚠️ NENHUMA REGRA DE `display` EM `.shipping_address`. O checkout.js abre e
   fecha esse bloco com slideToggle, que escreve display e height INLINE —
   estilo inline vence folha, e uma regra nossa só faria o painel piscar ao
   marcar a caixa. Terceira vez que a mesma armadilha aparece neste arquivo:
   calculadora de frete (§20.6), cupom (§21.5) e agora aqui. */
body.woocommerce-checkout .shipping_address { padding-top: var(--jn-s-2); }
/* Título do bloco de pagamento (impresso pelo snippet). Fica no mesmo degrau
   dos outros cabeçalhos de seção do formulário. */
body .jn-pay-heading {
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h3);
  font-weight: 600;
  line-height: var(--jn-lh-head);
  font-optical-sizing: auto;
  color: var(--jn-heading);
  margin: var(--jn-s-6) 0 0;
}
/* Caixas de seleção do checkout: entrega, criar conta, termos. Alvo de 24px
   com área clicável de 44px pela linha inteira do rótulo. */
body.woocommerce-checkout .woocommerce form input[type="checkbox"] {
  flex: none;
  width: 24px;
  height: 24px;
  margin: 0;
  accent-color: var(--jn-terracotta);
}
body.woocommerce-checkout .woocommerce form input[type="checkbox"]:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 2px;
}

/* --- 21.4 Resumo do pedido: a parte que o carrinho NÃO tem ----------------
   ⚠️ POR QUE ESTE BLOCO EXISTE. O §20.6 é o componente compartilhado e já
   alcança `.woocommerce-checkout-review-order`. Só que no carrinho aquele
   cartão contém APENAS a tabela de totais, e no checkout ele contém também uma
   tabela de ITENS (thead + tr.cart_item + tfoot) e o bloco de pagamento
   inteiro. As regras genéricas de célula do §20.6 — `text-align: right`,
   `width: 45%` no th, corpo pequeno — passam a cair no nome do produto, que
   ficaria alinhado à direita.
   ⚠️ E A CORREÇÃO É ADITIVA DE PROPÓSITO: dá para consertar estreitando os
   seletores do §20.6 para as linhas de total, mas o carrinho já foi auditado e
   entregue, e a tabela de totais dele NÃO tem <tfoot> (o cart-totals.php
   emite os <tr> soltos) — qualquer estreitamento por `tfoot` o quebraria
   inteiro, e um estreitamento por lista de classes de linha some assim que um
   plugin adiciona uma linha nova. Regra da casa: não se mexe no que já está
   auditado quando o mesmo resultado cabe num override.

   ⚠️ E É POR ISSO QUE TODO SELETOR AQUI COMEÇA COM
   `body.woocommerce-checkout table.…`, que parece exagero e não é. As regras
   do §20.6 são `.woocommerce-checkout-review-order table.shop_table th`, ou
   seja (0,2,2). Um override escrito como `.woocommerce-checkout-review-order-table th`
   é (0,1,1) e PERDE — o que significa que ele não faz absolutamente nada e
   nada na tela avisa. Foi medido: com a versão curta, a coluna do nome do
   produto e a do preço ficaram com 212px cada, porque quem mandava ainda era o
   `width: 45%` do §20.6. Mesma classe de engano do `.jn-ship-note td` (§20.6),
   do `[hidden]` do cupom (§20.5) e dos dois h1. NÃO encurtar estes seletores. */
body.woocommerce-checkout table.woocommerce-checkout-review-order-table thead th {
  padding-bottom: var(--jn-s-3);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-eyebrow);
  font-weight: 700;
  letter-spacing: var(--jn-ls-eyebrow);
  text-transform: uppercase;
  color: var(--jn-ink-deep);
  border-bottom: 1px solid var(--jn-line-strong);
}
/* ⚠️ SEGUNDA HERANÇA DO §20.6 QUE PRECISA DE OVERRIDE. Lá o <th> vale 45% da
   largura, e no carrinho isso está certo: a coluna 1 carrega só rótulos curtos
   ("Subtotal", "Total"). Aqui a coluna 1 carrega o NOME DO PRODUTO e a 2 só um
   preço de cinco caracteres. Medido neste preview: 212px para cada uma, com o
   nome quebrando em três linhas ao lado de um "€5.80" nadando em espaço vazio.
   O 58% devolve a proporção sem tocar no §20.6. */
body.woocommerce-checkout table.woocommerce-checkout-review-order-table th { width: 58%; }
body.woocommerce-checkout table.woocommerce-checkout-review-order-table thead th.product-name { width: auto; text-align: left; }
body.woocommerce-checkout table.woocommerce-checkout-review-order-table thead th.product-total { text-align: right; }
body.woocommerce-checkout table.woocommerce-checkout-review-order-table tbody td.product-name {
  width: auto;
  text-align: left;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  color: var(--jn-ink-deep);
}
body.woocommerce-checkout table.woocommerce-checkout-review-order-table tbody td.product-total {
  text-align: right;
  white-space: nowrap;
  font-family: var(--jn-font-head);
  font-weight: 600;
  color: var(--jn-terracotta);
}
/* A quantidade vem grudada no nome, dentro de <strong class="product-quantity">
   com um &times;. Vira um contador discreto em vez de negrito no meio da
   frase. */
body.woocommerce-checkout table.woocommerce-checkout-review-order-table .product-quantity {
  display: inline-block;
  margin-left: var(--jn-s-2);
  font-weight: 700;
  color: var(--jn-heading);
  white-space: nowrap;
}
/* Variações do item (`dl.variation`). */
body.woocommerce-checkout table.woocommerce-checkout-review-order-table dl.variation {
  margin: var(--jn-s-1) 0 0;
  font-size: var(--jn-t-eyebrow);
  color: var(--jn-ink-deep);
}
/* ⚠️ O `<p>` DE DENTRO DO `<dd>` TAMBÉM PRECISA ENTRAR. O
   `wc_get_formatted_cart_item_data()` embrulha o valor da variação num
   parágrafo, que é bloco: com `display: inline` só no dt/dd, o resultado na
   tela foi "Finish:" numa linha e "Matte" na seguinte. Visto no preview. */
body.woocommerce-checkout table.woocommerce-checkout-review-order-table dl.variation dt,
body.woocommerce-checkout table.woocommerce-checkout-review-order-table dl.variation dd,
body.woocommerce-checkout table.woocommerce-checkout-review-order-table dl.variation dd p { display: inline; margin: 0; }
body.woocommerce-checkout table.woocommerce-checkout-review-order-table dl.variation dt { margin-right: var(--jn-s-1); }
/* A primeira linha do <tfoot> abre o bloco de contas: separador mais firme
   para o olho saber onde os itens acabaram. */
body.woocommerce-checkout table.woocommerce-checkout-review-order-table tfoot tr:first-child th,
body.woocommerce-checkout table.woocommerce-checkout-review-order-table tfoot tr:first-child td { border-top: 1px solid var(--jn-line-strong); }
/* ⚠️ O NÚMERO DE LINHAS DE IMPOSTO É VARIÁVEL. Com exibição "itemizada" o Woo
   emite uma `tr.tax-rate` POR alíquota (uma loja UE com OSS pode ter várias);
   com exibição única, uma `tr.tax-total`; com preços já com imposto embutido,
   NENHUMA, e o valor aparece como `.includes_tax` dentro do total. Por isso
   não há nada aqui que dependa de existir exatamente uma linha. */
body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.tax-rate th,
body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.tax-total th,
body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.jn-vat-note th { font-weight: 400; }
body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.jn-vat-note th,
body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.jn-vat-note td {
  font-size: var(--jn-t-eyebrow);
  color: var(--jn-ink-deep);
}
/* Frete dentro do resumo: a lista de métodos com rádio. Mesmo tratamento do
   §20.6 para `ul#shipping_method`, aqui só o alinhamento à esquerda dentro de
   uma célula que o §20.6 alinhou à direita. */
body.woocommerce-checkout table.woocommerce-checkout-review-order-table td[data-title],
body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.shipping td { text-align: right; }
body.woocommerce-checkout table.woocommerce-checkout-review-order-table ul#shipping_method {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: var(--jn-s-2);
  text-align: left;
}
body.woocommerce-checkout table.woocommerce-checkout-review-order-table ul#shipping_method li { margin: 0; display: flex; align-items: center; gap: var(--jn-s-2); }
body.woocommerce-checkout table.woocommerce-checkout-review-order-table ul#shipping_method label { font-size: var(--jn-t-small); color: var(--jn-ink-deep); }
body.woocommerce-checkout table.woocommerce-checkout-review-order-table ul#shipping_method input[type="radio"] { accent-color: var(--jn-terracotta); width: 20px; height: 20px; }
/* ⚠️ NO CELULAR A LINHA DE FRETE EMPILHA, e isso não é preferência: medido a
   430px, a coluna 2 fica com 129px e cada método quebrava em duas linhas
   ("Standard:" / "€4.95"), com o rádio ao lado de um texto de duas alturas.
   O rótulo sobe e a lista ocupa a largura inteira.
   A `tr` também entra na regra pelo mesmo motivo do §20.3: sem ela, as duas
   células viram blocos dentro de uma linha de tabela e o navegador as embrulha
   numa célula anônima com largura de conteúdo. */
@media (max-width: 560px) {
  body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.shipping,
  body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.shipping > th,
  body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.shipping > td {
    display: block;
    width: auto;
    text-align: left;
  }
  body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.shipping > th { padding-bottom: var(--jn-s-2); border-bottom: 0; }
  /* ⚠️ O separador SAI daqui e vai para a linha seguinte. Uma célula em
     `display: block` deixa de participar do modelo de tabela e passa a valer a
     largura da célula anônima que a embrulha (medido: 180px de 340), então a
     borda de baixo ficava um traço curto no meio do cartão. A linha de baixo é
     uma `tr` normal e desenha o traço inteiro. Vale para qualquer linha que
     venha depois do frete, não só o total: taxa, imposto e tarifa entram no
     meio dependendo da configuração da loja. */
  body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.shipping > td { padding-top: 0; border-bottom: 0; }
  body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.shipping + tr > th,
  body.woocommerce-checkout table.woocommerce-checkout-review-order-table tr.shipping + tr > td { border-top: 1px solid var(--jn-line); }
}

/* --- 21.5 Cupom discreto --------------------------------------------------
   O checkout já entrega o cupom colapsado: um aviso com link `.showcoupon` e o
   `form.checkout_coupon` escondido. O trabalho aqui é tirar a cara de "caixa
   de aviso do sistema", que é o que chama atenção para o campo e manda a
   cliente para o Google atrás de código (mesma razão do §20.5).
   ⚠️ NENHUMA REGRA DE `display` EM `.checkout_coupon`. O template já nasce com
   `style="display:none"` INLINE e o checkout.js abre com `slideToggle`, que
   reescreve display e height inline. Estilo inline vence folha, e uma regra
   nossa aqui só faria o painel piscar. Mesma armadilha da calculadora de frete
   (§20.6) e do flexslider (§19.3). */
body.woocommerce-checkout .woocommerce-form-coupon-toggle { margin: 0 0 var(--jn-s-5); }
body.woocommerce-checkout .woocommerce-form-coupon-toggle .woocommerce-info {
  margin: 0;
  padding: var(--jn-s-3) 0;
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--jn-line);
  color: var(--jn-ink-deep);
  font-size: var(--jn-t-small);
}
body.woocommerce-checkout .woocommerce-form-coupon-toggle .woocommerce-info::before { content: none; }
body.woocommerce-checkout a.showcoupon {
  font-weight: 700;
  color: var(--jn-terracotta);
  text-decoration: underline;
  text-underline-offset: 3px;
}
body.woocommerce-checkout a.showcoupon:focus-visible { outline: 3px solid var(--jn-terracotta); outline-offset: 3px; }
body.woocommerce-checkout form.checkout_coupon {
  margin: 0 0 var(--jn-s-5);
  padding: var(--jn-s-5);
  background: var(--jn-white);
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-card);
}
body.woocommerce-checkout form.checkout_coupon .form-row { display: inline-block; }
body.woocommerce-checkout form.checkout_coupon p:first-child { margin: 0 0 var(--jn-s-3); font-size: var(--jn-t-small); }
body.woocommerce-checkout form.checkout_coupon input#coupon_code {
  min-width: 12rem;
  min-height: 48px;
  padding: 0 var(--jn-s-4);
  background: var(--jn-white);
  border: 2px solid var(--jn-ink-deep);
  border-radius: var(--jn-r-pill);
  margin-right: var(--jn-s-2);
}
/* Cadeia longa pela mesma razão do §20.5: `.woocommerce .button` do Woo vence
   seletor curto e devolve o botão ao cinza/verde default. */
body.woocommerce-checkout .woocommerce form.checkout_coupon button.button {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 48px;
  padding: 0 var(--jn-s-5);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--jn-ink-deep);
  background: transparent;
  border: 2px solid var(--jn-line-strong);
  border-radius: var(--jn-r-pill);
  cursor: pointer;
  transition: border-color var(--jn-d-fast) var(--jn-ease-soft),
              color var(--jn-d-fast) var(--jn-ease-soft);
}
body.woocommerce-checkout .woocommerce form.checkout_coupon button.button:hover {
  color: var(--jn-terracotta);
  border-color: var(--jn-terracotta);
}
body.woocommerce-checkout .woocommerce form.checkout_coupon button.button:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 3px;
}

/* --- 21.6 Pagamento -------------------------------------------------------
   ⚠️ O BLOCO INTEIRO É SUBSTITUÍDO A CADA `update_checkout` (fragmento
   `.woocommerce-checkout-payment`). Nada aqui pode depender de estado escrito
   por JS que não seja reaplicado no `updated_checkout`.
   ⚠️ OS ÍCONES DAS BANDEIRAS SÃO DO GATEWAY, não nossos. O Mollie/Stripe
   imprime `$gateway->get_icon()` dentro do <label>. Desenhar imitações de
   Visa/iDEAL/PayPal seria marca de terceiro redesenhada por nós e, pior,
   mentiria sobre o que a loja realmente aceita — o que aparece depende do que
   o operador ativar. Aqui só existe o encaixe. */
body.woocommerce-checkout #payment { background: transparent; border-radius: 0; }
body.woocommerce-checkout #payment ul.payment_methods {
  list-style: none;
  margin: var(--jn-s-5) 0 0;
  padding: var(--jn-s-5) 0 0;
  border: 0;
  border-top: 1px solid var(--jn-line);
  display: grid;
  gap: var(--jn-s-2);
}
/* ⚠️ O RÁDIO É IRMÃO DO RÓTULO, NÃO FILHO DELE. O `payment-method.php` emite
   `<li><input type="radio"><label for="…">…</label><div class="payment_box">`,
   e é por isso que pôr o flex no <label> não junta os dois: medido no preview,
   o rádio ficou numa linha e "Card" na de baixo. Quem precisa ser a grade é o
   <li>, com a caixa de detalhes atravessando as duas colunas. */
body.woocommerce-checkout #payment ul.payment_methods li.wc_payment_method {
  margin: 0;
  padding: 0;
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-card);
  background: var(--jn-white);
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: center;
}
body.woocommerce-checkout #payment ul.payment_methods li.wc_payment_method > input[type="radio"] { margin-left: var(--jn-s-4); }
body.woocommerce-checkout #payment ul.payment_methods li.wc_payment_method > div.payment_box { grid-column: 1 / -1; }
/* O rádio nativo fica visível de propósito: é o controle, e escondê-lo atrás
   de um desenho custa foco e estado para quem usa teclado. */
body.woocommerce-checkout #payment ul.payment_methods li label {
  display: flex;
  align-items: center;
  gap: var(--jn-s-3);
  min-height: 52px;
  margin: 0;
  /* Sem recuo à esquerda: quem dá o respiro do rádio é a margem dele, e o
     rótulo começa logo depois. */
  padding: var(--jn-s-3) var(--jn-s-4) var(--jn-s-3) var(--jn-s-3);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  font-weight: 600;
  color: var(--jn-ink-deep);
  cursor: pointer;
}
body.woocommerce-checkout #payment ul.payment_methods li input[type="radio"] {
  flex: none;
  width: 20px;
  height: 20px;
  margin: 0;
  accent-color: var(--jn-terracotta);
}
body.woocommerce-checkout #payment ul.payment_methods li input[type="radio"]:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 3px;
}
/* Método escolhido: o `:has()` é enfeite útil, e a ausência dele não tira
   nada essencial — o rádio marcado continua sendo o estado. */
body.woocommerce-checkout #payment ul.payment_methods li.wc_payment_method:has(input:checked) {
  border-color: var(--jn-ink-deep);
  box-shadow: inset 0 0 0 1px var(--jn-ink-deep);
}
/* Logo do gateway: altura travada para uma fileira de bandeiras não desmontar
   a linha. */
body.woocommerce-checkout #payment ul.payment_methods li label img {
  max-height: 24px;
  width: auto;
  margin-left: auto;
  vertical-align: middle;
}
/* ⚠️ SEM REGRA DE `display` NO `.payment_box`. O template nasce com
   `style="display:none"` inline nos métodos não escolhidos e o checkout.js usa
   slideDown/slideUp. Mesma armadilha do cupom. */
body.woocommerce-checkout #payment div.payment_box {
  margin: 0;
  padding: var(--jn-s-4);
  background: var(--jn-shade);
  border-radius: 0 0 var(--jn-r-card) var(--jn-r-card);
  font-size: var(--jn-t-small);
  color: var(--jn-ink-deep);
}
/* O Woo desenha um "bico" com ::before; num cartão de canto reto ele fica
   sobrando. */
body.woocommerce-checkout #payment div.payment_box::before { content: none; }
body.woocommerce-checkout #payment div.payment_box p { margin: 0; }

/* --- 21.7 Termos, privacidade e direito de arrependimento -----------------
   ⚠️ ESTES BLOCOS SÓ APARECEM SE AS PÁGINAS ESTIVEREM CONFIGURADAS. O
   `wc_terms_and_conditions_checkbox_enabled()` exige uma página de Termos
   escolhida em WooCommerce → Configurações → Avançado, e o texto de
   privacidade exige a página de Privacidade do WordPress. Sem elas o CSS aqui
   está perfeito e NADA aparece na tela — e a loja fica sem o aceite que a UE
   exige. Está no INSTALL como portão do operador. */
body.woocommerce-checkout .woocommerce-terms-and-conditions-wrapper {
  margin: var(--jn-s-5) 0 0;
  font-size: var(--jn-t-small);
  color: var(--jn-ink-deep);
}
body.woocommerce-checkout .woocommerce-privacy-policy-text p,
body.woocommerce-checkout .woocommerce-terms-and-conditions-wrapper > p { margin: 0 0 var(--jn-s-3); line-height: var(--jn-lh-body); }
body.woocommerce-checkout .woocommerce-terms-and-conditions-wrapper a {
  color: var(--jn-terracotta);
  text-decoration: underline;
  text-underline-offset: 3px;
}
body.woocommerce-checkout .woocommerce-terms-and-conditions-wrapper a:focus-visible { outline: 3px solid var(--jn-terracotta); outline-offset: 2px; }
body.woocommerce-checkout .woocommerce-terms-and-conditions-wrapper .form-row {
  margin: 0;
  padding: var(--jn-s-3) 0 0;
}
body.woocommerce-checkout .woocommerce-terms-and-conditions-wrapper label.checkbox {
  display: flex;
  align-items: flex-start;
  gap: var(--jn-s-3);
  margin: 0;
  font-size: var(--jn-t-small);
  font-weight: 400;
  letter-spacing: 0;
  text-transform: none;
  line-height: var(--jn-lh-body);
  cursor: pointer;
}
/* Caixa de termos não centraliza com o texto de duas linhas: alinha pela
   primeira. */
body.woocommerce-checkout .woocommerce-terms-and-conditions-wrapper label.checkbox input[type="checkbox"] { margin-top: 2px; }
/* O texto do direito de arrependimento (14 dias) é impresso pelo snippet logo
   abaixo do CTA. É informação de direito, não selo de confiança: fica em texto
   corrido, sem ícone, para não virar decoração. */
.jn-withdrawal {
  margin: var(--jn-s-3) 0 0;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-eyebrow);
  line-height: var(--jn-lh-body);
  color: var(--jn-ink-deep);
  text-align: center;
}

/* --- 21.8 "Place order" ---------------------------------------------------
   ⚠️ PONTO TRAVADO #20 — CADEIA LONGA OBRIGATÓRIA.
   O botão nasce `<button id="place_order" class="button alt">`. O ponto
   travado #15 está escopado em `.single_add_to_cart_button` e o #19 em
   `.wc-proceed-to-checkout a.checkout-button` — nenhum dos dois alcança este,
   e o #19 nem poderia ser alargado, porque lá é um <a> e aqui é um <button>.
   Sem a cadeia abaixo o último botão do funil volta ao verde/roxo default do
   Elementor. NÃO encurtar. */
body.woocommerce-checkout .woocommerce #payment .form-row.place-order {
  margin: var(--jn-s-5) 0 0;
  padding: 0;
}
body.woocommerce-checkout .woocommerce #payment button#place_order.button.alt {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  min-height: 56px;
  margin: 0;
  padding: 0 var(--jn-s-5);
  font-family: var(--jn-font-body);
  font-size: 0.9375rem;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--jn-on-action);              /* branco sobre terracota = 6,45:1 */
  background-color: var(--jn-terracotta);
  border: 2px solid var(--jn-terracotta);
  border-radius: var(--jn-r-pill);
  cursor: pointer;
  transition: background-color var(--jn-d-base) var(--jn-ease-soft),
              border-color var(--jn-d-base) var(--jn-ease-soft),
              transform var(--jn-d-fast) var(--jn-ease-soft),
              box-shadow var(--jn-d-base) var(--jn-ease-soft);
}
body.woocommerce-checkout .woocommerce #payment button#place_order.button.alt:hover {
  background-color: var(--jn-ink-deep);
  border-color: var(--jn-ink-deep);
  transform: translateY(-2px);
  box-shadow: 0 6px 18px var(--jn-shadow-lift);
}
body.woocommerce-checkout .woocommerce #payment button#place_order.button.alt:active { transform: translateY(0); box-shadow: none; }
body.woocommerce-checkout .woocommerce #payment button#place_order.button.alt:focus-visible {
  outline: 3px solid var(--jn-ink-deep);
  outline-offset: 3px;
}
/* O botão do <noscript> ("Update totals") existe para quem está sem JS: fica
   visível e secundário, nunca escondido. */
body.woocommerce-checkout .woocommerce #payment .place-order noscript button.button {
  min-height: 48px;
  margin: var(--jn-s-3) 0;
  padding: 0 var(--jn-s-5);
  color: var(--jn-ink-deep);
  background: transparent;
  border: 2px solid var(--jn-line-strong);
  border-radius: var(--jn-r-pill);
}
/* A fileira de confiança REUSA o componente do §19.8 / §20.7. */
body.woocommerce-checkout #payment .jn-trust {
  margin: var(--jn-s-4) 0 0;
  padding: var(--jn-s-4);
  grid-template-columns: minmax(0, 1fr);
}

/* --- 21.9 Barra fixa do celular ------------------------------------------
   ⚠️ O PADRÃO DO CARRINHO NÃO SERVE AQUI, E COPIÁ-LO SERIA ERRADO. Lá a barra
   era `aria-hidden` com `tabindex="-1"` porque duplicava um LINK que já existia
   na página: atalho de ponteiro, nada mais. Aqui o controle final é um
   <button type="submit"> dentro do form. Duplicá-lo criaria um segundo botão
   capaz de enviar o pedido (risco de envio duplo e de brigar com o JS dos
   gateways, que se prendem ao id `place_order`), e fingir que ele envia sem
   enviar seria mentir no rótulo.
   Então esta barra faz outra coisa, e diz o que faz: mostra o total e leva ao
   bloco de pagamento. É um controle DE VERDADE — focável, com nome próprio,
   último na ordem do DOM. Ela some quando o botão real está na tela.

   A casca REUSA `.jn-buybar` do §19.14 inteira — inclusive o `.jn-buybar__btn`,
   que já foi escrito sem amarrar a tag e portanto serve tanto ao <a> do
   carrinho quanto ao <button> daqui. O que falta é só o estado de foco, que
   no §19.14 vinha do fato de o controle não ser focável.

   ⚠️ O PREFIXO `body.woocommerce-checkout` NESTAS DUAS LINHAS NÃO É ZELO
   EXCESSIVO. A barra do PRODUTO também usa `<button class="jn-buybar__btn">`
   (snippets-product.php), então uma regra solta em `button.jn-buybar__btn`
   vazaria para uma página já auditada e mudaria o hover dela sem que nenhuma
   ferramenta visse: hover não é estado que o axe meça. */
body.woocommerce-checkout button.jn-buybar__btn:hover { background: var(--jn-ink-deep); border-color: var(--jn-ink-deep); }
body.woocommerce-checkout button.jn-buybar__btn:focus-visible { outline: 3px solid var(--jn-ink-deep); outline-offset: 3px; }

/* ⚠️ FOCO PRESO NUMA BARRA QUE NÃO ESTÁ NA TELA. Também consequência de ser um
   controle de verdade: o §19.14 esconde a barra com `transform: translateY(110%)`,
   e transform NÃO tira nada da ordem de tabulação. Num elemento `fixed` isso é
   pior ainda, porque o navegador não tem como rolá-lo até a vista. Resultado: a
   ≤860px, quem navega por teclado tabula para "Go to payment" e o anel de foco
   fica fora da tela (WCAG 2.4.7). No carrinho a pergunta nem existia — lá o
   controle é `tabindex="-1"`.
   O axe não avalia elementos escondidos por transform, então os dois testes
   passaram com o defeito presente.
   `visibility: hidden` tira da ordem de tabulação; o atraso de saída mantém o
   deslizar (sem ele a barra sumiria em corte seco em vez de descer). */
body.woocommerce-checkout .jn-buybar {
  visibility: hidden;
  transition: transform var(--jn-d-base) var(--jn-ease), visibility 0s var(--jn-d-base);
}
body.woocommerce-checkout .jn-buybar.is-visible {
  visibility: visible;
  transition: transform var(--jn-d-base) var(--jn-ease), visibility 0s 0s;
}

/* --- 21.10 Avisos, região viva e estado de atualização --------------------
   ⚠️ ESCOPADO EM `body.woocommerce-checkout` DE PROPÓSITO. Os avisos do Woo
   (`.woocommerce-error`, `.woocommerce-info`, `.woocommerce-message`) aparecem
   em várias páginas, e o carrinho já foi auditado com eles no estado default.
   Generalizar esta skin é uma mudança separada, que pede nova auditoria da
   página do carrinho. Aqui ela fica onde foi medida. */
body.woocommerce-checkout .woocommerce-error,
body.woocommerce-checkout .woocommerce-info,
body.woocommerce-checkout .woocommerce-message {
  margin: 0 0 var(--jn-s-5);
  padding: var(--jn-s-4) var(--jn-s-5);
  list-style: none;
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  line-height: var(--jn-lh-body);
  color: var(--jn-ink-deep);
  background: var(--jn-white);
  border: 1px solid var(--jn-line);
  border-left: 4px solid var(--jn-line-strong);
  border-radius: var(--jn-r-card);
}
body.woocommerce-checkout .woocommerce-error { border-left-color: var(--jn-terracotta); }
body.woocommerce-checkout .woocommerce-error li { margin: 0 0 var(--jn-s-2); }
body.woocommerce-checkout .woocommerce-error li:last-child { margin-bottom: 0; }
body.woocommerce-checkout .woocommerce-NoticeGroup { display: block; margin: 0 0 var(--jn-s-5); }

/* Enquanto o AJAX corre o Woo já joga um véu branco a 0.6 sobre a tabela e o
   pagamento (`block()`), e no envio põe `.processing` no form. O esmaecimento
   daqui SOMA com aquele — 0.75 pelo mesmo motivo do §20.12. */
body.woocommerce-checkout form.checkout.processing {
  opacity: .75;
  pointer-events: none;
  transition: opacity var(--jn-d-base) var(--jn-ease-soft);
}
/* ⚠️ NÃO remover achando que são divs vazias. São DUAS de propósito e não uma:
   `role="status"` (polite) anuncia o novo total sem interromper quem está
   digitando, e `role="alert"` (assertive) é para erro de pedido, que precisa
   cortar a fala. Um nó só não faz os dois — trocar `aria-live` no mesmo nó em
   tempo de execução é justamente o que não anuncia de forma confiável.
   As duas são impressas ANTES do <form>, fora dos dois fragmentos que o AJAX
   substitui: dentro, o nó seria trocado e o anúncio nunca sairia. */
.jn-checkout-live,
.jn-checkout-alert {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* --- 21.11 Reduced motion -------------------------------------------------
   Mesma disciplina do §20.13: desliga o que DESLOCA (o levantar do CTA e o
   esmaecer do AJAX) e mantém as transições de cor, que não movem nada. */
@media (prefers-reduced-motion: reduce) {
  body.woocommerce-checkout .woocommerce #payment button#place_order.button.alt:hover {
    transform: none !important;
    box-shadow: none !important;
  }
  body.woocommerce-checkout form.checkout.processing { transition: none !important; }
}

/* ==========================================================================
   22. CABEÇALHO (Theme Builder) — `header-eu.json`
   --------------------------------------------------------------------------
   ⚠️ PONTO TRAVADO #25. Leia os três parágrafos antes de mexer.

   (1) A ÂNCORA É `.elementor-location-header`, e ela é EMITIDA PELO ELEMENTOR,
   não por nós. É a classe que o Theme Builder põe no root do documento de
   cabeçalho. Medida nos dois sites da marca. NÃO ancorar na TAG `<header>`:
   o Elementor emite `<div>` nas versões mais antigas e `<header>` nas novas,
   e um seletor de tag escolheria o lado errado sozinho. É a mesma regra de
   "nunca ancorar no nome do widget" do §18.12.

   (2) O CONTÊINER DE TOPO NÃO TEM `_element_id`, E ISSO É OBRIGATÓRIO. A
   âncora de seção do §3 é `.e-con.e-parent[id]`, "contêiner de topo COM id",
   e ela não é escopada por página. Um id aqui herdaria de uma vez os 112px
   de padding de seção, o `display: block` e o reset de padding nos filhos.
   Era a armadilha anunciada no §0; esta é a saída.

   ⚠️ REV.05 CORRIGE UMA PREMISSA ERRADA DESTE PARÁGRAFO. Até aqui estava
   escrito que os ids internos (`#jn-hdr-bar`, `#jn-hdr-actions`) estavam a
   salvo "porque o Elementor marca contêiner aninhado como `.e-child` e nunca
   `.e-parent`". FALSO, e medido no site ao vivo: o cabeçalho renderiza
   `.e-child` porque foi mexido no editor, mas o RODAPÉ, importado direto do
   JSON, renderiza `.e-parent` em TODOS os níveis, inclusive nos aninhados.
   Quem decide é o campo `isInner` do payload, que o nosso JSON traz `false`
   em todo contêiner. Ou seja: `.e-parent` é o que uma importação limpa
   produz, e o `.e-child` do cabeçalho é que é o caso excepcional. Não basta
   tirar o id do contêiner de TOPO; qualquer id de contêiner dentro de um
   documento do Theme Builder pode casar a âncora de seção. A saída é a
   guarda do 22.0a, que neutraliza o §3 aqui dentro por PESO. Medido, com o
   cabeçalho forçado a `.e-parent`: com a guarda 217px, sem ela 595px.
   Ver ponto travado #27.

   (3) O PREÇO DISSO é que o cabeçalho pode ou não ser alcançado pela base do
   §2 e pelo anel de foco do §6, que são escopados em `.e-parent[id]`: depende
   de como o template foi construído, que é justamente o que não se controla
   daqui. Por isso fonte, cor, medida e `:focus-visible` são REDECLARADOS
   aqui, INCONDICIONALMENTE. Se alguém apagar o 22.0 achando que é repetição,
   o cabeçalho volta para a fonte do kit do Elementor, calado.

   Sobre PESO: `.elementor-location-header` é exclusiva deste documento, então
   dentro dela dá para pesar à vontade sem vazar para o resto do site. Por isso
   aqui NÃO vale a disciplina de (0,1,1) do ponto travado #22 (aquela protege a
   base da PÁGINA, que divide escopo com o kit do Elementor). O que vale é a do
   §18.12: vencer o CSS do widget por peso e nunca por ordem.

   Adversários medidos (arquivos reais do Elementor Pro deste site):
     widget-nav-menu.min.css ............ `a` com padding 13px 20px  (0,2,1)
                                          `.elementor-menu-toggle`  (0,1,0)
                                          dropdown branco/cinza     (0,3,0)
     widget-woocommerce-menu-cart.min.css toggle e bolha            (0,5,0)
   O carrinho é o caso fácil: aquele widget é inteiro dirigido por CUSTOM
   PROPERTIES com fallback (`var(--toggle-button-icon-color, #69727d)`), então
   ali não se briga por especificidade, SETA-SE O TOKEN. É a API do próprio
   widget, e ela continua valendo quando o operador mexer no painel (o painel
   escreve os mesmos tokens com peso maior, e deve mesmo ganhar).
   -------------------------------------------------------------------------- */

/* --- 22.0 Escopo e base --------------------------------------------------
   Vem PRIMEIRO de propósito: o reset de padding de contêiner empata (0,2,0)
   com a casca logo abaixo e perde pela ordem, exatamente como no §3. Mover
   este bloco para o fim do arquivo apaga o respiro do cabeçalho. */
.elementor-location-header .e-con { padding: 0; }

/* --- 22.0a Guarda contra o §3 (ponto travado #27) ------------------------
   ⚠️ NÃO REMOVER. A regra logo acima pesa (0,2,0) e PERDE para a seção do §3,
   que pesa (0,2,1) por causa do `[id]`. Enquanto os contêineres internos
   renderizam `.e-child` isso não aparece; numa importação limpa do JSON eles
   renderizam `.e-parent` e o §3 entra: 107,52px de padding em cada contêiner
   com id, mais a casca de 1200px nos filhos. Medido no cabeçalho forçado a
   `.e-parent`: sem estas duas regras ele vai de 217px para 595px.

   As duas regras existem porque o §3 faz DUAS coisas: a SEÇÃO
   (`.e-con.e-parent[id]`, (0,2,1)) e a CASCA (`… > .e-con`, (0,3,0)). Uma
   guarda só para a primeira deixa a segunda forçando `width: 100%` em cada
   coluna, e a fila em flex sai com um bloco por linha. Pesos daqui: (0,3,1)
   e (0,4,1), então vencem por PESO e não por ordem (ponto travado #23). */
.elementor-location-header .e-con.e-parent[id] { padding: 0; }
.elementor-location-header .e-con.e-parent[id] > .e-con {
  width: auto;
  max-width: none;
  margin-inline: 0;
}

.elementor-location-header {
  position: relative;
  z-index: 30;
  background-color: var(--jn-creme);
  border-bottom: 1px solid var(--jn-line);
  color: var(--jn-text);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  line-height: var(--jn-lh-body);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  overflow-wrap: break-word;
}
.elementor-location-header > .e-con {
  --display: block;
  display: block;
  padding-block: var(--jn-s-4);
  padding-left: max(var(--jn-gutter), env(safe-area-inset-left));
  padding-right: max(var(--jn-gutter), env(safe-area-inset-right));
}
/* Anel de foco: o do §6 não alcança aqui (ver parágrafo 3 do cabeçalho). */
.elementor-location-header a:focus-visible,
.elementor-location-header button:focus-visible,
.elementor-location-header input:focus-visible,
.elementor-location-header [tabindex]:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 3px;
  border-radius: var(--jn-r-media);
}
.elementor-location-header :is(a, button) { -webkit-tap-highlight-color: rgba(141, 78, 42, 0.15); }

/* Alvo do "Skip to content". Precisa ser focável por programa, senão em
   Chrome e Safari o link rola a página e o FOCO fica para trás, no menu. */
.jn-skip-target { position: absolute; width: 1px; height: 1px; }
.jn-skip-target:focus { outline: none; }

/* --- 22.1 A barra --------------------------------------------------------
   Grade de áreas, e não uma pilha de flex, porque é a grade que deixa o
   MESMO DOM virar dois layouts sem trocar a ordem de leitura: no celular o
   logo divide a linha com carrinho e hambúrguer e a busca desce inteira; a
   partir de 768px a busca sobe para o meio e o menu ocupa a linha de baixo,
   como no site BR.

   ⚠️ 768px não é um número escolhido no desenho: é a fronteira que o próprio
   Elementor usa para o `dropdown: mobile` do widget de menu
   (`@media (min-width: 768px) { .elementor-nav-menu--dropdown-mobile
   .elementor-menu-toggle { display: none } }`, medido no arquivo do plugin).
   Se esta media query e aquela discordarem, existe uma faixa de largura com
   hambúrguer E menu horizontal ao mesmo tempo, ou com nenhum dos dois. */
#jn-hdr-bar {
  --display: grid;
  display: grid;
  /* ⚠️ NÃO REMOVER o `position: relative`. Ele é a caixa de contenção de DUAS
     coisas que saem do fluxo: o painel do menu recolhido (22.7) e a gaveta do
     carrinho (22.5a, que zera o `position` dos três ancestrais justamente para
     chegar aqui). Sem ele, as duas reancoram calado no próximo ancestral
     posicionado, que é `.elementor-location-header`, e no desktop o resultado
     ainda parece quase certo. Há checagem no verify-tokens.mjs. */
  position: relative;
  width: 100%;
  max-width: var(--jn-container);
  margin-inline: auto;
  align-items: center;
  gap: var(--jn-s-3) var(--jn-s-4);
  grid-template-columns: 1fr auto auto;
  grid-template-areas:
    "logo   actions nav"
    "search search  search";
}
/* O widget que só carrega o <script> de rotulagem dos <nav> não pode ocupar
   célula: sem isto a grade ganha uma linha implícita e o cabeçalho cresce.
   O bloco de rótulos ao lado dele já sai do fluxo pelo `.jn-i18n` do §9. */
.elementor-location-header .jn-hdr__nav-a11y { display: none; }
.elementor-location-header .jn-hdr__logo   { grid-area: logo; }
.elementor-location-header .jn-hdr__search { grid-area: search; }
.elementor-location-header .jn-hdr__menu   { grid-area: nav; }
#jn-hdr-actions {
  grid-area: actions;
  --display: flex;
  display: flex;
  align-items: center;
  gap: var(--jn-s-2);
}

/* --- 22.2 Logo ----------------------------------------------------------
   O widget é o `Site Logo` do Theme Builder: a imagem vem do Personalizar,
   não do JSON. Sem logo escolhido no Personalizar o widget não renderiza
   NADA (não é erro, é ausência de dado) — está nos portões do INSTALL. */
.elementor-location-header .jn-hdr__logo img {
  display: block;
  width: auto;
  height: 52px;
  max-width: 100%;
}

/* --- 22.3 Busca de produto ----------------------------------------------
   ⚠️ Form escrito à mão, e NÃO o widget `Search Form` do Pro. O motivo é o
   `<input type="hidden" name="post_type" value="product">`: sem ele a busca
   do WordPress devolve páginas e posts junto, e numa loja isso é ruído. O
   widget do Pro não tem esse campo. De quebra, a marcação do preview passa a
   ser a MESMA do JSON, byte a byte, em vez de uma reconstrução. */
.elementor-location-header .jn-search {
  display: flex;
  align-items: stretch;
  width: 100%;
}
.elementor-location-header .jn-search__input {
  flex: 1 1 auto;
  min-width: 0;
  min-height: 48px;
  padding: 0 var(--jn-s-5);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  color: var(--jn-text);
  background: var(--jn-white);
  border: 1px solid var(--jn-line-strong);
  border-right: 0;
  border-radius: var(--jn-r-pill) 0 0 var(--jn-r-pill);
  -webkit-appearance: none;
  appearance: none;
}
/* ⚠️ 0.8 é PISO, não gosto: abaixo disso o placeholder reprova contraste AA.
   O verifier tem uma checagem só para isto, e ela já pegou este campo em 0.75. */
.elementor-location-header .jn-search__input::placeholder { color: var(--jn-ink-deep); opacity: 0.8; }
.elementor-location-header .jn-search__submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex: 0 0 auto;
  min-width: 56px;
  min-height: 48px;
  padding: 0 var(--jn-s-4);
  color: var(--jn-on-action);
  background: var(--jn-terracotta);
  border: 1px solid var(--jn-terracotta);
  border-radius: 0 var(--jn-r-pill) var(--jn-r-pill) 0;
  cursor: pointer;
  transition: background-color var(--jn-d-fast) var(--jn-ease-soft);
}
.elementor-location-header .jn-search__submit:hover { background: var(--jn-ink); border-color: var(--jn-ink); }
.elementor-location-header .jn-search__icon { width: 22px; height: 22px; }

/* --- 22.4 Seletor de idioma (reserva para o WPML) ------------------------
   O slot nasce VAZIO e some enquanto estiver vazio. Um espaço reservado de
   largura fixa deixaria um buraco visível por meses, e no dia da instalação
   o WPML injeta a própria marcação (bandeira, lista, dropdown) com a largura
   dele — reservar largura seria chutar. `:has()` é baseline e já está em uso
   no §0c.

   ⚠️ SEM combinador filho aqui, e isto é o oposto da regra do §0c. Lá o `>` é
   obrigatório porque as seções são contêineres aninhados e `:has()` sem ele
   casaria as quatro camadas de uma vez. Aqui o alvo é um WIDGET, e entre o
   wrapper e o conteúdo o Elementor mete sempre um `.elementor-widget-container`
   — com `> .jn-lang` a regra não casa nada e o slot vazio fica ocupando lugar
   no cabeçalho. Foi assim que ela nasceu, e só apareceu na medição no DOM
   real: `display: block` onde tinha de ser `none`. */
.elementor-location-header .jn-hdr__lang:has(.jn-lang:empty) { display: none; }
.elementor-location-header .jn-lang {
  display: flex;
  align-items: center;
  gap: var(--jn-s-2);
  font-size: var(--jn-t-small);
  color: var(--jn-text);
}

/* --- 22.5 Carrinho -------------------------------------------------------
   Tokens primeiro, seletor só onde o widget não expõe token. A bolha do
   contador nasce VERMELHA (#d9534f é o fallback do plugin) e o ícone CINZA
   (#69727d): são as duas cores que denunciariam "tema padrão" no cabeçalho. */
.elementor-location-header .jn-hdr__cart {
  --toggle-button-background-color: transparent;
  --toggle-button-hover-background-color: transparent;
  --toggle-button-border-width: 0;
  --toggle-button-icon-color: var(--jn-ink);
  --toggle-button-icon-hover-color: var(--jn-terracotta);
  --toggle-icon-size: 26px;
  --items-indicator-background-color: var(--jn-terracotta);
  --items-indicator-text-color: var(--jn-white);
  --cart-background-color: var(--jn-white);
  --cart-border-color: var(--jn-line);
  /* ⚠️ O canto arredondado JÁ VALE, e quem o consome é `.elementor-menu-cart
     __main`, não o `__container`. Medido: `__main` devolve 6px, que é o
     `--jn-r-card` dos cards. O `__container` devolve 0 e vai continuar
     devolvendo, porque em modo mini-cart ele é só o envelope de posição e
     leva `background: none`. Medir o elemento de fora e concluir "falta
     border-radius" foi o diagnóstico da Rev.05; não repetir. */
  --cart-border-radius: var(--jn-r-card);
  --cart-padding: var(--jn-s-5);
  --divider-color: var(--jn-line);
  --product-price-color: var(--jn-terracotta);
  --menu-cart-subtotal-color: var(--jn-heading);
  --empty-message-color: var(--jn-text);
  --remove-item-button-color: var(--jn-ink-deep);
  --remove-item-button-hover-color: var(--jn-terracotta);
  --view-cart-button-background-color: transparent;
  --view-cart-button-text-color: var(--jn-terracotta);
  --checkout-button-background-color: var(--jn-terracotta);
  --checkout-button-text-color: var(--jn-white);
  --checkout-button-hover-background-color: var(--jn-ink);
  --checkout-button-hover-text-color: var(--jn-white);
}
/* Alvo de toque: o botão nasce do tamanho do ícone. 48px é o piso do brief. */
.elementor-location-header .jn-hdr__cart .elementor-menu-cart__toggle .elementor-button {
  min-width: 48px;
  min-height: 48px;
  padding: 0;
}

/* --- 22.5a Gaveta do mini carrinho (ponto travado #27) --------------------
   ⚠️ NÃO REMOVER, e não trocar por `right: 0`. Este é o único ponto do
   carrinho que NÃO se resolve por token: o widget não expõe custom property
   para o lado em que a gaveta abre.

   Adversário MEDIDO, no arquivo do plugin deste site:
     .elementor-widget-woocommerce-menu-cart.elementor-menu-cart--cart-type-mini-cart
     .elementor-menu-cart__container { position:absolute; top:100%;
       inset-inline: 0 auto; min-width: 330px; }              (0,3,0)

   `inset-inline: 0 auto` em LTR é `left: 0`: a gaveta é ancorada pela borda
   ESQUERDA do botão e cresce 330px para a DIREITA. O botão mora no fim da
   barra, então a gaveta sai da tela. Medido antes: a caixa ia de 1312px a
   1642px numa viewport de 1536, e `documentElement.scrollWidth` subia de
   1521 para 1642, ou seja, 106px de rolagem horizontal no site inteiro,
   ABERTA OU FECHADA quando o estado fechado não é `scale(0)`.

   A correção ancora pela borda DIREITA: `inset-inline: auto 0`. A forma
   lógica é obrigatória, e não `right: 0`: o `left: 0` do plugin sobrevive a
   um `right` sozinho (são propriedades diferentes), e a forma lógica é a que
   continua correta em RTL, que é a razão de o próprio plugin usá-la.

   `min-width: 0` também é obrigatório: os 330px do plugin são MIN-width, e
   min vence max, então sem zerar isto o `max-width` abaixo não valeria nada
   e a gaveta estouraria para a esquerda em telas estreitas.

   ⚠️ POR QUE NÃO `overflow-x: clip` NO CABEÇALHO. Cortar resolveria a barra
   de rolagem e ESCONDERIA a gaveta cortada, com o botão de finalizar compra
   do lado de fora. Trocaria um defeito visível por um caminho de compra
   quebrado, e ainda calaria a próxima regressão do mesmo tipo. Peso daqui
   (0,4,0), acima dos (0,3,0) do plugin: vence por PESO, nunca por ordem.

   ⚠️ O BLOCO DE ANCORAGEM ABAIXO NÃO É OPCIONAL, e a primeira versão desta
   correção não o tinha. Ancorar pela direita resolve o desktop e NÃO resolve
   o celular: a caixa de contenção da gaveta é o `__toggle_wrapper`, que tem
   48px, e no celular o carrinho não é o último item da barra (o hambúrguer
   vem depois dele). Medido a 360px: a gaveta ficava com a borda direita em
   261px e a esquerda em MENOS 59px, ou seja, um pedaço dela fora da tela
   pela ESQUERDA. Isso não aparece em `scrollWidth`, porque estouro à
   esquerda não estende a área rolável: seria mais um defeito MUDO.

   A saída é trocar a caixa de contenção, e não inventar número mágico.
   Zerando o `position` dos três ancestrais posicionados entre a gaveta e a
   barra, quem passa a conter é `#jn-hdr-bar`, que é exatamente a medida do
   conteúdo do cabeçalho. Aí `inset-inline: auto 0` alinha a gaveta pela
   direita da barra e `max-width: 100%` a impede de passar da esquerda, sem
   `100vw` (que conta a barra de rolagem e erra por 15px) e sem contar
   quanto mede o hambúrguer. Medido de 360px a 1280px: a gaveta cabe dentro
   da barra nas seis larguras.

   A bolha do contador NÃO se mexe com isso: ela é posicionada em relação a
   `.elementor-button-icon`, que não está nesta lista. Medido. */
.elementor-location-header .jn-hdr__cart.elementor-widget-woocommerce-menu-cart,
.elementor-location-header .jn-hdr__cart .elementor-menu-cart__toggle_wrapper,
.elementor-location-header #jn-hdr-actions {
  position: static;
}
.elementor-location-header .jn-hdr__cart.elementor-widget-woocommerce-menu-cart .elementor-menu-cart__container {
  inset-inline: auto 0;
  min-width: 0;
  width: 330px;
  max-width: 100%;
}

/* --- 22.6 Menu ------------------------------------------------------------
   ⚠️ O item vai em `a.elementor-item`, quatro classes, e NÃO em `... li a`.
   Medido: `.elementor-nav-menu__align-justify .elementor-nav-menu--layout-
   horizontal .elementor-nav-menu > li > a` pesa (0,3,2), que EMPATARIA com
   `li a` aqui. Empate se resolve pela ordem de carregamento, e é justamente
   o que o ponto travado #23 proíbe. Com `.elementor-item` a conta vai a
   (0,4,2) e nenhum adversário do plugin chega perto. */
.elementor-location-header .jn-hdr__menu .elementor-nav-menu li a.elementor-item {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: var(--jn-s-2);
  font-family: var(--jn-font-head);
  font-size: 1.0625rem;
  font-weight: 500;
  line-height: var(--jn-lh-head);
  color: var(--jn-heading);
  text-decoration: none;
  background-color: transparent;
  border-bottom: 2px solid transparent;
  transition: color var(--jn-d-base) var(--jn-ease-soft),
              border-color var(--jn-d-base) var(--jn-ease-soft);
}
/* ⚠️ HOVER, FOCO E ITEM ATUAL NÃO SÃO DECIDIDOS AQUI, e sim no §16, que é o
   ponto travado #10 e usa `!important` (`.elementor-nav-menu .elementor-item
   :hover/:focus/.elementor-item-active { color: terracota !important }`).
   Aquele bloco existe para neutralizar o verde #61CE70 que o Elementor traz
   por padrão, e vem de uma rodada anterior. As duas regras concordam no valor
   DE PROPÓSITO. Se alguém trocar a cor de hover só aqui, nada muda na tela e
   a busca pelo motivo é longa: trocar no §16 também, ou tirar o `!important`
   de lá com a consciência de que ele protege contra o verde. */
.elementor-location-header .jn-hdr__menu .elementor-nav-menu li a.elementor-item:hover {
  color: var(--jn-terracotta);
  background-color: transparent;
}
/* Página atual: cor E sublinhado. Cor sozinha não é sinal acessível. */
.elementor-location-header .jn-hdr__menu .elementor-nav-menu li a.elementor-item.elementor-item-active {
  color: var(--jn-terracotta);
  background-color: transparent;
  border-bottom-color: var(--jn-terracotta);
}
/* ⚠️ `--main` no seletor NÃO é enfeite. O widget emite DUAS <ul> com a classe
   `.elementor-nav-menu`: a do menu horizontal e a do painel recolhido. Sem o
   `--main`, a fila em `flex` também pega o painel do celular, e o menu do
   telefone sai DEITADO, dois itens por linha, com barra de rolagem. Medido. */
.elementor-location-header .jn-hdr__menu .elementor-nav-menu--main .elementor-nav-menu {
  display: flex;
  flex-wrap: wrap;
  gap: 0 var(--jn-s-5);
  padding: 0;
  margin: 0;
}

/* Hambúrguer.
   ⚠️ NÃO declarar `display` nesta regra. Quem esconde o hambúrguer acima de
   768px é `.elementor-nav-menu--dropdown-mobile .elementor-menu-toggle`, que
   pesa (0,2,0). Esta regra pesa (0,3,0) e passaria por cima: o hambúrguer
   apareceria no desktop AO LADO do menu horizontal. */
.elementor-location-header .jn-hdr__menu .elementor-menu-toggle {
  --nav-menu-icon-color: var(--jn-ink);
  align-items: center;
  justify-content: center;
  min-width: 48px;
  min-height: 48px;
  padding: 0;
  font-size: 26px;
  color: var(--jn-ink);
  background-color: transparent;
  border-radius: var(--jn-r-pill);
}
.elementor-location-header .jn-hdr__menu .elementor-menu-toggle:hover { color: var(--jn-terracotta); }

/* --- 22.7 Menu recolhido (celular) ---------------------------------------
   ⚠️ O painel do menu recolhido NÃO é posicionado pelo plugin: por padrão ele
   fica NO FLUXO e só colapsa por `max-height: 0`. Dentro de uma célula de
   grade de 48px, aberto, ele sairia com 48px de largura. O Elementor resolve
   isso com a opção "Full Width", que depende do JS dele para medir e posicionar.
   Aqui é resolvido em CSS: `position: absolute` ancorado no `#jn-hdr-bar`
   (que é `position: relative` no 22.1). A regra de colapso do plugin
   (`:not(.elementor-active) + .elementor-nav-menu__container`) continua
   mandando na abertura e no fechamento — só a caixa mudou de lugar. */
@media (max-width: 767px) {
  /* ⚠️ Esta linha é metade da correção, e sem ela a outra metade não faz nada.
     O bloco que posiciona um `absolute` é o ancestral POSICIONADO mais próximo,
     e o Elementor dá `position: relative` a TODO wrapper de widget. Sem zerar
     isso aqui, o painel se ancora no próprio widget do menu e reabre com os
     48px do hambúrguer. Medido: `left: 322px, width: 48px`. */
  .elementor-location-header .jn-hdr__menu { position: static; }
  .elementor-location-header .jn-hdr__menu .elementor-nav-menu__container.elementor-nav-menu--dropdown {
    position: absolute;
    left: 0;
    right: 0;
    top: calc(100% + var(--jn-s-3));
    z-index: 40;
    margin-top: 0;
    font-size: inherit;
    background-color: var(--jn-white);
    border: 1px solid var(--jn-line);
    border-radius: var(--jn-r-card);
    box-shadow: 0 12px 32px var(--jn-shadow-lift);
  }
  .elementor-location-header .jn-hdr__menu .elementor-nav-menu--dropdown li a.elementor-item {
    min-height: 48px;
    padding: var(--jn-s-3) var(--jn-s-5);
    border-bottom: 0;
  }
  .elementor-location-header .jn-hdr__menu .elementor-nav-menu--dropdown li a.elementor-item:hover,
  .elementor-location-header .jn-hdr__menu .elementor-nav-menu--dropdown li a.elementor-item:focus,
  .elementor-location-header .jn-hdr__menu .elementor-nav-menu--dropdown li a.elementor-item.elementor-item-active {
    color: var(--jn-terracotta);
    background-color: var(--jn-shade);
  }
}

/* --- 22.8 Tablet e desktop (a partir de 768px) --------------------------- */
@media (min-width: 768px) {
  .elementor-location-header > .e-con { padding-block: var(--jn-s-5); }
  #jn-hdr-bar {
    gap: var(--jn-s-5) var(--jn-s-6);
    grid-template-columns: auto minmax(0, 1fr) auto;
    grid-template-areas:
      "logo search actions"
      "nav  nav    nav";
  }
  .elementor-location-header .jn-hdr__logo img { height: 76px; }
  /* ⚠️ `justify-content` na <ul> NÃO centraliza nada aqui, e isso foi medido.
     Quem é o contêiner flex é o <nav> (`.elementor-nav-menu--layout-horizontal`
     é `display: flex`), então a <ul> é um ITEM dimensionado pelo conteúdo e
     centralizar por dentro dela não move a fila. O que centraliza é a margem
     automática na própria <ul>, que é o mesmo mecanismo do
     `.elementor-nav-menu__align-center` do plugin. */
  .elementor-location-header .jn-hdr__menu .elementor-nav-menu--main .elementor-nav-menu { margin-inline: auto; gap: 0 var(--jn-s-7); }
  .elementor-location-header .jn-hdr__menu .elementor-nav-menu li a.elementor-item { padding: var(--jn-s-2) 0; }
}
@media (min-width: 981px) {
  .elementor-location-header .jn-hdr__logo img { height: 92px; }
}

/* --- 22.9 Reduced motion ------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .elementor-location-header .jn-hdr__menu .elementor-nav-menu li a.elementor-item { transition: none !important; }
  .elementor-location-header .jn-search__submit { transition: none !important; }
  .jn-skip { transition: none !important; }
}

/* ==========================================================================
   23. RODAPÉ (Theme Builder) — `footer-eu.json`
   --------------------------------------------------------------------------
   ⚠️ PONTO TRAVADO #26. Vale aqui, inteiro, o cabeçalho do §22: âncora de
   classe e nunca de tag, contêiner de topo SEM `_element_id`, e base
   redeclarada.

   ⚠️ REV.05 CORRIGE O MOTIVO DA REDECLARAÇÃO. Estava escrito "porque §2 e §6
   não alcançam este documento". No site ao vivo isso é FALSO justamente
   aqui: os contêineres do rodapé renderizam `.e-parent[id]`, então §2 e §6
   ALCANÇAM. Continuam redeclarados, e a razão certa é outra: alcançam ou
   não conforme o template tenha sido importado ou reconstruído no editor, e
   isso não se controla daqui. Redeclarar incondicionalmente é o que faz o
   rodapé ter a mesma cara nos dois casos. Ver ponto travado #27.

   ⚠️ REV.05, ALTURA. O rodapé media 779px no ar, e a causa não era o
   desenho: era o §3 entrando pelos ids (215px em `#jn-ftr-cols`, 108px em
   `#jn-ftr-base`) somados a 144px de padding de faixa e a uma lista legal
   empilhada de 188px. Com a guarda do 23.0a, a faixa compacta e os links
   legais na linha do disclaimer, mede 240px. Ver §23.4.

   Modelo: o rodapé do site BR (três blocos numa faixa creme, disclaimer no
   canto). Duas mudanças deliberadas:

   • O CNPJ brasileiro NÃO atravessa. Além de ser jurisdição errada, o dado
     que a UE cobra no Impressum é o nº de IVA/registro da entidade EUROPEIA.
     ⚠️ RODADA LEGAL (04/09/2026): a entidade existe e o marcador
     `[EU VAT / company number]` SAIU — no lugar estão o nome do titular e o
     nº de IVA (Bruno Simões Coelho · PT298604990). O verifier reprova se o
     CNPJ voltar ao JSON E reprova se o marcador voltar (seria apagar um dado
     real). A morada completa e o telefone vivem no §1 dos Termos, que é onde
     a UE aceita a identificação por extenso.
   • Os contatos são ÍCONE COM RÓTULO, e o BR é só ícone. Um link cujo nome
     acessível é "Envelope" não diz para onde vai. O rótulo custa duas
     palavras e resolve para leitor de tela e para quem não decodifica o
     desenho do ícone.
   -------------------------------------------------------------------------- */

/* --- 23.0 Escopo e base -------------------------------------------------- */
.elementor-location-footer .e-con { padding: 0; }

/* --- 23.0a Guarda contra o §3 (ponto travado #27) ------------------------
   ⚠️ NÃO REMOVER. É a mesma guarda do 22.0a, e aqui ela não é preventiva:
   é o conserto do defeito medido. `#jn-ftr-cols`, `#jn-ftr-pay` e
   `#jn-ftr-base` renderizam `.e-con.e-parent[id]` no ar, casam a âncora de
   seção do §3 e recebiam 107,52px de padding em cima e embaixo, mais
   `width: 100%` em cada coluna pela casca. Sozinho, isso respondia por
   323px dos 779px do rodapé (215px na fila e 108px na faixa de baixo, os dois
   medidos). A regra logo acima pesa (0,2,0) e perde para
   os (0,2,1) do §3; estas pesam (0,3,1) e (0,4,1). */
.elementor-location-footer .e-con.e-parent[id] { padding: 0; }
.elementor-location-footer .e-con.e-parent[id] > .e-con {
  width: auto;
  max-width: none;
  margin-inline: 0;
}

.elementor-location-footer {
  background-color: var(--jn-creme);
  border-top: 1px solid var(--jn-line);
  color: var(--jn-text);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-small);
  line-height: var(--jn-lh-body);
  -webkit-font-smoothing: antialiased;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  overflow-wrap: break-word;
}
.elementor-location-footer > .e-con {
  --display: block;
  display: block;
  /* ⚠️ REV.05: era `var(--jn-section-y-sm)`, que dá 72px em cada ponta e
     sozinho já valia 144px de rodapé. O rodapé não é uma seção de conteúdo,
     é a moldura do site, e o alvo do brief é 200px a 280px de altura total.
     Esta faixa é a única do arquivo com este ritmo, então mora aqui e não
     no bloco de tokens: um token novo no :root convidaria a reusar 40px de
     seção em outro lugar, que é o oposto do que o §3 estabelece. */
  padding-block: clamp(32px, 3.2vw, 40px);
  padding-left: max(var(--jn-gutter), env(safe-area-inset-left));
  padding-right: max(var(--jn-gutter), env(safe-area-inset-right));
  padding-bottom: calc(clamp(32px, 3.2vw, 40px) + env(safe-area-inset-bottom, 0px));
}
.elementor-location-footer a:focus-visible,
.elementor-location-footer button:focus-visible,
.elementor-location-footer [tabindex]:focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 3px;
  border-radius: var(--jn-r-media);
}
.elementor-location-footer :is(a, button) { -webkit-tap-highlight-color: rgba(141, 78, 42, 0.15); }

/* --- 23.1 A fila de blocos -----------------------------------------------
   Empilhada e centrada no celular (pedido do brief), em linha a partir de
   641px, a mesma fronteira que os diferenciais da home usam para virar 3-up,
   então o ritmo do site não ganha um breakpoint novo.

   ⚠️ REV.05: era `grid` com `repeat(3, 1fr)`. Duas razões para sair da grade,
   as duas medidas. (a) Um bloco escondido não some da grade, some da FILA:
   com `#jn-ftr-pay` em `display: none`, a grade de três trilhas deixava o
   terço da direita vazio. Em flex, o bloco simplesmente não ocupa lugar e os
   que sobram se ajustam. (b) `repeat(auto-fit, minmax(N, 1fr))` resolveria
   (a), mas traz um degrau invisível de 3 para 2 trilhas por volta de 750px,
   que não coincide com o 641px do resto do arquivo. Flex com base de 240px
   quebra linha onde o conteúdo manda e não inventa fronteira nova.

   ⚠️ `--flex-direction` JUNTO com `flex-direction` não é redundância: o
   `.e-con` do Elementor declara `flex-direction: var(--flex-direction)`, e o
   padrão dele é `column`. Setar só a propriedade CSS funciona por peso, mas
   deixa o token mentindo para quem for depurar; setar só o token não vence.
   Medido: sem os dois, `flex-basis` vira ALTURA e a fila fica com 240px de
   altura por bloco. */
#jn-ftr-cols {
  --display: flex;
  display: flex;
  --flex-direction: column;
  flex-direction: column;
  align-items: center;
  width: 100%;
  max-width: var(--jn-container);
  margin-inline: auto;
  gap: var(--jn-s-5) var(--jn-s-7);
  text-align: center;
}
.elementor-location-footer .jn-ftr__title .elementor-heading-title {
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-eyebrow);
  font-weight: 700;
  letter-spacing: var(--jn-ls-eyebrow);
  line-height: var(--jn-lh-head);
  text-transform: uppercase;
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-4);
}

/* --- 23.2 Contatos ------------------------------------------------------- */
.jn-contact {
  display: flex;
  flex-wrap: wrap;
  gap: var(--jn-s-3);
  justify-content: center;
  list-style: none;
  margin: 0;
  padding: 0;
}
.jn-contact__item { margin: 0; }
.jn-contact__link {
  display: inline-flex;
  align-items: center;
  gap: var(--jn-s-2);
  min-height: 44px;
  padding: 0 var(--jn-s-4);
  font-size: var(--jn-t-small);
  font-weight: 600;
  color: var(--jn-terracotta);
  text-decoration: none;
  background: transparent;
  border: 1px solid var(--jn-line-strong);
  border-radius: var(--jn-r-pill);
  transition: background-color var(--jn-d-fast) var(--jn-ease-soft),
              border-color var(--jn-d-fast) var(--jn-ease-soft);
}
.jn-contact__link:hover { background-color: var(--jn-shade); border-color: var(--jn-terracotta); }
.jn-contact__icon { flex: none; width: 20px; height: 20px; }

/* --- 23.3 Formas de pagamento (slot) -------------------------------------
   ⚠️ O bloco INTEIRO some enquanto a lista estiver vazia, título junto. Um
   "Payment methods" sem ícone nenhum é pior do que não ter a seção: parece
   que a loja não aceita pagamento. Colou o primeiro <li>, o bloco reaparece
   sozinho. As marcas reais entram quando o operador enviar a imagem (a Mollie
   define quais métodos a loja aceita na UE). */
.elementor-location-footer #jn-ftr-pay:has(.jn-pay:empty) { display: none; }
.jn-pay {
  display: flex;
  flex-wrap: wrap;
  gap: var(--jn-s-3);
  justify-content: center;
  list-style: none;
  margin: 0;
  padding: 0;
}
.jn-pay__item { margin: 0; }
.jn-pay__mark {
  display: block;
  width: auto;
  height: 28px;
  padding: var(--jn-s-1) var(--jn-s-2);
  background: var(--jn-white);
  border: 1px solid var(--jn-line);
  border-radius: var(--jn-r-media);
}

/* --- 23.4 Links legais ---------------------------------------------------
   ⚠️ REV.05 MUDOU O LUGAR DELES, e a conta é o motivo. Empilhados eram
   188px de altura, quase um quarto do rodapé inteiro. Deitados eles somam
   486px de texto mais três intervalos de 24px, ou seja, 558px de linha.
   Isso NÃO cabe numa coluna da fila: no desktop a coluna dá 368px, e a
   768px, que é onde o brief manda medir, dá 204px. Numa coluna eles
   voltariam a empilhar exatamente na largura em que o brief pede que não
   empilhem.

   A faixa do disclaimer é o único lugar do rodapé com a linha inteira
   disponível: 1200px no desktop e 707px a 768px, e 558px cabe nos dois. Por
   isso o bloco legal foi movido no `footer-eu.json` para dentro de
   `#jn-ftr-base`, e o título "Legal" saiu junto: um rótulo de seção em cima
   de letra miúda seria uma quarta linha para dizer o que os quatro links já
   dizem. É a única mudança estrutural do payload nesta rodada, e ela obriga
   a REIMPORTAR o rodapé (ver INSTALL.md).

   `row-gap: 0` de propósito: o alvo de toque de 44px já dá o respiro entre
   linhas quando um idioma mais longo quebrar a fila. */
.elementor-location-footer .jn-ftr__links ul {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  column-gap: var(--jn-s-5);
  row-gap: 0;
  list-style: none;
  margin: 0;
  padding: 0;
}
.elementor-location-footer .jn-ftr__links li { margin: 0; }
.elementor-location-footer .jn-ftr__links a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  color: var(--jn-text);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: color var(--jn-d-fast) var(--jn-ease-soft),
              border-color var(--jn-d-fast) var(--jn-ease-soft);
}
.elementor-location-footer .jn-ftr__links a:hover {
  color: var(--jn-terracotta);
  border-bottom-color: currentColor;
}

/* --- 23.5 Faixa do disclaimer --------------------------------------------
   A "linha divisória" que o brief pede manter é este `border-top`. Ela ficou,
   e separa a fila de blocos da letra miúda. No celular a faixa vira coluna e
   tudo centraliza.

   ⚠️ CORRIGIDO EM 09/09/2026 — ATÉ AQUI ESTE COMENTÁRIO DIZIA que a faixa
   carrega "os links legais à esquerda e o aviso de direitos à direita", na
   MESMA linha. Deixou de ser verdade, e por duas somas que aconteceram na
   rodada legal:

     · a fila legal ganhou um 5º item, o **Livro de Reclamações** (obrigação
       portuguesa do DL 156/2005, que exige link visível em todas as páginas);
     · o aviso trocou o marcador `[EU VAT / company number]` pelos dados reais
       da entidade, e ficou mais largo.

   Medido a 1536px: fila legal **745px** + aviso **513px** = 1258px contra os
   1200px do contêiner. O `flex-wrap: wrap` faz o aviso descer para a própria
   linha, à esquerda, e o `space-between` deixa de ter efeito porque cada linha
   passa a ter um item só. NÃO é defeito: não há sobreposição em largura
   nenhuma (medido a 1536, 1024 e 768) e o rodapé foi de 240px para **272px**,
   dentro da faixa de 200–280 que o brief da Rev.05 fixou.

   Se um dia se quiser a linha única de volta, o caminho é ENCURTAR O AVISO
   (tirar o nome do titular, deixando só o nº de IVA, que é o dado que a UE
   cobra) — e não apagar o Livro de Reclamações, que é obrigação legal. */
#jn-ftr-base {
  --display: flex;
  display: flex;
  --flex-direction: column;
  flex-direction: column;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--jn-s-3) var(--jn-s-7);
  width: 100%;
  max-width: var(--jn-container);
  margin-inline: auto;
  margin-top: var(--jn-s-5);
  padding-top: var(--jn-s-4);
  border-top: 1px solid var(--jn-line);
  text-align: center;
}
.elementor-location-footer .jn-ftr__note p {
  margin: 0;
  max-width: none;
  font-size: var(--jn-t-eyebrow);
  letter-spacing: 0.02em;
  color: var(--jn-text);
}

/* --- 23.6 Tablet e desktop -----------------------------------------------
   Uma fronteira só, 641px, a mesma do resto do arquivo. Medido nos dois
   lados: a 640px o rodapé empilha e cresce (o brief permite), a 641px ele
   entra na faixa compacta. */
@media (min-width: 641px) {
  #jn-ftr-cols {
    --flex-direction: row;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: flex-start;
    text-align: left;
  }
  /* Base de 240px: dois blocos numa linha de 707px (tablet) ainda dão 329px
     cada, que é mais que os 290px de que os contatos precisam. Abaixo disso
     a linha quebra sozinha, em vez de espremer as pílulas. */
  #jn-ftr-cols > .e-con { flex: 1 1 240px; }
  .jn-contact,
  .jn-pay { justify-content: flex-start; }
  #jn-ftr-base {
    --flex-direction: row;
    flex-direction: row;
    align-items: center;
    justify-content: space-between;
    text-align: left;
  }
  .elementor-location-footer .jn-ftr__links ul { justify-content: flex-start; }
}

/* --- 23.7 Reduced motion ------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  .jn-contact__link,
  .elementor-location-footer .jn-ftr__links a { transition: none !important; }
}

/* ==========================================================================
   24. PÁGINAS DE TEXTO LEGAL — TERMOS, ENVIOS E DEVOLUÇÕES, E OS DOCUMENTOS
       GERADOS PELO COMPLIANZ (privacidade, cookies)
   --------------------------------------------------------------------------
   ⚠️ PONTO TRAVADO #28.

   O HANDOFF DESTA RODADA DIZIA QUE ESTE BLOCO NÃO PRECISAVA EXISTIR: "o
   custom.css do tema filho já cobre a tipografia e o layout geral". NÃO
   COBRE — e o §17b é a prova, porque ele só existe porque as páginas que não
   são do Elementor não recebiam nada. A lista dele é FECHADA:

       body:is(.woocommerce-cart, .woocommerce-checkout,
               .post-type-archive-product, .single-product)

   Uma página comum do WordPress (`body.page` sem dados do Elementor) não está
   nessa lista, não é uma seção `.e-con.e-parent[id]` (§2/§3) e não carrega
   classe `jn-*` de widget. Das três âncoras do §0, NENHUMA a alcança.

   O que ela recebe, MEDIDO em `hello-elementor/assets/css/reset.css` e
   `theme.css` da instalação em disco (não inferido — o tema está lá):

       body             color:#333 · -apple-system,… · line-height:1.5 · bg #fff
       h1..h6           font-weight:500 · line-height:1.2 · 2.5/2/1.75rem
       p                margin-block-end:.9rem
       a                color:#c36                    ← rosa, não terracota
       .page-content a  text-decoration:underline
       body:not([class*=elementor-page-]) .site-main   max-width:1140px

   ⚠️ E NÃO É SÓ LEITURA DE ARQUIVO. Isto foi MEDIDO no site ao vivo
   (janaguardanapos-eu.local, 04/09/2026), com a marcação real desta página
   dentro de `.page-content` e a pilha inteira de folhas carregada — Woo, kit
   do Elementor, widgets, `custom.css` do tema filho. Desligando SÓ o §24, o
   computed style devolveu:

       background   rgb(255,255,255)     ← branco, não creme
       font-family  -apple-system, …     ← fonte de sistema, não Raleway
       color        rgb(51,51,51)        ← cinza, não o marrom da marca
       h2           -apple-system 32px, cinza
       a            rgb(204,51,102)      ← #c36, ROSA
       largura      1140px  →  171 CARACTERES POR LINHA

   Cento e setenta e um. É a falha muda de sempre — zero erro no console, e o
   cabeçalho e o rodapé por cima e por baixo continuam perfeitos, o que torna
   o defeito ainda mais fácil de não ver. Com o §24 ligado, na MESMA página:
   creme, Raleway, marrom, terracota, e 69 caracteres por linha.

   --------------------------------------------------------------------------
   ⚠️ POR QUE O ÂNCORA É [id="cmplz-document"] E NÃO #cmplz-document.
   Duas das quatro páginas (privacidade e cookies) são geradas pelo Complianz,
   e a marcação NÃO é nossa. Medido no plugin instalado (7.5.2,
   documents/class-document.php):

       linha  640   <div id="cmplz-document" class="cmplz-document …">
       linha 1202   o MESMO nome, cmplz-document, vai também para o <body>

   O segundo uso é a armadilha: `.cmplz-document { max-width }` espremeria o
   SITE INTEIRO — cabeçalho e rodapé juntos — a uma medida de leitura nessas
   duas páginas. Por isso o alvo é o id. E escrito como ATRIBUTO de propósito:
   `#cmplz-document` pesa (1,0,0) e `.jn-legal` pesa (0,1,0), então as duas
   metades da mesma regra teriam pesos diferentes e uma poderia valer onde a
   outra não vale. `[id="cmplz-document"]` pesa (0,1,0) — o mesmo que
   `.jn-legal`. As quatro páginas viram o MESMO objeto para esta folha.

   ⚠️ PREMISSA CORRIGIDA EM 19/09/2026 — LER O §24.9 ANTES DE MEXER AQUI. A
   conta acima está certa, mas ela supunha que do lado do Complianz não havia
   adversário. Com a Cookie Policy gerada, mediu-se que o próprio plugin
   escreve em `#cmplz-document` (1,0,0) e VENCE estas regras: caixa a 800px,
   corpo a 14px, h2 a 22px. O §24.9 repõe SÓ as propriedades disputadas, com
   id, e é a única parte desta folha autorizada a usar `#cmplz-document`.

   --------------------------------------------------------------------------
   ⚠️ O `html` NA FRENTE DAS REGRAS DE TIPOGRAFIA. O kit do Elementor
   (uploads/elementor/css/post-16.css, medido) hoje declara só TOKENS, sem
   nenhuma regra de h1..h6. Mas basta o operador preencher Site Settings →
   Typography para o kit passar a emitir `.elementor-kit-16 h2 { … }` em
   (0,1,1) — e o kit carrega ANTES desta folha quando ela mora em Site
   Settings. `html :is(.jn-legal, [id="cmplz-document"]) :is(h2,h3,h4)` fecha
   em (0,1,2) e vence por PESO, nunca por ordem. É a doutrina do §18.12
   aplicada ANTES de o adversário existir, porque aqui ele nasce de um clique
   no painel.

   --------------------------------------------------------------------------
   ⚠️ QUAL TEMPLATE DE PÁGINA USAR — a resposta muda a marcação, e uma das
   opções custa acessibilidade. Medido nos arquivos do plugin e do tema:

     · Padrão (hello-elementor/template-parts/single.php): imprime
       <main id="content" class="site-main">, <div class="page-header">
       <h1 class="entry-title"> e <div class="page-content">.
     · "Elementor Full Width" (elementor/modules/page-templates/templates/
       header-footer.php + print_callback()): imprime the_content() NU —
       sem <main>, sem <h1>, sem casca nenhuma.

   No Full Width a página fica SEM LANDMARK PRINCIPAL e SEM h1, e nas duas
   páginas do Complianz não há como repor o h1: o documento delas começa em
   h2. Este bloco funciona nos DOIS templates (a medida e o respiro moram na
   própria caixa, não na casca do tema), mas a recomendação é o template
   PADRÃO, e ela está no INSTALL como portão de operador.
   ========================================================================== */

/* --- 24.1 Base da página -------------------------------------------------
   Mesmas declarações do §2 e do §17b, nem uma a mais: é o desenho já
   aprovado chegando onde ele ainda não chegava. (0,1,1) nos dois seletores,
   a mesma conta do §17b, e o adversário é `body` do reset em (0,0,1). */
body:has(.jn-legal),
body.cmplz-document {
  /* ⚠️ MEDIDA PRÓPRIA, e NÃO o `--jn-measure` global. Não é preferência: foi
     CONTADO no navegador, caractere a caractere, com um Range por linha na
     prosa real desta página. O token da marca vale 68ch, e `ch` é o avanço do
     glifo "0" — em Raleway o glifo médio é bem mais estreito, então 68ch
     entrega LINHA DE 91 CARACTERES. Medido nesta página, a 1536px:
         68ch → 666px → 91 caracteres/linha
         58ch → 568px → 79
         54ch → 529px → 69     ← escolhido
         52ch → 509px → 68
         46ch → 450px → 63
     Uma landing se lê aos pedaços e 91 passa; um documento jurídico se lê de
     cabo a rabo, e aí a linha longa faz o olho perder a volta. NÃO trocar por
     `var(--jn-measure)` para "padronizar": isso devolve os 91. E NÃO mexer no
     token global para consertar aqui — ele carrega o resto do site.
     O `.page-header` do §24.3 lê esta mesma variável, e é por isso que ela
     mora no <body> e não na caixa: título e texto têm de bater na mesma
     borda esquerda. */
  --jn-legal-measure: 54ch;
  color: var(--jn-text);
  font-family: var(--jn-font-body);
  font-size: var(--jn-t-body);
  line-height: var(--jn-lh-body);
  background: var(--jn-creme);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  -webkit-text-size-adjust: 100%;
  text-size-adjust: 100%;
  overflow-wrap: break-word;
}

/* --- 24.2 A caixa de leitura ---------------------------------------------
   ⚠️ `measure + 2 × gutter`, NÃO `measure`. Mesma conta do §17b e pela mesma
   razão: com box-sizing border-box (reset do Hello, no `*`) o padding come a
   largura, e `max-width: 68ch` deixaria a linha em ~56ch no desktop, abaixo
   do piso confortável. Esta conta devolve os 68ch de TEXTO.

   O respiro vertical mora AQUI, e não no `main` do tema, porque no template
   Full Width o `main` não existe. */
.jn-legal,
[id="cmplz-document"] {
  width: 100%;
  max-width: calc(var(--jn-legal-measure) + 2 * var(--jn-gutter));
  margin-inline: auto;
  padding-inline: max(var(--jn-gutter), env(safe-area-inset-left));
  padding-right: max(var(--jn-gutter), env(safe-area-inset-right));
  padding-block: 0 var(--jn-section-y-sm);
}
/* Sem `.page-header` não existe título do tema para dar o respiro de cima —
   é o caso do template Full Width. Só então a caixa paga esse respiro. */
body:not(:has(.page-header)) :is(.jn-legal, [id="cmplz-document"]) {
  padding-block-start: var(--jn-section-y-sm);
}

/* --- 24.3 O título da página ---------------------------------------------
   Quem imprime é o TEMA (h1.entry-title), não nós: assim as quatro páginas
   têm exatamente um h1 e todas o obtêm pelo mesmo caminho — inclusive as duas
   do Complianz, onde não temos como injetar marcação.

   Mesmo tratamento do `.jn-cart-title` (§20) e do título de arquivo (§18.1):
   é o mesmo tipo de objeto, um cabeçalho utilitário de página. Amatic SC fica
   para os títulos de seção da marca.

   ⚠️ Os `max-width: none` / `padding-inline: 0` desarmam a caixa de 1140px
   que o tema põe em `.page-header .entry-title` (0,2,0); sem eles o título
   ficaria centrado numa medida e o texto em outra. Nossa cadeia fecha em
   (0,3,1). */
body:has(.jn-legal) .page-header,
body.cmplz-document .page-header {
  width: 100%;
  max-width: calc(var(--jn-legal-measure) + 2 * var(--jn-gutter));
  margin-inline: auto;
  padding-inline: max(var(--jn-gutter), env(safe-area-inset-left));
  padding-right: max(var(--jn-gutter), env(safe-area-inset-right));
  padding-block: var(--jn-section-y-sm) 0;
}
body:has(.jn-legal) .page-header .entry-title,
body.cmplz-document .page-header .entry-title {
  max-width: none;
  padding-inline: 0;
  font-family: var(--jn-font-head);
  font-size: var(--jn-t-h1);
  font-weight: 600;
  line-height: var(--jn-lh-tight);
  font-optical-sizing: auto;
  color: var(--jn-heading);
  margin: 0 0 var(--jn-s-6);
}

/* --- 24.4 Títulos internos ----------------------------------------------- */
html :is(.jn-legal, [id="cmplz-document"]) :is(h2, h3, h4) {
  font-family: var(--jn-font-head);
  color: var(--jn-heading);
  line-height: var(--jn-lh-head);
  font-weight: 600;
  font-optical-sizing: auto;
}
html :is(.jn-legal, [id="cmplz-document"]) h2 {
  font-size: clamp(1.375rem, 1.2rem + 0.7vw, 1.625rem);
  margin: var(--jn-s-7) 0 var(--jn-s-3);
}
html :is(.jn-legal, [id="cmplz-document"]) h3 {
  font-size: var(--jn-t-h3);
  margin: var(--jn-s-6) 0 var(--jn-s-2);
}
html :is(.jn-legal, [id="cmplz-document"]) h4 {
  font-size: var(--jn-t-body);
  letter-spacing: 0.01em;
  margin: var(--jn-s-5) 0 var(--jn-s-2);
}
/* O primeiro filho nunca paga margem de cima: quem dá o respiro é o título da
   página, logo acima. Vale para o nosso HTML (onde o primeiro é a linha de
   data) e para o documento do Complianz (onde é um h2). */
html :is(.jn-legal, [id="cmplz-document"]) > :first-child { margin-block-start: 0; }

/* --- 24.5 Corpo, listas e ênfase ----------------------------------------- */
html :is(.jn-legal, [id="cmplz-document"]) p { margin: 0 0 var(--jn-s-4); }
html :is(.jn-legal, [id="cmplz-document"]) :is(ul, ol) {
  margin: 0 0 var(--jn-s-4);
  padding-inline-start: var(--jn-s-5);
}
html :is(.jn-legal, [id="cmplz-document"]) li { margin-block-end: var(--jn-s-2); }
html :is(.jn-legal, [id="cmplz-document"]) li::marker { color: var(--jn-line-strong); }
/* ⚠️ ZERO opacity, como no ponto travado #17. A ênfase vem de PESO e do
   marrom de título — medido 5,64:1 sobre o creme, aprova AA em corpo. */
html :is(.jn-legal, [id="cmplz-document"]) :is(strong, b) {
  font-weight: 700;
  color: var(--jn-heading);
}
html :is(.jn-legal, [id="cmplz-document"]) hr {
  height: 0;
  border: 0;
  border-top: 1px solid var(--jn-line);
  margin: var(--jn-s-7) 0;
}
/* A linha de data logo abaixo do título. Classe de conteúdo, nossa, no HTML
   colado — não depende de nada do tema. */
html .jn-legal .jn-legal__meta {
  font-size: var(--jn-t-small);
  letter-spacing: 0.02em;
  color: var(--jn-text);
  margin: 0 0 var(--jn-s-6);
}

/* --- 24.6 Links -----------------------------------------------------------
   Adversários medidos: `a { color:#c36 }` (0,0,1) do reset e
   `.page-content a { text-decoration: underline }` (0,1,1) do theme.css.
   A cadeia daqui fecha em (0,1,2) e vence os dois por peso.
   Terracota sobre creme = 5,67:1 (AA). */
html :is(.jn-legal, [id="cmplz-document"]) a {
  color: var(--jn-action);
  text-decoration: underline;
  text-underline-offset: 0.18em;
  text-decoration-thickness: 1px;
  text-decoration-color: var(--jn-line-strong);
  transition: color var(--jn-d-fast) var(--jn-ease-soft),
              text-decoration-color var(--jn-d-fast) var(--jn-ease-soft);
}
html :is(.jn-legal, [id="cmplz-document"]) a:hover {
  color: var(--jn-heading);
  text-decoration-color: currentColor;
}
/* ⚠️ O anel de foco do §6 está escopado em `.e-con:where(.e-parent[id])` e
   NÃO chega aqui — é a mesma armadilha anunciada no §22.0 e no §23.0 para o
   cabeçalho e o rodapé. Apagar isto por parecer repetição devolve o foco ao
   anel do navegador. */
body:has(.jn-legal) :is(a, button, summary, [tabindex]):focus-visible,
body.cmplz-document :is(a, button, summary, [tabindex]):focus-visible {
  outline: 3px solid var(--jn-terracotta);
  outline-offset: 3px;
  border-radius: var(--jn-r-media);
}

/* --- 24.7 O que o §24 NÃO disputa, e a única linha de defesa que fica ----
   ⚠️ AQUI EU ERREI E CORRIGI NA MESMA RODADA — vale mais como aviso do que
   como CSS. A primeira versão deste bloco estilizava uma `<table>`, e o
   preview trazia uma `<table>` que EU inventei. A partir dela cheguei a
   calibrar um `table-layout: fixed` "medido a 390px". Era medição de verdade,
   sobre marcação de mentira: **o Complianz não usa tabela.** Lido nos
   templates do plugin instalado:

       templates/cookiepolicy/purpose_row.php   <div class="cookies-per-purpose">
                                                  <div class="purpose">…
       templates/cookiepolicy/cookies_row.php   <div class="name-header">…<div class="name">
                                                <div class="retention-header">…<div class="retention">
                                                <div class="function-header">…<div class="function">

   É uma GRADE de `<div>`, e quem a estiliza é o próprio
   `assets/css/document-grid.css` do plugin, que já traz o responsivo dele:
   `1fr 1fr 1fr` acima de 600px e `100px 1fr` abaixo. Ou seja, a peça mais
   complexa da política de cookies **não é nossa e não precisa ser**. É a
   lição da Rev.02 outra vez, do avesso: lá o preview reproduzia menos do que
   o site tinha; aqui ele reproduzia uma coisa que o site nunca teria.

   Então este bloco não disputa `.cmplz-dropdown`, `.cookies-per-purpose`,
   `.cmplz-categories` nem o banner. O painel do Complianz manda neles, e deve
   mandar. Se depois do wizard alguma coisa aqui brigar com o plugin, a saída
   é APAGAR a regra, não aumentar o peso. */

/* A única linha que fica, e a razão dela.
   ⚠️ `anywhere`, e NÃO `break-word`. Só `anywhere` entra na conta de
   MIN-CONTENT, e é o min-content que decide se um bloco empurra a página.
   Medido a 390px com um nome de cookie real (`woocommerce_cart_hash`, que não
   tem onde quebrar): sem esta linha o mínimo do bloco ia a 385px contra 315px
   disponíveis, ou seja 40px de rolagem horizontal no SITE INTEIRO; com ela o
   mínimo cai para 107px. `break-word` não resolve — ele quebra na hora de
   pintar e a largura mínima continua a mesma.

   Os alvos não foram escolhidos a esmo: `.name`, `.retention` e `.function`
   são os nomes de classe do `cookies_row.php` acima, e `th`/`td` cobrem uma
   tabela colada à mão numa página legal nossa. Nada aqui força layout: só
   autoriza a quebra quando a palavra não cabe.

   E NÃO trocar por `display: block; overflow-x: auto`: isso vira região
   rolável sem foco de teclado (o axe reprova por
   `scrollable-region-focusable`) e não dá para pôr `tabindex` por CSS numa
   marcação que é do plugin. Seria trocar um defeito por outro. */
html [id="cmplz-document"] :is(th, td, .name, .retention, .function) {
  overflow-wrap: anywhere;
}

/* Tabela: para uma tabela colada À MÃO numa página legal nossa. O Complianz
   não emite nenhuma (ver acima), então isto NÃO é a política de cookies. */
html [id="cmplz-document"] table,
html .jn-legal table {
  width: 100%;
  border-collapse: collapse;
  margin: 0 0 var(--jn-s-4);
  font-size: var(--jn-t-small);
}
html [id="cmplz-document"] :is(th, td),
html .jn-legal :is(th, td) {
  text-align: left;
  padding: var(--jn-s-2) var(--jn-s-3);
  border-bottom: 1px solid var(--jn-line);
  vertical-align: top;
}
html [id="cmplz-document"] th,
html .jn-legal th {
  font-family: var(--jn-font-body);
  font-weight: 700;
  color: var(--jn-heading);
}
html .jn-legal :is(th, td) { overflow-wrap: anywhere; }
html [id="cmplz-document"] summary { cursor: pointer; }

/* --- 24.9 O CSS do PLUGIN vence o §24 na página dele (lock #29) ----------
   ⚠️ CORRIGE UMA PREMISSA DO §24.0, E SÓ DEU PARA MEDIR EM 19/09/2026, com a
   Cookie Policy JÁ GERADA pelo wizard (antes disso a página não existia).

   O §24.0 escolheu `[id="cmplz-document"]` (0,1,0) para que as duas metades da
   regra pesassem igual. A conta estava certa; a premissa escondida, não: ela
   supunha que do lado do Complianz não havia adversário. Há. O plugin carrega
   `assets/css/document.min.css` e escreve no PRÓPRIO id, em (1,0,0) e (1,0,1):

       #cmplz-document                 max-width 800px · font-size 14px
       #cmplz-document h2, h3          font-size · text-align · text-transform
                                       · margin-top · padding/margin-bottom
       #cmplz-document h4, h5          font-size · text-align · margens
       #cmplz-document p, li, td       font-size · margin-top
       #cmplz-document a:focus         border 2px + padding

   MEDIDO na página ao vivo, com o §24 inteiro carregado: caixa a **800px**
   (não os ~625 da medida), corpo e listas a **14px** (não var(--jn-t-body)) e
   h2 a **22px** (não os 26 do clamp). Fonte, cor e link eram nossos porque o
   plugin não os declara. É o Aprendizado #19 outra vez: o seletor CASA e
   PERDE, em silêncio, e o resto da página fica certo o bastante para o defeito
   passar por escolha de desenho.

   ⚠️ POR QUE AQUI O ID É OBRIGATÓRIO, e o §24.0 continua valendo para o resto:
   não existe soma de classes que vença (1,0,0) — só id, ou !important. Então
   esta é a ÚNICA parte do §24 escrita com `#cmplz-document`, e ela declara
   apenas as propriedades DISPUTADAS acima; os valores continuam morando nos
   blocos 24.2/24.4/24.5, e aqui só são repetidos. É a doutrina do §18.12:
   vencer por PESO, nunca por ordem. `html body #cmplz-document …` fecha em
   (1,0,3)+ e ganha do (1,0,1) do plugin sem depender de quem carrega primeiro.

   ⚠️ NÃO apagar por parecer repetição do §24.2/24.4/24.5: sem este bloco a
   política de cookies volta a 800px e 14px, e nada acusa. E NÃO estender aos
   `.jn-legal`: lá o adversário não existe e o peso extra só atrapalharia. */
html body #cmplz-document {
  max-width: calc(var(--jn-legal-measure) + 2 * var(--jn-gutter));
  font-size: var(--jn-t-body);
  margin-block-start: 0;
}
html body #cmplz-document :is(h2, h3, h4, h5) {
  text-align: left;
  text-transform: none;
  padding-bottom: 0;
}
html body #cmplz-document h2 {
  font-size: clamp(1.375rem, 1.2rem + 0.7vw, 1.625rem);
  margin: var(--jn-s-7) 0 var(--jn-s-3);
}
html body #cmplz-document h3 {
  font-size: var(--jn-t-h3);
  margin: var(--jn-s-6) 0 var(--jn-s-2);
}
html body #cmplz-document :is(h4, h5) {
  font-size: var(--jn-t-body);
  margin: var(--jn-s-5) 0 var(--jn-s-2);
}
html body #cmplz-document :is(p, li, td) {
  font-size: var(--jn-t-body);
  margin-top: 0;
}
html body #cmplz-document > :first-child { margin-block-start: 0; }
/* O plugin põe BORDA no foco (`#cmplz-document a:focus`, 2px + padding), que
   empurra o texto ao redor. Quem marca o foco aqui é o anel do §24.6; esta
   linha só desarma a borda, e é por isso que ela repete o `outline` do §6. */
html body #cmplz-document a:focus {
  border: 0;
  padding: 0;
}

/* --- 24.8 Reduced motion ------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  html :is(.jn-legal, [id="cmplz-document"]) a { transition: none !important; }
}
