/* ==========================================================================
   Fashion Finder - Design System
   Premium Minimalist & Editorial Aesthetic (Clean, Minimalist, Belo e Estiloso)
   Derived from ui-ux-pro-max Guidelines
   ========================================================================== */

@import url('https://fonts.googleapis.com/css2?family=Playfair+Display:ital,wght@0,400;0,500;0,600;1,500&family=Cormorant+Garamond:ital,wght@0,300;0,400;1,300;1,400&family=DM+Sans:ital,wght@0,300;0,400;0,500;0,600;1,400&display=swap');

:root {
  /* Paleta oficial Fashion Finder — brand kit v1.1 (Agosto 2026), ver
     docs/BRAND_KIT.md. Marfim/Tinta/Dourado FF são as 3 cores primárias
     obrigatórias; Ouro UI é o apoio definido especificamente pra
     interface do app (não confundir com o Dourado FF do logo — ver
     02_Cores/cores.md do kit: "Ouro UI... nunca substitui o Dourado FF
     no logo", e o inverso também: o Dourado FF de logo é forte demais
     pra UI de botão/foco repetida). */
  --bg-main: #FAF7F2;        /* Marfim — fundo principal light */
  --bg-card: #FFFFFF;
  --bg-card-hover: #F4F1EA;

  --primary: #A8843A;        /* Ouro UI — apoio de interface (CTAs, foco, links) */
  --primary-glow: rgba(168, 132, 58, 0.15);
  --gold-marca: #CDA349;     /* Dourado FF — SÓ pra logo/momentos de marca, nunca CTA repetido */
  --secondary: #1A1410;      /* Tinta — texto sobre fundo claro */
  --secondary-glow: rgba(26, 20, 16, 0.08);

  /* Apoio editorial — no máx. 1 por peça, nunca substituem o Dourado FF (ver kit) */
  --claret: #7A3A42;
  --verde-bouteille: #2A4840;

  --success: #15803d;
  --warning: #b45309;

  --text-main: #1A1410;      /* Tinta                    — 15.9:1 sobre Marfim */
  --text-muted: #57504A;     /*                            7.4:1  */
  /* #7A736C dava 4.37:1 sobre o Marfim — reprovava AA (4.5) em texto normal,
     e é justamente a cor da legenda de loja e do texto de apoio. Escurecido
     o mínimo necessário para passar, mantendo o cinza quente do kit. */
  --text-dim: #6E675F;       /*                            5.3:1  */
  /* Ouro UI como TEXTO dá 3.26:1 e reprova AA. --primary continua sendo a cor
     de preenchimento/borda/foco definida pelo brand kit; --primary-ink é a
     versão legível para quando o ouro precisa virar texto sobre o Marfim. */
  --primary-ink: #8A6B26;    /*                            4.7:1  */
  --border-glass: #E7E1D6;
  --line-strong: #D8D1C4;    /* borda de contorno de controle, 1 nível acima */
  --border-active: #A8843A;  /* Ouro UI */

  /* Typografia — brand kit: Playfair Display (títulos), Cormorant
     Garamond (serif editorial/subtítulo — DIFERENTE de "Cormorant"
     solto, que era o que estava aqui antes), DM Sans (corpo/UI). */
  --font-display: 'Playfair Display', serif;
  --font-editorial: 'Cormorant Garamond', serif;
  --font-sans: 'DM Sans', sans-serif;
  
  /* Shadows & Radius */
  --shadow-sm: 0 2px 8px rgba(26, 20, 16, 0.04);
  --shadow-md: 0 8px 30px rgba(26, 20, 16, 0.06);
  --shadow-glow: 0 0 20px var(--primary-glow);
  
  --radius-sm: 4px;
  --radius-md: 12px;
  --radius-lg: 20px;
  --radius-full: 9999px;

  --transition: all 0.25s cubic-bezier(0.25, 0.8, 0.25, 1);

  /* ======================================================================
     ESCALAS — a base tinha 27 tamanhos de fonte soltos (de 0.5rem a 2.2rem,
     vários abaixo de 12px) e ~19 valores de espaçamento sem sistema. Estas
     duas escalas são a fonte única a partir daqui.

     CONTRATO PARA A LANDING PAGE PÚBLICA e qualquer superfície nova:
     importe este arquivo e use estas variáveis. Não redefina paleta, fonte
     nem espaçamento em outro CSS — a paleta e as famílias vêm do brand kit
     (docs/BRAND_KIT.md) e estão acima; estas escalas são a camada de UI.
     ====================================================================== */

  /* Tipografia (16px base). --fs-micro só em caixa alta com tracking. */
  --fs-micro: 0.6875rem;   /* 11px */
  --fs-xs:    0.75rem;     /* 12px */
  --fs-sm:    0.8125rem;   /* 13px */
  --fs-md:    0.9375rem;   /* 15px — corpo */
  --fs-lg:    1.0625rem;   /* 17px — preço */
  --fs-xl:    1.375rem;    /* 22px — título de seção */
  --fs-2xl:   1.75rem;     /* 28px — título de tela */
  --fs-3xl:   2.375rem;    /* 38px — hero */

  --lh-tight: 1.15;
  --lh-snug: 1.35;
  --lh-normal: 1.6;
  --tracking-caps: 0.09em;

  /* Espaço, base 4 */
  --sp-1: 4px;   --sp-2: 8px;   --sp-3: 12px;  --sp-4: 16px;  --sp-5: 24px;
  --sp-6: 32px;  --sp-7: 48px;  --sp-8: 64px;  --sp-9: 96px;

  /* Layout */
  --shell-max: 1120px;
  --gutter: var(--sp-5);
  --tap: 44px;             /* alvo mínimo de toque */

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --dur: 0.2s;
}

/* Base Styles */
* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  -webkit-tap-highlight-color: transparent;
}

body {
  background-color: var(--bg-main);
  color: var(--text-main);
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  line-height: var(--lh-normal);
  -webkit-font-smoothing: antialiased;
  min-height: 100vh;
  min-height: 100dvh;
  overflow-x: hidden;
}

/* Casca do app.
   A moldura de celular simulada (bezel de 12px + notch + fundo cinza atrás)
   saiu: no desktop ela transformava o produto num mockup de portfólio dentro
   de 440px e jogava fora o resto da tela; e mesmo no celular ela custava
   altura de conteúdo. Agora é uma coluna responsiva de verdade.

   O .app-content continua sendo o elemento que rola (app shell de verdade),
   porque app.js zera `viewport.scrollTop` a cada troca de view. */
.device-container {
  width: 100%;
  height: 100vh;
  height: 100dvh;
  max-width: none;
  max-height: none;
  background-color: var(--bg-main);
  border: none;
  border-radius: 0;
  box-shadow: none;
  position: relative;
  display: flex;
  flex-direction: column;
}

.device-notch { display: none; }

/* App Header styling */
header {
  /* 36px de topo existiam pra desviar do notch simulado, que não existe mais */
  padding: var(--sp-3) var(--gutter);
  padding-top: max(var(--sp-3), env(safe-area-inset-top, 0px));
  background: var(--bg-main);
  display: flex;
  gap: var(--sp-3);
  align-items: center;
  border-bottom: 1px solid var(--border-glass);
  z-index: 100;
  flex-shrink: 0;
}

/* A marca nunca é o elemento que encolhe: o badge de estado e o botão
   interno é que cedem espaço. Antes o wordmark truncava em "FashionFi…". */
.brand {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  margin-right: auto;
  flex-shrink: 0;
}

.brand-logo {
  width: 28px;
  height: 28px;
  background: var(--secondary);
  border-radius: var(--radius-sm);
  display: flex;
  justify-content: center;
  align-items: center;
  padding: 5px;
}
.brand-logo img {
  width: 100%;
  height: 100%;
  object-fit: contain;
}

.brand-name {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-xl);
  line-height: 1;
  color: var(--secondary);
  letter-spacing: 0.01em;
  white-space: nowrap;
}

/* App Scrollable Content */
.app-content {
  flex: 1;
  overflow-y: auto;
  /* Bug real corrigido: 90px fixo não sobrava margem nenhuma pra
     env(safe-area-inset-bottom) (barra de gestos/home indicator em
     iPhone) — em aparelho real, o bottom-nav (76px + o padding próprio
     dele) mais a área segura passava de 90px, e o fim do último card
     ficava atrás da navegação. Agora soma a área segura de verdade. */
  padding: var(--sp-5) var(--gutter) calc(96px + env(safe-area-inset-bottom, 0px));
  position: relative;
  scrollbar-width: none; /* Hide scrollbar */
}
.app-content::-webkit-scrollbar {
  display: none;
}

/* A coluna de leitura tem largura máxima: sem isso, no desktop o texto
   corrido atravessaria 1400px e nenhuma linha seria legível. */
.view-panel {
  width: 100%;
  max-width: var(--shell-max);
  margin-inline: auto;
}

/* Bottom Navigation Bar */
.bottom-nav {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  height: 76px;
  background: rgba(250, 247, 242, 0.95);
  backdrop-filter: blur(12px);
  border-top: 1px solid var(--border-glass);
  display: flex;
  justify-content: space-around;
  align-items: center;
  /* Mesma soma do app-content acima — a barra em si também precisa
     respeitar a área segura, não só empurrar o conteúdo pra cima dela. */
  padding-bottom: calc(12px + env(safe-area-inset-bottom, 0px));
  z-index: 200;
}

.nav-item {
  flex: 1 1 0;
  max-width: 160px;
  min-height: var(--tap);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  background: none;
  border: none;
  color: var(--text-dim);
  font-family: var(--font-sans);
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: 0.03em;
  cursor: pointer;
  transition: color var(--dur) var(--ease);
  padding: var(--sp-1) var(--sp-3);
  border-radius: var(--radius-sm);
}

.nav-item svg {
  width: 19px;
  height: 19px;
  stroke: currentColor;
  stroke-width: 1.7;
  fill: none;
  transition: var(--transition);
}

.nav-item:hover {
  color: var(--secondary);
}

/* Ativo em Tinta, não em Ouro UI: o ouro como texto de 11px dava 3.26:1
   sobre o Marfim e reprovava AA — e a nav não é o lugar do sotaque da marca. */
.nav-item.active {
  color: var(--secondary);
}

.nav-item.active svg {
  stroke-width: 2.1;
}

/* ==========================================================================
   View 1: Landing (Upload Look)
   ========================================================================== */
.view-panel {
  display: none;
  animation: fadeIn 0.4s cubic-bezier(0.215, 0.610, 0.355, 1) forwards;
}

.view-panel.active {
  display: block;
}

@keyframes fadeIn {
  from { opacity: 0; transform: translateY(6px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Fotografia editorial — protagonista (ver index.html para a justificativa
   do asset reaproveitado e do LCP). Full-bleed no mobile: cancela só o
   padding horizontal de .app-content (a única coisa entre ela e a borda
   da tela), nunca o padding vertical/header, que continuam intocados. */
.landing-hero {
  position: relative;
  margin-inline: calc(-1 * var(--gutter));
  width: calc(100% + 2 * var(--gutter));
  aspect-ratio: 4 / 5;
  max-height: 62vh;
  min-height: 320px;
  overflow: hidden;
  background: var(--bg-card-hover);
}
.landing-hero img {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Sobe o foco para ombro/rosto — a composição editorial tem o ponto de
     interesse no terço superior, não no centro geométrico do quadro. */
  object-position: 50% 22%;
}
/* Transição para o off-white da seção de texto, sem corte abrupto —
   mesma cor de fundo do resto do app, nenhuma cor nova. */
.landing-hero::after {
  content: '';
  position: absolute;
  inset: auto 0 0 0;
  height: 40%;
  background: linear-gradient(180deg, rgba(250, 247, 242, 0) 0%, var(--bg-main) 92%);
}

.hero-text {
  text-align: center;
  margin: var(--sp-5) auto var(--sp-5);
  max-width: 32ch;
}

/* Serifada itálica nas 3 linhas (headline emocional da tela — o resto da
   tela é sans, ver .upload-desc/.section-title). font-size fluido: uma
   escala continua entre mobile e desktop lê melhor numa headline quebrada
   em linhas curtas do que 3 saltos discretos de breakpoint. */
.hero-title {
  font-family: var(--font-editorial);
  font-style: italic;
  font-size: clamp(2.2rem, 8vw, 3.4rem);
  font-weight: 400;
  line-height: 1.08;
  letter-spacing: -0.01em;
  color: var(--secondary);
}

.hero-title span {
  /* A foto já é o elemento de cor da tela — um quarto acento (ouro) aqui
     competiria; as 3 linhas ficam na mesma tinta. */
  color: inherit;
  font-style: inherit;
  font-weight: inherit;
}

.hero-subtitle {
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  color: var(--text-muted);
  margin: var(--sp-3) auto 0;
  line-height: var(--lh-normal);
  font-weight: 400;
}

/* Área de entrada — deixou de parecer uma caixa/dropzone (a referência não
   tem borda nem card ao redor do CTA); continua sendo o MESMO #drop-zone
   funcional (drag/drop cobre toda a área, só sem a moldura tracejada). */
.upload-card {
  background: none;
  border: 0;
  padding: var(--sp-2) 0 0;
  text-align: center;
  cursor: pointer;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
}

/* Ícone genérico de upload e o rótulo "Arraste uma foto..." saem de cena
   visualmente (a foto editorial acima já comunica o que o app faz) — o
   texto continua no DOM (leitor de tela, e o hint de arrastar reaparece
   pequeno em .upload-desc abaixo), só não ocupa mais o topo do cartão. */
.upload-icon-container,
.upload-label {
  display: none;
}

/* Dica de arrastar, discreta, DEPOIS dos botões (ver order em
   .upload-actions) — informação real (drag&drop continua funcionando),
   só não compete mais com o CTA. */
/* Escondida por padrão (mobile/tablet — QA real em iPhone: drag&drop não é
   ação útil em touch, é ruído): "ou arraste uma foto aqui" só reaparece em
   desktop (ver @media min-width:960px), onde mouse+drag&drop faz sentido.
   display:none, não visibility:hidden — o objetivo é recuperar o espaço
   vertical que ela ocupava, não só escondê-la visualmente. Não é lida por
   nenhum handler em app.js (puramente textual). */
.upload-desc {
  display: none;
  order: 2;
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-top: 2px;
}

.upload-actions {
  order: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  width: 100%;
  max-width: 360px;
}

.btn-camera {
  justify-content: center;
  background: var(--secondary);
  color: #FFF;
  border: 1px solid var(--secondary);
  min-height: var(--tap);
  padding: 0 var(--sp-5);
  border-radius: var(--radius-full);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-sm);
  letter-spacing: 0.01em;
  text-transform: none;   /* CAIXA ALTA num CTA de 12px era grito, não ênfase */
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  cursor: pointer;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
  box-shadow: none;
}

.btn-camera:hover { background: #000; }

/* Ação principal — "Fotografar" (#camera-btn). Mesmo botão de sempre, só
   maior e sozinho na linha (ver .upload-actions em coluna). */
.btn-camera-principal {
  width: 100%;
  min-height: 52px;
  font-size: var(--fs-md);
}

/* Ação secundária — "ou escolha da galeria" (#galeria-btn): virou link de
   texto simples, sem pílula/ícone, mesmo elemento/id/listener de sempre. */
.btn-camera.btn-camera-secundario {
  width: auto;
  background: none;
  border: 0;
  color: var(--text-dim);
  text-decoration: underline;
  text-underline-offset: 3px;
  font-weight: 400;
  padding: var(--sp-2) var(--sp-3);
}
.btn-camera.btn-camera-secundario:hover {
  background: none;
  color: var(--text-main);
}

/* Busca por descrição (texto livre) — caminho SEPARADO da foto, mesmo
   nível hierárquico do upload (nunca um Google genérico: um divisor "ou"
   simples, igual ao que já existe entre "Fotografar"/"escolher da
   galeria", deixa claro que são duas formas de fazer a MESMA coisa). */
.text-search-divider {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  color: var(--text-dim);
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: 0.08em;
  margin: var(--sp-2) 0;
}
.text-search-divider::before,
.text-search-divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background: var(--border-glass);
}

.text-search-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-3);
  text-align: center;
}

.text-search-label {
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-main);
  margin: 0;
}

.text-search-row {
  display: flex;
  width: 100%;
  max-width: 360px;
  gap: var(--sp-2);
}

.text-search-input {
  flex: 1;
  min-width: 0;
  min-height: var(--tap);
  padding: 0 var(--sp-4);
  border: 1px solid var(--line-strong);
  border-radius: var(--radius-full);
  background: var(--bg-card);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  color: var(--text-main);
}
.text-search-input:focus {
  outline: none;
  border-color: var(--secondary);
}
.text-search-input::placeholder { color: var(--text-dim); }

.btn-text-search {
  flex: 0 0 auto;
  justify-content: center;
  background: var(--secondary);
  color: #FFF;
  border: 1px solid var(--secondary);
  min-height: var(--tap);
  padding: 0 var(--sp-4);
  border-radius: var(--radius-full);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-sm);
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  cursor: pointer;
  transition: background var(--dur) var(--ease);
}
.btn-text-search:hover { background: #000; }
.btn-text-search:disabled { opacity: 0.6; cursor: default; }

.text-search-error {
  color: var(--danger, #B3261E);
  font-size: var(--fs-xs);
  margin: 0;
}
.btn-camera-secundario svg { display: none; }

.btn-camera svg {
  width: 16px;
  height: 16px;
  stroke: currentColor;
  stroke-width: 1.8;
  fill: none;
}

/* Um único aviso de privacidade, no rodapé. Antes havia DOIS blocos na mesma
   tela dizendo coisas diferentes: este (envio ao Gemini) logo abaixo do
   upload e o .privacy-toast (LGPD) no fim — e eles se contradiziam. */
.privacy-notice {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  text-align: center;
  margin: var(--sp-3) auto 0;
  max-width: 52ch;
  line-height: var(--lh-snug);
}

.privacy-notice a {
  color: var(--primary-ink);
  text-decoration: underline;
  text-underline-offset: 2px;
}

#file-input {
  display: none;
}

/* Preset sections (popular style gallery) */
.presets-section {
  margin-top: var(--sp-8);
}

/* Rótulo quieto — os presets são um atalho funcional (pular o upload), não
   parte da narrativa emocional da hero; escopado a .presets-section para
   não tocar .section-title nas outras 6 telas que a reusam. */
.presets-section .section-title {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  color: var(--text-dim);
}

.section-title {
  font-family: var(--font-display);
  font-weight: 500;
  font-size: var(--fs-xl);
  line-height: var(--lh-tight);
  margin-bottom: var(--sp-4);
  color: var(--secondary);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}

/* O tracinho dourado antes de todo título de seção era um enfeite repetido
   em cinco telas; a hierarquia já vem do tamanho e da família. */
.section-title::before { content: none; }

.presets-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-3);
}

.preset-card {
  background: var(--bg-card-hover);
  border: 0;
  border-radius: var(--radius-md);
  overflow: hidden;
  cursor: pointer;
  position: relative;
  transition: opacity var(--dur) var(--ease);
  aspect-ratio: 3 / 4;
}

.preset-card:hover {
  opacity: 0.92;
  transform: none;
  box-shadow: none;
}

.preset-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: var(--transition);
}

.preset-card:hover img {
  transform: scale(1.03);
}

.preset-overlay {
  position: absolute;
  bottom: 0;
  left: 0;
  right: 0;
  padding: 12px;
  background: linear-gradient(0deg, rgba(26, 20, 16, 0.95) 0%, rgba(26, 20, 16, 0) 100%);
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
}

.preset-title {
  font-family: var(--font-sans);
  font-weight: 500;
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: white;
  letter-spacing: 0.01em;
  margin-top: 2px;
}

/* A etiqueta "BLAZERS/CALÇAS/SAPATOS" saiu: ouro de 9px sobre foto, ilegível,
   e repetia o que a própria imagem e o título já dizem. */
.preset-tag { display: none; }

/* Aviso LGPD: virou nota de rodapé, não caixa de alerta com barra dourada.
   O conteúdo agora vive em .privacy-notice (um aviso só na tela). */
.privacy-toast {
  margin-top: var(--sp-7);
  padding-top: var(--sp-4);
  background: none;
  border: 0;
  border-top: 1px solid var(--border-glass);
  border-radius: 0;
  display: flex;
  gap: var(--sp-2);
  align-items: flex-start;
}

.privacy-toast svg {
  width: 14px;
  height: 14px;
  margin-top: 3px;
  stroke: var(--text-dim);
  stroke-width: 1.6;
  fill: none;
  flex-shrink: 0;
}

.privacy-text {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  line-height: var(--lh-snug);
}
.privacy-text strong { color: var(--text-muted); font-weight: 600; }

/* ==========================================================================
   View 2: Scanning & AI Pipeline
   ========================================================================== */
/* O preview mostra a foto INTEIRA. Antes era `aspect-ratio: 0.85` fixo +
   `object-fit: cover`: num retrato 9:16 isso escondia 18% do topo e 18% da
   base sem avisar — o sapato sumia da tela enquanto a IA continuava
   analisando os pixels dele. A usuária via a IA "procurar um sapato que nem
   aparece na foto".

   Agora o container assume a proporção da própria imagem (--preview-ar,
   escrita por app.js quando a foto carrega), então `contain` preenche
   exatamente e não sobra tarja. Os limites de altura/largura só entram em
   fotos extremas — e aí `contain` gera tarja em vez de cortar. */
.scanning-canvas-wrapper {
  position: relative;
  width: 100%;
  /* aspect-ratio DINÂMICO (Agente A, testado contra 8 formatos × 5
     containers): uma proporção fixa (ex. 3/4) reintroduz o corte
     silencioso em qualquer foto que não seja 3:4 — exatamente o bug desta
     rodada. --preview-ar é escrito por app.js a partir da foto real. */
  aspect-ratio: var(--preview-ar, 0.85);
  max-height: 68vh;      /* retrato muito longo não empurra a página inteira */
  min-height: 180px;     /* paisagem muito larga não vira uma faixa fina */
  max-width: 520px;      /* Agente C: não deixa o preview dominar telas largas */
  margin-inline: auto;
  background: var(--bg-main);
  border-radius: var(--radius-lg);
  overflow: hidden;
  border: 0;
}

/* `contain` em vez de `cover`: a usuária precisa ver a foto que ela mandou
   INTEIRA — com `cover` a peça de interesse podia estar justamente na parte
   cortada, e as caixas de detecção ficavam desalinhadas com o que ela via.
   O grayscale(10%) saiu: dessaturar a roupa numa busca por cor é
   contraproducente. */
.scanning-image {
  display: block;
  width: 100%;
  height: 100%;
  /* contain, nunca cover: preserva a proporção e não corta nada em silêncio.
     Sobrar tarja é preferível a esconder conteúdo que a IA está analisando.
     filter: none — dessaturar a roupa numa busca por cor é contraproducente
     (Agente C; ambos os lados do merge concordam, ver comentário acima). */
  object-fit: contain;
  filter: none;
}

/* Bounding boxes — agora são várias e simultâneas, vindas da detecção real.
   O spotlight de 1000px do estilo antigo empilhava e escurecia tudo quando
   havia mais de uma caixa; aqui cada caixa se destaca por si. */
/* As caixas vêm em % da IMAGEM, não do container. Enquanto o container tinha
   proporção fixa e a imagem era cortada, os dois espaços não coincidiam e a
   caixa "Sapato" era desenhada em cima da calça. app.js agora ajusta estas
   medidas para o retângulo realmente pintado pela imagem, então 79% aqui é
   79% da foto. */
.bounding-boxes-container {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
  z-index: 50;
  pointer-events: none;
}

.bounding-box {
  position: absolute;
  border: 1.5px solid var(--primary);
  border-radius: var(--radius-sm);
  background: rgba(168, 132, 58, 0.06);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.35), 0 2px 12px rgba(26, 20, 16, 0.25);
  cursor: pointer;
  pointer-events: auto;
  opacity: 0;
  transform: scale(0.94);
  animation: entradaCaixa 0.45s cubic-bezier(0.22, 1, 0.36, 1) forwards;
  transition: background 0.2s ease, box-shadow 0.2s ease;
}

.bounding-box:hover,
.bounding-box:focus-visible {
  background: rgba(168, 132, 58, 0.16);
  box-shadow: 0 0 0 1px rgba(255, 255, 255, 0.5), 0 4px 18px rgba(26, 20, 16, 0.35);
}

/* Caixa aproximada (sem IA): tracejada, para não passar por detecção real */
.bounding-box-aproximada {
  border-style: dashed;
  border-color: var(--text-dim);
  background: rgba(120, 113, 108, 0.08);
}

.bounding-box-label {
  position: absolute;
  top: -9px;
  left: 6px;
  background: var(--secondary);
  color: var(--bg-main);
  font-family: var(--font-sans);
  font-size: 0.52rem;
  font-weight: 600;
  padding: 2px 7px;
  border-radius: var(--radius-full);
  letter-spacing: 0.4px;
  white-space: nowrap;
  max-width: calc(100% - 12px);
  overflow: hidden;
  text-overflow: ellipsis;
}

.bounding-box-aproximada .bounding-box-label {
  background: var(--text-dim);
}

@keyframes entradaCaixa {
  to { opacity: 1; transform: scale(1); }
}

.scanning-dica {
  text-align: center;
  font-size: 0.68rem;
  color: var(--text-dim);
  margin-top: 10px;
  letter-spacing: 0.2px;
}

/* Aviso de espera longa na tela de escaneamento. Reaproveita o MESMO tom
   âmbar de aviso que .aviso-sem-match-forte / .estado-categoria-indeterminada-aviso
   / .step-item.failed já usam (var(--warning)) — deliberadamente nenhuma cor
   de alarme nova: esperar mais não é erro, é só demora, e um vermelho novo
   diria à usuária que algo quebrou quando nada quebrou. */
.scanning-espera {
  text-align: center;
  font-size: 0.68rem;
  line-height: 1.5;
  color: var(--warning);
  margin-top: 14px;
  letter-spacing: 0.2px;
}

.scanning-espera[hidden] { display: none; }

/* O movimento contínuo durante a espera de rede é requisito real de
   performance percebida (scripts/test-perceived-performance.js): tem que ser
   CSS puro, porque o JS está bloqueado esperando a resposta. O que muda é a
   FORMA: em vez de uma linha de laser dourada de 1px com glow — linguagem de
   ficção científica atravessando a roupa que a usuária quer enxergar — é uma
   faixa de luz larga, quase transparente, passando devagar sobre a foto.
   Mesmo sinal ("estamos trabalhando"), sem competir com a imagem. */
.laser-line {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 45%;
  background: linear-gradient(180deg,
    rgba(255, 255, 255, 0) 0%,
    rgba(255, 255, 255, 0.28) 50%,
    rgba(255, 255, 255, 0) 100%);
  box-shadow: none;
  animation: scanMotion 3.2s infinite ease-in-out;
  pointer-events: none;
  z-index: 60;
}

@keyframes scanMotion {
  0%   { top: -45%; }
  50%  { top: 100%; }
  100% { top: -45%; }
}

/* Progresso, não "pipeline". */
.pipeline-stepper {
  max-width: 520px;
  margin: var(--sp-6) auto 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
}

.step-item {
  display: flex;
  align-items: baseline;
  gap: var(--sp-3);
  opacity: 0.4;
  transition: opacity var(--dur) var(--ease);
}

.step-item.active,
.step-item.completed,
.step-item.failed {
  opacity: 1;
}

/* O indicador vira um ponto. O número dentro do círculo não informava nada:
   ninguém precisa saber que a extração de atributos é a "etapa 2 de 3". */
.step-indicator {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--border-glass);
  border: 0;
  display: block;
  font-size: 0;
  color: transparent;
  overflow: hidden;
  flex-shrink: 0;
  align-self: center;
}

.step-item.active .step-indicator {
  background: var(--primary);
}

.step-item.completed .step-indicator {
  background: var(--secondary);
}

.step-details {
  flex: 1;
  min-width: 0;
}

.step-name {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-sm);
  color: var(--text-main);
}

.step-desc {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  line-height: var(--lh-snug);
  margin-top: 2px;
}

/* Dynamic attribute chips */
.attribute-chips {
  margin-top: 8px;
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.chip {
  background: var(--bg-card-hover);
  border: 1px solid var(--border-glass);
  color: var(--text-main);
  font-size: 0.65rem;
  font-weight: 500;
  padding: 3px 8px;
  border-radius: var(--radius-sm);
  animation: popIn 0.25s cubic-bezier(0.175, 0.885, 0.32, 1.15) forwards;
}

@keyframes popIn {
  from { opacity: 0; transform: scale(0.9); }
  to { opacity: 1; transform: scale(1); }
}

/* --------------------------------------------------------------------------
   Confirmação "Foto carregada" — feedback real da Fran (ver index.html):
   bloco-irmão da foto, NUNCA position:absolute sobre ela, então não pode
   escondê-la. Compacta (uma linha), para não competir com o resto da tela.

   Rodada 2: sem CTA e sem lógica de "dismissed" — é status permanente
   enquanto a tela de scanning está ativa (some sozinha quando a view troca,
   igual a qualquer outro elemento da tela; nunca por timer/clique). -------- */
.photo-confirm-banner {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  max-width: 520px;
  margin: var(--sp-3) auto 0;
  padding: 10px var(--sp-4);
  background: var(--bg-card);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-sm);
}

.photo-confirm-icon {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  background: rgba(21, 128, 61, 0.12);
  display: flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
}
.photo-confirm-icon svg { width: 15px; height: 15px; stroke: var(--success); }

.photo-confirm-text {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: 1px;
}
.photo-confirm-title {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-sm);
  color: var(--text-main);
}
.photo-confirm-sub {
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

@media (max-width: 439px) {
  .photo-confirm-banner { padding: 8px var(--sp-3); gap: var(--sp-2); }
}

/* Instrução de toque — Rodada 2 (achado real no iPhone): não pode aparecer
   como se já houvesse peça destacada quando ainda não existe nenhuma
   bounding box na tela. Escondida por padrão; app.js liga .pecas-prontas
   no MESMO lugar onde desenharCaixas(pecas) já é chamado (nenhuma mudança
   em desenharCaixas nem em geometria — só visibilidade). visibility, não
   display: não abre/fecha espaço embaixo da foto de forma abrupta. */
.scanning-dica { visibility: hidden; }
.scanning-dica.pecas-prontas { visibility: visible; }

/* --------------------------------------------------------------------------
   Painel de IA — retangular, grande, evidente, em área PRÓPRIA (nunca
   sobre a foto ou sobre as peças identificadas). Feedback real da Fran:
   ela gostou do conceito, mas quer a linguagem da versão anterior —
   maior/retangular — em vez de um traço fino e discreto. Envolve o mesmo
   .pipeline-stepper de sempre (ids/classes internos intocados). -------- */
.ai-indicator-card {
  max-width: 520px;
  margin: var(--sp-4) auto 0;
  padding: var(--sp-4);
  background: var(--bg-card);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-md);
}

.ai-indicator-header {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-bottom: var(--sp-3);
}

/* Sinal de atividade honesto: 3 pontos pulsando em sequência, nunca um
   percentual ou barra preenchida (ver scripts/test-perceived-performance.js
   — nenhuma classe com "progress" no nome, nenhum número renderizado). */
.ai-indicator-activity {
  display: flex;
  gap: 5px;
  flex-shrink: 0;
}
.ai-indicator-activity span {
  width: 7px;
  height: 7px;
  border-radius: var(--radius-full);
  background: var(--secondary);
  opacity: 0.25;
  animation: aiActivityPulse 1.2s ease-in-out infinite;
}
.ai-indicator-activity span:nth-child(2) { animation-delay: 0.15s; }
.ai-indicator-activity span:nth-child(3) { animation-delay: 0.3s; }

@keyframes aiActivityPulse {
  0%, 100% { opacity: 0.25; transform: scale(1); }
  50% { opacity: 1; transform: scale(1.15); }
}

.ai-indicator-copy {
  display: flex;
  flex-direction: column;
  gap: 2px;
  min-width: 0;
}
.ai-indicator-title {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-md);
  color: var(--text-main);
}
.ai-indicator-sub {
  font-family: var(--font-sans);
  font-size: var(--fs-xs);
  color: var(--text-dim);
}

/* Steps: mesma lógica de sempre (.active/.completed/.failed alternados por
   app.js), compactados dentro do card retangular (Rodada 2: achado real no
   iPhone — o card ficava alto demais; aqui só padding/gap encolhem, nenhum
   texto/etapa/estado foi removido). */
.ai-indicator-card .pipeline-stepper {
  margin: 0;
  padding-top: var(--sp-3);
  border-top: 1px solid var(--border-glass);
  gap: var(--sp-2);
}

/* ==========================================================================
   View 3: Results Matching
   ========================================================================== */
.results-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 16px;
}

/* Ação secundária: texto, não caixa. Em caixa alta dentro de uma pílula,
   "NOVA BUSCA" quebrava em duas linhas em 390px. */
.btn-back-landing {
  background: none;
  border: 0;
  color: var(--text-muted);
  min-height: var(--tap);
  padding: 0 var(--sp-2);
  border-radius: var(--radius-full);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-sm);
  cursor: pointer;
  text-transform: none;
  letter-spacing: 0;
  white-space: nowrap;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  transition: color var(--dur) var(--ease);
}
.btn-back-landing:hover { color: var(--text-main); background: none; }

.btn-back-landing:hover {
  background: var(--bg-card-hover);
  border-color: var(--text-dim);
}

.btn-back-landing svg {
  width: 12px;
  height: 12px;
  stroke: var(--text-main);
  stroke-width: 2.2;
}

/* A peça consultada é uma LINHA de contexto, não um cartão. Antes esta tela
   empilhava quatro molduras (preview + abas + tuner + cada match) antes do
   primeiro produto aparecer. */
/* Rodada 3 (redesign Resultados, Figma 04): a linha de contexto virou um
   pequeno cartão editorial — mesma inspiração do bloco superior do Figma,
   MENOS o percentual de confiança ("97%"). Decisão de produto já tomada:
   ranking (finalScore) e rótulo (similaridadeVisual + vetos) não têm hoje
   uma única fonte de verdade (ver docs/EXPLAINABILITY_AUDIT.md e a
   investigação read-only da Rodada 3) — expor um número aqui repetiria o
   erro que a própria auditoria já tinha decidido evitar no rótulo de cada
   card. Sem número, sem "melhor match". */
.results-query-preview {
  display: flex;
  gap: var(--sp-4);
  background: var(--bg-card);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  padding: var(--sp-4);
  align-items: center;
  margin-bottom: var(--sp-6);
  box-shadow: none;
}

/* Busca por texto — não há foto/recorte nenhum pra mostrar. A classe é
   alternada por JS (ver executarBuscaPorTexto em app.js) só quando a
   origem da busca é texto; volta ao normal na próxima busca por foto. */
.results-query-preview--texto .results-query-image { display: none; }

.results-query-image {
  width: 56px;
  height: 72px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  background: var(--bg-card-hover);
  flex-shrink: 0;
}

.results-query-details {
  flex: 1;
  min-width: 0;
}

.results-query-eyebrow {
  font-family: var(--font-sans);
  font-size: var(--fs-micro);
  font-weight: 600;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  margin-bottom: 2px;
}

.results-query-title {
  font-family: var(--font-display);
  font-weight: 600;
  font-size: var(--fs-lg);
  line-height: var(--lh-tight);
  color: var(--secondary);
}

.results-query-tags {
  font-size: var(--fs-xs);
  color: var(--text-dim);
  margin-top: 2px;
}

/* Abas: pílulas que rolam na horizontal. O segmented control de largura
   fixa espremia "Parte de cima · Marfim" em três linhas em 390px. */
.results-tabs {
  display: flex;
  gap: var(--sp-2);
  background: none;
  border: 0;
  padding: 0;
  border-radius: 0;
  margin-bottom: var(--sp-5);
  overflow-x: auto;
  scrollbar-width: none;
}
.results-tabs::-webkit-scrollbar { display: none; }

.results-tabs .tab-btn {
  flex: 0 0 auto;
  min-height: 40px;
  border: 1px solid var(--border-glass);
  background: transparent;
  border-radius: var(--radius-full);
  padding: 0 var(--sp-4);
  font-size: var(--fs-sm);
  font-family: var(--font-sans);
  font-weight: 600;
  color: var(--text-muted);
  cursor: pointer;
  white-space: nowrap;
  transition: var(--transition);
}

.results-tabs .tab-btn:hover {
  border-color: var(--line-strong);
  color: var(--text-main);
}

.results-tabs .tab-btn.active {
  background: var(--secondary);
  border-color: var(--secondary);
  color: #FFF;
  box-shadow: none;
}

/* Refinamento: separador, não cartão. O painel é opcional e vem depois da
   lista (ver index.html) — é ajuste, não formulário a preencher antes de
   ver produto nenhum. */
.attribute-tuner {
  background: none;
  border: 0;
  border-top: 1px solid var(--border-glass);
  border-radius: 0;
  margin: var(--sp-6) 0 var(--sp-5);
  overflow: hidden;
  box-shadow: none;
}

.tuner-header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-3);
  min-height: var(--tap);
  padding: var(--sp-3) 0;
  cursor: pointer;
  background: none;
  user-select: none;
}

.tuner-title {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  color: var(--text-main);
  text-transform: none;   /* era CAIXA ALTA de 12px num rótulo longo */
  letter-spacing: 0;
}

.tuner-icon {
  width: 15px;
  height: 15px;
  stroke: var(--text-dim);
  stroke-width: 1.8;
  fill: none;
}

.tuner-chevron {
  width: 12px;
  height: 12px;
  stroke: var(--text-dim);
  transition: transform var(--transition);
}

.attribute-tuner.expanded .tuner-chevron {
  transform: rotate(180deg);
}

.tuner-content {
  padding: 0 0 var(--sp-4);
  display: flex;
  flex-direction: column;
  gap: 0;
  border-top: 0;
  /* Bug real corrigido: 500px cortava o painel quando os grupos apareciam
     todos abertos ao mesmo tempo (design antigo). O acordeão atual nunca
     precisa disso — só um <select> compacto por vez — mas o teto sobe bem
     acima de qualquer necessidade real, nunca corta por acidente de novo. */
  max-height: 2400px;
  transition: max-height 0.3s cubic-bezier(0.25, 0.8, 0.25, 1), opacity 0.25s ease-out;
  opacity: 1;
}

.tuner-content.collapsed {
  max-height: 0;
  padding-top: 0;
  padding-bottom: 0;
  border-top: 1px solid transparent;
  opacity: 0;
  pointer-events: none;
  overflow: hidden;
}

.tuner-group {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.tuner-group-title {
  font-size: 0.6rem;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  color: var(--text-muted);
  font-weight: 700;
}

.tuner-group-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
}

.tuner-chip {
  font-size: 0.68rem;
  padding: 4px 10px;
  border-radius: var(--radius-sm);
  background: var(--bg-card-hover);
  border: 1px solid var(--border-glass);
  color: var(--text-muted);
  cursor: pointer;
  transition: var(--transition);
  font-weight: 500;
}

.tuner-chip:hover {
  background: var(--border-glass);
  color: var(--secondary);
}

.tuner-chip.selected {
  background: var(--secondary);
  color: white;
  border-color: var(--secondary);
  font-weight: 600;
}

/* Acordeão por campo — cada grupo (categoria/subcategoria/material/
   modelagem/estampa/cor) começa FECHADO, só o valor atual visível; o
   seletor só entra em tela quando "Alterar" é tocado. Compartilhado por
   "/" e "/homologacao" (public/app.js:montarGrupoTuner). */
.tuner-group-linha {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 10px;
  padding: 10px 0;
  border-top: 1px solid var(--border-glass);
  min-height: 44px;
}
.tuner-group:first-child .tuner-group-linha { border-top: none; padding-top: 0; }
.tuner-group-info { min-width: 0; flex: 1; }
.tuner-group-rotulo { font-size: 0.68rem; text-transform: uppercase; letter-spacing: 0.5px; font-weight: 700; color: var(--text-muted); }
.tuner-group-valor { font-size: 0.78rem; color: var(--secondary); margin-top: 2px; overflow-wrap: break-word; }
.btn-alterar-tuner {
  flex-shrink: 0;
  min-height: 34px;
  padding: 5px 14px;
  border-radius: var(--radius-full);
  border: 1px solid var(--primary);
  background: var(--bg-card);
  color: var(--primary-ink);
  font-size: 0.7rem;
  font-weight: 700;
  cursor: pointer;
}
.btn-alterar-tuner:hover { background: var(--primary-glow); }
.tuner-group-editor { padding: 0 0 12px; }
.tuner-group-editor.colapsado { display: none; }
.tuner-group-editor select {
  width: 100%;
  min-height: 40px;
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-sm);
  padding: 6px 10px;
  font-size: 0.8rem;
  font-family: var(--font-sans);
  background: var(--bg-card);
  color: var(--text-main);
}

.btn-aplicar-tuner {
  margin-top: 14px;
  width: 100%;
  min-height: 46px;
  border-radius: var(--radius-md);
  border: none;
  background: var(--primary);
  color: #fff;
  font-family: var(--font-sans);
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.3px;
  cursor: pointer;
  transition: var(--transition);
}
.btn-aplicar-tuner:hover { filter: brightness(0.94); }

/* Matches Grid List — Rodada 3: "matches-grid" só EMPILHA as seções
   (Produtos reais / Catálogo demonstrativo Zara); o grid fotográfico de
   verdade é a seção (.resultados-secao) abaixo, que é a mãe direta de
   cada card (ver public/app.js:renderizarResultados — secaoReal/secaoDemo
   .appendChild(cardResultadoReal/cardResultado), nunca outro wrapper no
   meio). Antes .matches-grid virava 2 colunas a partir de 640px e cada
   COLUNA era uma seção inteira (real ao lado da demo) — nunca um grid de
   fotos de produto. */
.matches-grid {
  display: flex;
  flex-direction: column;
  gap: var(--sp-7);
}

/* Grid editorial de cards — protagonista da tela (Figma 04). 2 colunas no
   mobile (cabe com folga: card ~163px de largura em 375px, foto 3:4 ainda
   generosa), 3 no tablet, 4 no desktop (ver breakpoints). Título/avisos/
   estados vazios são os únicos filhos que não são card — cada um recebe
   grid-column:1/-1 explicitamente para ocupar a linha inteira. */
.resultados-secao {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--sp-4);
}

/* "PRODUTOS REAIS" em caixa alta era vocabulário interno de engenharia
   exposto na vitrine (o texto em si vem de app.js). Aqui vira um rótulo
   discreto de agrupamento, não uma manchete. */
.resultados-secao-titulo {
  grid-column: 1 / -1;
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-micro);
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
}

.resultados-secao-titulo-demo {
  opacity: 0.6;
  font-style: italic;
  text-transform: none;
  letter-spacing: normal;
}

/* Vitrine, não formulário: sem borda e sem sombra, separado por espaço.
   A altura fixa de 124px também saiu — ela existia para conter um card com
   moldura e era o que apertava a foto do produto em 90px de largura numa
   busca que é 100% visual. */
/* Rodada 3: card vertical (imagem no topo, texto abaixo) — é o que faz o
   grid parecer vitrine editorial em vez de lista. Antes era uma linha
   horizontal (miniatura + texto ao lado), o que fazia sentido numa lista
   de 1 coluna mas nunca formou um grid de fotos de verdade. */
.match-card {
  background: none;
  border: 0;
  border-radius: 0;
  overflow: visible;
  transition: none;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  height: auto;
  position: relative;
  box-shadow: none;
}

/* Card de produto real: pode crescer (detalhes técnicos + avaliação
   humana, só em homologMode) — bug real corrigido aqui, não só aparente.
   .match-card sozinho tem height:124px + overflow:hidden (correto pro
   catálogo demonstrativo, que nunca cresce); herdar isso pro card real
   cortava silenciosamente QUALQUER coisa além da linha imagem+info —
   inclusive "AVALIAR ESTE RESULTADO", que existia no DOM mas nunca era
   visível pra ninguém, em nenhum tamanho de tela. */
.match-card-real {
  height: auto;
  overflow: visible;
  flex-direction: column;
}
.match-card-real-topo {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  height: auto;
}
/* Sem moldura no card, os blocos de homologação não precisam mais recuar
   14px para respeitar uma borda que não existe. */
.match-card-real .btn-detalhes-tecnicos,
.match-card-real .detalhes-tecnicos-painel,
.match-card-real .card-avaliacao-real {
  margin-left: 0;
  margin-right: 0;
}
/* Diagnóstico técnico agora vem DEPOIS da avaliação (ordem pedida: o que
   importa pra homologar vem primeiro) — a margem inferior do card
   pertence a ele agora, não mais à avaliação. Botão sempre tem respiro
   embaixo (é o último elemento visível quando o painel está fechado, que
   é o padrão); o painel aberto ganha o mesmo respiro por conta própria. */
.match-card-real .btn-detalhes-tecnicos { margin-bottom: 12px; }
.match-card-real .detalhes-tecnicos-painel:not(.colapsado) { margin-top: 4px; margin-bottom: 14px; }

.match-card:hover {
  border-color: transparent;
  transform: none;
  box-shadow: none;
}

/* Rodada 3: a foto ocupa 100% da largura do card (topo do grid), não mais
   uma miniatura estreita ao lado do texto — ela é o conteúdo principal
   numa busca visual. Proporção 3:4 (editorial vertical) mantida como
   estava; só a largura mudou de fixa/percentual para 100%. object-fit:
   cover (abaixo, .match-card img) continua recortando sem distorcer —
   não avaliei necessário trocar por outra proporção: as imagens de
   produto já chegam em tamanhos variados por loja e o recorte 3:4 já
   resolve isso hoje. */
.match-card-image-box {
  width: 100%;
  flex-shrink: 0;
  aspect-ratio: 3 / 4;
  height: auto;
  position: relative;
  border-radius: var(--radius-md);
  overflow: hidden;
  background: var(--bg-card-hover);
}

.match-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s var(--ease);
}

.match-card:hover img { transform: scale(1.03); }

/* O selo de check colorido sobre a foto saiu: a MESMA informação já está
   escrita logo abaixo ("Muito parecido"/"Boa semelhança", vindo de
   rotuloSemelhanca) e em palavra ela é acionável — em selo, é decoração
   sobre o produto. Um sinal por informação, não dois. */
.match-card-score { display: none; }

.match-card-info {
  flex: 1;
  min-width: 0;
  padding: var(--sp-1) 0;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
}

.match-card-header {
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
}

/* A loja é legenda. Antes competia com o nome do produto em peso 700. */
.match-card-category {
  font-size: var(--fs-micro);
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: var(--tracking-caps);
  font-weight: 600;
  order: 2;          /* desce: produto e preço vêm antes da loja */
}

.match-card-name {
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  font-weight: 500;
  color: var(--text-main);
  line-height: var(--lh-snug);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  order: 1;
}

.match-card-footer {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: center;
  gap: var(--sp-2) var(--sp-3);
  margin-top: auto;
  padding-top: var(--sp-1);
}

.match-card-price {
  font-family: var(--font-sans);
  font-size: var(--fs-lg);
  font-weight: 600;
  letter-spacing: -0.01em;
  color: var(--secondary);
}

/* CTA de contorno: presente e clicável em 40px, sem virar o bloco de cor
   mais forte da tela repetido seis vezes. */
.btn-buy {
  background: transparent;
  color: var(--secondary);
  border: 1px solid var(--secondary);
  min-height: 40px;
  padding: 0 var(--sp-4);
  border-radius: var(--radius-full);
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-xs);
  letter-spacing: 0.01em;
  text-transform: none;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  white-space: nowrap;
  flex-shrink: 0;
  transition: background var(--dur) var(--ease), color var(--dur) var(--ease);
}

.btn-buy:hover {
  background: var(--secondary);
  color: #FFF;
  box-shadow: none;
}

.btn-buy svg {
  width: 13px;
  height: 13px;
  stroke: currentColor;
  stroke-width: 2;
  fill: none;
}

/* --------------------------------------------------------------------------
   Avaliação humana no card real — só em homologMode (ver .somente-homologacao
   no topo do arquivo). "Sistema: X" (label automática do motor) fica
   visualmente separado de "Sua avaliação" (o card real que a usuária avalia,
   nunca uma tela paralela).
   -------------------------------------------------------------------------- */
.rotulo-sistema-prefixo {
  font-size: 0.6rem;
  color: var(--text-dim);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  align-self: center;
}

/* "Diagnóstico técnico" — deliberadamente discreto (fonte pequena, cinza,
   sem cara de terminal/dev tool). É pra investigação pontual de caso
   estranho por Marina/equipe técnica, nunca a informação principal do
   card — Fran/David homologam só com "Muito bom/Aceitável/Ruim", sem
   precisar entender nenhum termo daqui. */
.btn-detalhes-tecnicos {
  margin-top: 10px;
  background: none;
  border: none;
  color: var(--text-dim);
  font-size: 0.64rem;
  font-weight: 600;
  padding: 4px 0;
  min-height: 28px;
  cursor: pointer;
  text-align: left;
}
.detalhes-tecnicos-painel {
  font-size: 0.7rem;
  background: var(--bg-card-hover);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  margin-top: 4px;
}
.detalhes-tecnicos-painel.colapsado { display: none; }
.diagnostico-linha {
  display: flex;
  justify-content: space-between;
  gap: 10px;
  padding: 4px 0;
  border-bottom: 1px dashed var(--border-glass);
}
.diagnostico-linha:last-child { border-bottom: none; }
.diagnostico-rotulo { color: var(--text-muted); flex-shrink: 0; }
.diagnostico-valor { color: var(--text-main); font-weight: 600; text-align: right; }

/* "Por que este resultado apareceu?" (V5 — radicalmente simplificado,
   item 4 do pedido): Categoria + Similaridade visual (linhas simples,
   .diagnostico-linha acima) seguidos de 3 grupos de chips — Combina/
   Difere/Não foi possível comparar (item 4.4) — nunca uma tabela grande
   (item 7, mobile). Detalhes técnicos crus ficam num <details> aninhado,
   fechado por padrão (item 4.7) — ver .diagnostico-tecnico-raw abaixo. */
.diagnostico-grupo {
  margin-top: 10px;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px;
}
.diagnostico-grupo-rotulo {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.62rem;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-muted);
  flex-basis: 100%;
}
.diagnostico-chip {
  display: inline-block;
  padding: 3px 9px;
  border-radius: var(--radius-full);
  font-size: 0.66rem;
  font-weight: 600;
  line-height: 1.4;
}
.chip-combina { background: rgba(21, 128, 61, 0.12); color: var(--success); }
.chip-difere { background: rgba(190, 40, 40, 0.1); color: #be2828; }
.chip-neutro { background: var(--bg-main); color: var(--text-dim); border: 1px dashed var(--border-glass); }

/* Detalhes técnicos crus (item 4.7) — <details> nativo aninhado DENTRO do
   painel "Por que apareceu?" (que já é um <details> conceitual via botão+
   painel colapsável). Fechado por padrão, sem JS extra. */
.diagnostico-tecnico-raw {
  margin-top: 12px;
  padding-top: 8px;
  border-top: 1px dashed var(--border-glass);
}
.diagnostico-tecnico-raw summary {
  cursor: pointer;
  list-style: none;
  font-size: 0.64rem;
  font-weight: 700;
  color: var(--text-dim);
}
.diagnostico-tecnico-raw summary::-webkit-details-marker { display: none; }
.diagnostico-tecnico-raw summary::before { content: '▸ '; }
.diagnostico-tecnico-raw[open] summary::before { content: '▾ '; }
.diagnostico-tecnico-raw[open] summary { margin-bottom: 6px; }

.card-avaliacao-real {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px dashed var(--border-glass);
}
.card-avaliacao-titulo {
  font-size: 0.62rem;
  font-weight: 800;
  letter-spacing: 0.5px;
  color: var(--text-muted);
  margin-bottom: 8px;
}
.botoes-avaliacao-real {
  display: grid;
  grid-template-columns: 1fr 1fr 1fr;
  gap: 6px;
}
.btn-avaliar-real {
  min-height: 40px;
  border-radius: var(--radius-full);
  border: 1.5px solid var(--border-glass);
  background: var(--bg-card);
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 0.66rem;
  font-weight: 700;
  cursor: pointer;
  padding: 4px;
}
.btn-avaliar-real[data-rating="muito_bom"].selecionado { background: rgba(21, 128, 61, 0.1); border-color: var(--success); color: var(--success); }
.btn-avaliar-real[data-rating="aceitavel"].selecionado { background: rgba(180, 83, 9, 0.1); border-color: var(--warning); color: var(--warning); }
.btn-avaliar-real[data-rating="ruim"].selecionado { background: rgba(185, 28, 28, 0.1); border-color: #b91c1c; color: #b91c1c; }

.motivo-ruim-real {
  margin-top: 10px;
  background: rgba(185, 28, 28, 0.06);
  border-radius: var(--radius-sm);
  padding: 10px;
}
.motivo-ruim-real.colapsado { display: none; }
.motivo-ruim-titulo { font-size: 0.66rem; font-weight: 700; margin-bottom: 8px; color: var(--secondary); }
.motivo-chips-real { display: flex; flex-direction: column; gap: 7px; }
.motivo-chips-real label { display: flex; align-items: center; gap: 8px; font-size: 0.7rem; color: var(--text-main); min-height: 24px; }

/* ACEITÁVEL — "o que ficou diferente?". MESMO slot e MESMOS componentes do
   painel de RUIM (uma só área de detalhe por card, nunca dois blocos
   abertos ao mesmo tempo); só o tom muda, pra acompanhar a cor do próprio
   botão "Aceitável" (âmbar) em vez do vermelho de "Ruim" — sinalizar
   "diferença parcial", nunca "erro". */
.motivo-ruim-real.modo-aceitavel { background: rgba(180, 83, 9, 0.07); }
.motivo-ruim-real.modo-aceitavel .motivo-ruim-titulo { color: var(--warning); }

/* 7 rótulos curtos leem melhor como chips lado a lado que como uma coluna
   de checkbox — menos rolagem dentro do card, menos fricção. Continuam
   sendo <input type=checkbox> de verdade (acessibilidade/teclado
   preservados), só apresentados como pílula. */
.motivo-chips-real.chips-inline { flex-direction: row; flex-wrap: wrap; gap: 6px; }
.motivo-chips-real.chips-inline label {
  min-height: 30px; padding: 4px 10px; gap: 6px;
  border: 1px solid var(--border-glass); border-radius: 999px;
  background: var(--surface, #fff); cursor: pointer;
}
.motivo-chips-real.chips-inline label:has(input:checked) {
  border-color: var(--warning); background: rgba(180, 83, 9, 0.12); font-weight: 600;
}
.motivo-categoria-detalhe-real { margin-top: 8px; }
.motivo-categoria-detalhe-real.colapsado { display: none; }
.campo-linha-mini-real { font-size: 0.64rem; display: block; color: var(--text-muted); }
.campo-linha-mini-real input {
  display: block; width: 100%; margin-top: 4px; min-height: 34px;
  border: 1px solid var(--border-glass); border-radius: var(--radius-sm);
  padding: 6px 8px; font-size: 0.72rem; font-family: var(--font-sans);
}
.motivo-outro-texto-real {
  width: 100%; margin-top: 8px; min-height: 50px;
  border: 1px solid var(--border-glass); border-radius: var(--radius-sm);
  padding: 6px 8px; font-size: 0.72rem; font-family: var(--font-sans); resize: vertical;
}
.btn-salvar-motivo-real {
  margin-top: 8px;
  width: 100%;
  min-height: 38px;
  border-radius: var(--radius-sm);
  border: none;
  background: var(--secondary);
  color: #fff;
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 700;
  cursor: pointer;
}

/* ==========================================================================
   View 4: Catalog Explorer
   ========================================================================== */
/* Campo + filtros não precisam de uma moldura própria em volta: eles já são
   controles, e a caixa branca criava mais um card na tela. */
.search-filter-box {
  background: none;
  border: 0;
  border-radius: 0;
  padding: 0;
  margin-bottom: var(--sp-5);
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  box-shadow: none;
}

.search-input-wrapper {
  position: relative;
  width: 100%;
}

.search-input-wrapper svg {
  position: absolute;
  left: 12px;
  top: 50%;
  transform: translateY(-50%);
  width: 14px;
  height: 14px;
  stroke: var(--text-dim);
  stroke-width: 2;
}

.search-input {
  width: 100%;
  background: var(--bg-main);
  border: 1px solid var(--border-glass);
  padding: 8px 12px 8px 36px;
  border-radius: var(--radius-sm);
  color: var(--text-main);
  font-family: var(--font-sans);
  font-size: 0.78rem;
  transition: var(--transition);
}

.search-input:focus {
  outline: none;
  border-color: var(--primary);
  background: white;
}

.catalog-tabs {
  display: flex;
  gap: 6px;
}

.tab-btn {
  flex: 1;
  background: var(--bg-card-hover);
  border: 1px solid var(--border-glass);
  color: var(--text-muted);
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 600;
  padding: 6px 0;
  border-radius: var(--radius-sm);
  cursor: pointer;
  transition: var(--transition);
  text-align: center;
}

.tab-btn.active {
  background: var(--secondary);
  color: white;
  border-color: var(--secondary);
}

/* Catalog Product Grid */
.catalog-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 14px;
}

/* Um único estilo de card no app inteiro: sem moldura, sem sombra, separado
   por espaço, com a foto em 3:4 como elemento principal. */
.catalog-card {
  background: none;
  border: 0;
  border-radius: 0;
  overflow: visible;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  transition: none;
  box-shadow: none;
}
.catalog-card:hover { transform: none; box-shadow: none; border-color: transparent; }

.catalog-card:hover {
  transform: translateY(-2px);
  border-color: var(--primary);
  box-shadow: var(--shadow-md);
}

.catalog-image-box {
  width: 100%;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  position: relative;
  border-radius: var(--radius-md);
  background: var(--bg-card-hover);
}

.catalog-image-box img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: var(--transition);
}

.catalog-card:hover .catalog-image-box img {
  transform: scale(1.03);
}

.catalog-details {
  padding: 0;
  flex: 1;
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: var(--sp-1);
}

.catalog-card-name {
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 500;
  color: var(--text-main);
  line-height: var(--lh-snug);
  order: 1;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.catalog-card-footer {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.catalog-card-price {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.8rem;
  color: var(--secondary);
}

/* 24×24px não é alcançável com o polegar: vira 44px de alvo, com o peso
   visual de um ícone e não o de uma caixa. */
.btn-catalog-buy {
  background: none;
  border: 0;
  color: var(--text-dim);
  width: var(--tap);
  height: var(--tap);
  margin-right: calc(var(--sp-3) * -1);
  border-radius: var(--radius-full);
  display: flex;
  justify-content: center;
  align-items: center;
  cursor: pointer;
  flex-shrink: 0;
  transition: var(--transition);
}
.btn-catalog-buy:hover { background: var(--bg-card-hover); color: var(--secondary); }
.btn-catalog-buy svg { width: 16px; height: 16px; stroke: currentColor; fill: none; stroke-width: 1.8; }

.btn-catalog-buy:hover {
  background: var(--primary);
  border-color: var(--primary);
  color: white;
}

.btn-catalog-buy svg {
  width: 10px;
  height: 10px;
  stroke: currentColor;
  stroke-width: 2.2;
}

/* ==========================================================================
   View 5: Admin Panel & Telemetry Dashboard
   ========================================================================== */
.admin-card {
  background: var(--bg-card);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  padding: 16px;
  margin-bottom: 16px;
  box-shadow: var(--shadow-sm);
}

.admin-card-title {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.8rem;
  text-transform: uppercase;
  letter-spacing: 0.8px;
  margin-bottom: 14px;
  color: var(--secondary);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.admin-badge {
  background: rgba(21, 128, 61, 0.1);
  color: var(--success);
  font-size: 0.55rem;
  font-weight: 700;
  padding: 2px 6px;
  border-radius: 2px;
  letter-spacing: 0.5px;
}

/* Telemetry Grid widgets */
.telemetry-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
}

.metric-box {
  background: var(--bg-main);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-sm);
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 4px;
}

.metric-label {
  font-size: 0.58rem;
  color: var(--text-dim);
  text-transform: uppercase;
  font-weight: 700;
  letter-spacing: 0.5px;
}

.metric-value {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 1.15rem;
  color: var(--secondary);
}

.metric-sub {
  font-size: 0.58rem;
  color: var(--success);
  display: flex;
  align-items: center;
  gap: 2px;
}

/* Telemetry Chart */
.chart-container {
  height: 100px;
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 12px;
  padding-top: 16px;
  border-bottom: 1px solid var(--border-glass);
  margin-bottom: 8px;
}

.chart-bar-wrapper {
  flex: 1;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 4px;
  height: 100%;
  justify-content: flex-end;
}

.chart-bar {
  width: 100%;
  max-width: 20px;
  border-top-left-radius: 2px;
  border-top-right-radius: 2px;
  background: var(--secondary);
  position: relative;
  transition: var(--transition);
}

.chart-bar:hover {
  background: var(--primary);
}

.chart-bar::before {
  content: attr(data-val);
  position: absolute;
  top: -14px;
  left: 50%;
  transform: translateX(-50%);
  font-size: 0.55rem;
  color: var(--text-muted);
  font-weight: 700;
}

.chart-label {
  font-size: 0.55rem;
  color: var(--text-dim);
  font-weight: 600;
}

/* Switch widgets */
.switch-row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 0;
  border-bottom: 1px solid var(--border-glass);
}
.switch-row:last-child {
  border-bottom: none;
}

.switch-label-box {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.switch-name {
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--secondary);
}

.switch-desc {
  font-size: 0.6rem;
  color: var(--text-dim);
}

.switch-control {
  position: relative;
  display: inline-block;
  width: 36px;
  height: 20px;
}

.switch-control input {
  opacity: 0;
  width: 0;
  height: 0;
}

.slider {
  position: absolute;
  cursor: pointer;
  top: 0;
  left: 0;
  right: 0;
  bottom: 0;
  background-color: var(--border-glass);
  transition: .2s;
  border-radius: var(--radius-full);
}

.slider:before {
  position: absolute;
  content: "";
  height: 14px;
  width: 14px;
  left: 3px;
  bottom: 3px;
  background-color: white;
  transition: .2s;
  border-radius: var(--radius-full);
  box-shadow: 0 1px 3px rgba(0,0,0,0.1);
}

input:checked + .slider {
  background-color: var(--secondary);
}

input:checked + .slider:before {
  transform: translateX(16px);
}

/* Coupons manager list */
.coupons-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}

.coupon-card {
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-sm);
  padding: 8px 10px;
  background: var(--bg-main);
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.coupon-info {
  display: flex;
  flex-direction: column;
  gap: 1px;
}

.coupon-code {
  font-family: var(--font-sans);
  font-weight: 700;
  font-size: 0.75rem;
  color: var(--secondary);
  display: flex;
  align-items: center;
  gap: 6px;
}

.coupon-discount {
  background: var(--primary-glow);
  color: var(--primary-ink);
  font-size: 0.55rem;
  font-weight: 700;
  padding: 1px 4px;
  border-radius: 2px;
}

.coupon-meta {
  font-size: 0.58rem;
  color: var(--text-muted);
}

.btn-clip {
  background: var(--bg-card);
  color: var(--secondary);
  border: 1px solid var(--border-glass);
  font-family: var(--font-sans);
  font-size: 0.65rem;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: var(--transition);
}

.btn-clip.clipped {
  background: var(--success);
  color: white;
  border-color: var(--success);
}

/* Bateria de confiabilidade (P1 approximate) — mesma linguagem visual do
   .btn-clip, classe própria pra não ser capturada pelo wiring de cupons
   (que lê data-coupon de todo .btn-clip). */
.btn-bateria {
  background: var(--bg-card);
  color: var(--secondary);
  border: 1px solid var(--border-glass);
  font-family: var(--font-sans);
  font-size: 0.65rem;
  font-weight: 600;
  padding: 4px 10px;
  border-radius: var(--radius-full);
  cursor: pointer;
  transition: var(--transition);
}
.btn-bateria:hover {
  background: var(--primary);
  color: white;
  border-color: var(--primary);
}

/* Live logger console */
.log-box {
  background: var(--secondary);
  border-radius: var(--radius-sm);
  font-family: monospace;
  font-size: 0.6rem;
  color: #D6D3D1;
  height: 90px;
  padding: 8px;
  overflow-y: auto;
  line-height: 1.45;
  scrollbar-width: none;
}
.log-box::-webkit-scrollbar {
  display: none;
}

.log-entry {
  margin-bottom: 2px;
  display: flex;
  gap: 4px;
}

.log-time {
  color: var(--primary);
}

.log-event {
  color: var(--bg-main);
}

/* Affiliate commission toasts */
.toast-overlay {
  position: absolute;
  top: 40px;
  left: 20px;
  right: 20px;
  background: var(--secondary);
  border: 1px solid var(--primary);
  border-radius: var(--radius-md);
  padding: 12px 14px;
  box-shadow: 0 10px 30px rgba(26, 20, 16, 0.2);
  display: flex;
  align-items: center;
  gap: 10px;
  transform: translateY(-80px);
  opacity: 0;
  transition: all 0.35s cubic-bezier(0.175, 0.885, 0.32, 1.15);
  z-index: 1000;
}

.toast-overlay.active {
  transform: translateY(0);
  opacity: 1;
}

.toast-overlay-icon {
  width: 20px;
  height: 20px;
  background: var(--primary-glow);
  border-radius: var(--radius-full);
  display: flex;
  justify-content: center;
  align-items: center;
  flex-shrink: 0;
}

.toast-overlay-icon svg {
  width: 12px;
  height: 12px;
  stroke: var(--primary);
  stroke-width: 2.5;
}

.toast-overlay-info {
  flex: 1;
}

.toast-overlay-title {
  font-family: var(--font-sans);
  font-weight: 600;
  font-size: var(--fs-sm);
  color: white;
  letter-spacing: 0.01em;
}

/* 9.6px era ilegível. Piso de --fs-xs (12px) para texto corrido, e o cinza
   sobe para passar contraste sobre a Tinta do toast. */
.toast-overlay-desc {
  font-size: var(--fs-xs);
  color: #C9C3BB;
  line-height: var(--lh-snug);
}

/* ==========================================================================
   Componentes da v2 — detecção dinâmica, explicabilidade e honestidade
   de estado. Seguem os tokens do design system definido no topo.
   ========================================================================== */

/* --------------------------------------------------------------------------
   Badge de estado da IA no cabeçalho.
   A interface passa a declarar se a análise é completa ou aproximada, em vez
   de apresentar os dois casos com a mesma confiança visual.
   -------------------------------------------------------------------------- */
/* Estado do sistema, não manchete: um ponto colorido + a palavra, em vez de
   uma pílula em caixa alta de 9px que competia com o wordmark da marca.
   O texto continua na árvore de acessibilidade mesmo quando recolhido. */
.ia-badge {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  flex-shrink: 1;
  min-width: 0;
  font-size: var(--fs-micro);
  font-weight: 500;
  letter-spacing: 0.01em;
  text-transform: none;
  padding: 0;
  border-radius: 0;
  border: 0;
  color: var(--text-dim);
  background: none;
  margin-left: auto;
  margin-right: 0;
  white-space: nowrap;
  overflow: hidden;
}

.ia-badge::before {
  content: '';
  width: 6px;
  height: 6px;
  flex-shrink: 0;
  border-radius: var(--radius-full);
  background: var(--border-glass);
}

.ia-badge-ativa {
  color: var(--text-dim);
  border-color: transparent;
  background: none;
}
.ia-badge-ativa::before { background: var(--success); }

.ia-badge-aproximada {
  color: var(--warning);
  border-color: transparent;
  background: none;
}
.ia-badge-aproximada::before { background: var(--warning); }

/* --------------------------------------------------------------------------
   Badge "HOMOLOGAÇÃO" — só existe quando public/app.js confirma
   state.homologMode (pathname /homologacao + servidor com
   HOMOLOGATION_MODE=true). [hidden] cobre o caso do JS não carregar; nunca
   depende só de CSS pra ficar escondido de usuária pública.
   -------------------------------------------------------------------------- */
.badge-homolog {
  font-size: 0.6rem;
  font-weight: 800;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  padding: 2px 8px;
  border-radius: var(--radius-full);
  background: var(--warning);
  color: #fff;
}
.badge-homolog[hidden] { display: none; }

/* Blocos exclusivos do modo homologação (avaliação humana no card,
   detalhes técnicos) — escondidos por padrão, revelados só quando
   public/app.js adiciona a classe "homolog-mode" ao <body> depois de
   confirmar com o servidor. Dupla trava: JS decide QUANDO, CSS decide o
   QUE mostrar — nenhuma das duas sozinha libera nada. */
.somente-homologacao { display: none; }
body.homolog-mode .somente-homologacao { display: block; }

/* .nav-item já é display:flex por padrão — a regra genérica acima
   (display:block) quebraria o layout em coluna do ícone+texto, por isso
   uma regra mais específica só pros itens de nav exclusivos de
   homologMode (Métricas/Catálogo real, ver public/index.html).

   BUG REAL corrigido (achado ao vivo na integração desta rodada):
   #nav-btn-admin (Painel) SAIU desta lista — ele ganhou a classe
   somente-homologacao (ver public/index.html) exatamente pra parar de
   ficar aberto por padrão pra qualquer visitante pública. Uma regra por
   ID como esta venceria a de baixo por especificidade e o esconderia
   justo dentro de homologMode, o único lugar onde agora ele deve
   aparecer — o oposto do que ele fazia até virar bug encontrado nesta
   rodada.

   #nav-btn-catalog (item antigo "Catálogo" demo) e a regra que o escondia
   só em homologMode SAÍRAM daqui — o botão foi removido de
   public/index.html na auditoria de mock/catálogo (Agente C): ele era o
   caminho público pra vitrine dos 65 produtos fabricados
   (catalog_enriched.js, 11 deles "Zara"), sempre visível pra QUALQUER
   visitante e escondido só em homologMode — o mesmo padrão de bug do
   #nav-btn-admin acima, mas nunca corrigido antes desta rodada. */
.bottom-nav .nav-item.somente-homologacao { display: none; }
body.homolog-mode .bottom-nav .nav-item.somente-homologacao { display: flex; }

/* View-panel exclusivo de homologMode (Métricas/Catálogo real): precisa
   ficar escondido mesmo se `.active` for aplicado por engano (nunca
   alcançável fora de homologMode hoje — só os nav-items acima levam lá,
   e esses já ficam escondidos — mas a trava fica explícita e redundante
   de propósito, mesma disciplina do resto do arquivo).

   BUG REAL corrigido (rodada cirúrgica de UX): a regra genérica de cima
   (`body.homolog-mode .somente-homologacao { display: block; }`, linha
   ~1988) TAMBÉM bate em qualquer .view-panel que tenha a classe
   somente-homologacao (ex.: #view-homolog-metricas) — e a especificidade
   dela (body + 2 classes) é MAIOR que `.view-panel.somente-homologacao
   { display: none; }` (2 classes só), então em homologMode o painel de
   Métricas ficava display:block O TEMPO TODO, mesmo sem `.active`,
   empilhado por baixo de QUALQUER outra aba (Buscar/Catálogo/Admin) —
   nunca só ao trocar entre Métricas e Catálogo. A regra abaixo agora tem
   especificidade maior que a genérica (body + 3 classes efetivas via
   :not()), então realmente esconde o painel sempre que ele não é o
   ativo, mesmo em homologMode. */
.view-panel.somente-homologacao { display: none; }
body.homolog-mode .view-panel.somente-homologacao:not(.active) { display: none; }
body.homolog-mode .view-panel.somente-homologacao.active { display: block; }

/* Seletor de avaliador (item 13).
   BUG REAL corrigido (achado ao vivo na integração desta rodada, smoke
   test no Preview): mesma classe de bug documentada duas vezes acima
   (view-panel e bottom-nav .nav-item) — `.homolog-evaluator-row` também
   tem a classe `somente-homologacao`, mas esta regra abaixo dela no
   arquivo (mesma especificidade: 1 classe) declarava `display: flex`
   incondicional, vencendo `.somente-homologacao { display: none; }` por
   ordem de cascata. Resultado real: o seletor "Avaliando como" ficava
   visível pra QUALQUER visitante pública, mesmo fora de homologMode —
   confirmado com `getComputedStyle` na raiz do site, body sem a classe
   homolog-mode. `display` sai da regra base e vai só pra versão travada
   por `body.homolog-mode`, mesma disciplina do resto do arquivo. */
.homolog-evaluator-row {
  align-items: center;
  gap: 8px;
  padding: 6px 16px 10px;
  font-size: 0.68rem;
  color: var(--text-muted);
  flex-wrap: wrap;
}
body.homolog-mode .homolog-evaluator-row { display: flex; }
.homolog-evaluator-row label { font-weight: 600; }
.homolog-evaluator-row select,
.homolog-evaluator-row input {
  font-family: var(--font-sans);
  font-size: 0.7rem;
  padding: 4px 8px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-glass);
  background: var(--bg-card);
  color: var(--text-main);
}
.homolog-evaluator-row input.colapsado { display: none; }

/* Botão "Atualizar" nos cards de Métricas/Catálogo real */
.btn-atualizar-homolog {
  float: right;
  font-family: var(--font-sans);
  font-size: 0.62rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  padding: 4px 10px;
  border-radius: var(--radius-full);
  border: 1px solid var(--border-glass);
  background: var(--bg-main);
  color: var(--primary-ink);
  cursor: pointer;
}

/* Tabelas simples (cobertura de catálogo, por categoria) — sempre dentro
   de um wrapper com scroll horizontal próprio, nunca estourando a
   largura da tela (mobile-first, mesma regra do resto do app). */
.tabela-scroll { overflow-x: auto; }
.tabela-homolog {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.66rem;
  white-space: nowrap;
}
.tabela-homolog th,
.tabela-homolog td {
  text-align: left;
  padding: 6px 8px;
  border-bottom: 1px solid var(--border-glass);
}
.tabela-homolog th {
  color: var(--text-muted);
  text-transform: uppercase;
  letter-spacing: 0.4px;
  font-size: 0.6rem;
}
.status-cobertura {
  padding: 2px 7px;
  border-radius: var(--radius-full);
  font-weight: 700;
  font-size: 0.6rem;
  white-space: nowrap;
}
.status-cobertura-boa { background: rgba(22, 163, 74, 0.12); color: #16a34a; }
.status-cobertura-media { background: rgba(180, 131, 9, 0.12); color: var(--warning); }
.status-cobertura-baixa,
.status-cobertura-sem-cobertura { background: rgba(190, 40, 40, 0.1); color: #be2828; }

/* Exportar dados */
.homolog-export-botoes { display: flex; flex-direction: column; gap: 8px; }

/* Backfill de embeddings (Gate P0, V6) — botões empilham no mobile
   (item: UX mobile), nunca lado a lado forçado. */
.homolog-embeddings-acoes { display: flex; flex-direction: column; gap: 8px; margin-top: 10px; }
.btn-embeddings-parar { border-color: rgba(190, 40, 40, 0.35); color: #be2828; }
.btn-embeddings-parar.colapsado { display: none; }
.btn-exportar-homolog {
  display: block;
  text-align: center;
  padding: 10px 12px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-glass);
  background: var(--bg-main);
  color: var(--primary-ink);
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 700;
  text-decoration: none;
}

/* Export agrupado em UM botão (V4, item 17.3) — <details>/<summary> nativo:
   zero JS novo, acessível por teclado, nunca muda os endpoints/CSVs
   individuais por baixo (cada <a> aqui é o MESMO link que já existia
   solto antes — só agrupado visualmente). "summary" é o botão único que a
   usuária vê fechado; abrir revela os CSVs individuais (fallback tipo
   dropdown pedido explicitamente, em vez de gerar um .zip no servidor). */
.export-dropdown { border: 1px solid var(--border-glass); border-radius: var(--radius-sm); background: var(--bg-main); overflow: hidden; }
.export-dropdown summary {
  cursor: pointer;
  list-style: none;
  padding: 10px 12px;
  text-align: center;
  color: var(--primary-ink);
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 700;
}
.export-dropdown summary::-webkit-details-marker { display: none; }
.export-dropdown summary::after { content: ' ▾'; }
.export-dropdown[open] summary::after { content: ' ▴'; }
.export-dropdown[open] summary { border-bottom: 1px solid var(--border-glass); }
.export-dropdown-itens { display: flex; flex-direction: column; gap: 6px; padding: 8px; }
.export-dropdown-itens .btn-exportar-homolog { border-style: dashed; }

/* Filtros do catálogo real (item 9) */
.homolog-catalogo-filtros {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin: 10px 0;
}
.homolog-filtro-checkbox {
  display: flex;
  align-items: center;
  gap: 6px;
  font-family: var(--font-sans);
  font-size: 0.7rem;
  font-weight: 700;
  color: var(--text-muted);
  padding: 0 4px;
}
.homolog-filtro-checkbox input { margin: 0; }
.homolog-catalogo-filtros select {
  font-family: var(--font-sans);
  font-size: 0.66rem;
  padding: 6px 8px;
  border-radius: var(--radius-sm);
  border: 1px solid var(--border-glass);
  background: var(--bg-card);
  color: var(--text-main);
}
.item-catalogo-real {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  padding: 8px 0;
  border-bottom: 1px dashed var(--border-glass);
  font-size: 0.68rem;
}
.item-catalogo-real:last-child { border-bottom: none; }
.item-catalogo-real-nome { color: var(--text-main); font-weight: 600; }
.item-catalogo-real-meta { color: var(--text-muted); font-size: 0.62rem; }
.item-catalogo-real-categoria { color: var(--text-muted); font-size: 0.6rem; margin-top: 1px; }
.item-catalogo-real-indisponivel { color: var(--warning); font-weight: 700; font-size: 0.6rem; }
.item-catalogo-real-motivos { color: var(--warning); font-size: 0.6rem; margin-top: 2px; }

/* --------------------------------------------------------------------------
   Aviso de modo aproximado nos resultados
   -------------------------------------------------------------------------- */
/* Aviso honesto, mas em uma linha e sem caixa âmbar: ele informa uma
   limitação, não é um erro que exige ação — a caixa colorida de 4 linhas
   dominava o topo da tela de resultados. */
.modo-aproximado-banner {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  padding: 0;
  margin-bottom: var(--sp-4);
  background: none;
  border: 0;
  border-radius: 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: var(--text-dim);
}

.modo-aproximado-banner svg {
  width: 14px;
  height: 14px;
  flex-shrink: 0;
  color: var(--warning);
  stroke: currentColor;
  fill: none;
  margin-top: 2px;
}

.modo-aproximado-banner strong { color: var(--warning); }

/* --------------------------------------------------------------------------
   Resumo por peça na tela de escaneamento
   -------------------------------------------------------------------------- */
.attribute-chips {
  flex-direction: column;
  gap: 10px;
  width: 100%;
  margin-top: 10px;
}

.peca-resumo {
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-md);
  padding: 9px 11px;
  background: var(--bg-card);
}

.peca-resumo-cabecalho {
  display: flex;
  align-items: center;
  gap: 8px;
  margin-bottom: 7px;
}

.peca-resumo-thumb {
  width: 28px;
  height: 28px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  border: 1px solid var(--border-glass);
  flex-shrink: 0;
}

.peca-resumo-titulo {
  font-family: var(--font-display);
  font-size: 0.92rem;
  font-weight: 600;
  color: var(--text-main);
  display: flex;
  align-items: center;
  gap: 7px;
}

.peca-sem-catalogo {
  font-family: var(--font-sans);
  font-size: 0.5rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--text-dim);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-full);
  padding: 1px 6px;
}

.peca-resumo-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 5px;
}

/* Confiança da IA vira sinal visual: a usuária distingue o que o sistema
   sabe do que ele está estimando. */
.chip-conf-alta {
  border-color: rgba(21, 128, 61, 0.35);
  color: var(--success);
  background: rgba(21, 128, 61, 0.05);
}

.chip-conf-media {
  border-color: var(--border-glass);
  color: var(--text-muted);
}

.chip-conf-baixa {
  border-color: var(--border-glass);
  color: var(--text-dim);
  background: repeating-linear-gradient(
    -45deg,
    transparent, transparent 3px,
    rgba(120, 113, 108, 0.07) 3px, rgba(120, 113, 108, 0.07) 6px
  );
}

.chip-vazio {
  color: var(--text-dim);
  font-style: italic;
  opacity: 0.75;
}

/* --------------------------------------------------------------------------
   Rodada 3 — BUG REAL corrigido: este bloco reativava (display:flex) o
   selo de percentual sobre a foto que a linha ~1471 (.match-card-score
   { display: none; }) já tinha deliberadamente removido — "a mesma
   informação já está escrita abaixo, um sinal por informação, não dois".
   Como vinha depois no arquivo e com a mesma especificidade, esta regra
   VENCIA a cascata e o selo (ícone de check colorido por faixa) voltava a
   aparecer, contradizendo a própria decisão documentada ali. As classes
   .match-score-num/.match-score-pct nunca correspondem a nada gerado por
   public/app.js (o selo real só tem um SVG de check, sem dígito) — eram
   sobra morta de um desenho anterior (número de 90-99%, citado no
   comentário antigo). Removidas; .match-card-score continua "display:
   none" (linha ~1471), única fonte de verdade — alinhado também com a
   instrução desta rodada de não dar ao Top 1 nenhum tratamento visual de
   certeza. */

/* --------------------------------------------------------------------------
   "Por que combina" — explicabilidade.
   Um motivo legível converte melhor do que um percentual sozinho.
   -------------------------------------------------------------------------- */
/* Explicabilidade LEVE: linha de texto, não fileira de pílulas coloridas.
   Seis cards × três badges verdes com borda era a maior fonte de ruído da
   tela. O texto continua o mesmo — muda o peso visual dele. */
.match-porques {
  display: block;
  margin: 0;
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
  color: var(--text-muted);
}

.porque {
  display: inline;
  font-size: inherit;
  font-weight: 500;
  letter-spacing: 0;
  padding: 0;
  border-radius: 0;
  border: 0;
  background: none;
  white-space: normal;
}

.porque + .porque::before {
  content: ' · ';
  color: var(--border-glass);
}

.porque-positivo {
  color: var(--text-muted);
  background: none;
  border-color: transparent;
}

.porque-negativo {
  color: var(--text-dim);
  background: none;
  border-color: transparent;
}

.meta-alerta {
  color: var(--warning);
  font-size: var(--fs-xs);
}

/* --------------------------------------------------------------------------
   Abas dinâmicas
   -------------------------------------------------------------------------- */
.tab-btn-indisponivel {
  opacity: 0.45;
  cursor: not-allowed;
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}

/* --------------------------------------------------------------------------
   Estados vazios — antes eram uma linha de texto cinza solta
   -------------------------------------------------------------------------- */
.estado-vazio {
  grid-column: 1 / -1;
  text-align: center;
  padding: var(--sp-8) var(--sp-5);
  color: var(--text-muted);
}

/* Todo estado vazio termina num caminho de saída, nunca só num diagnóstico. */
.btn-estado-vazio {
  margin-top: var(--sp-5);
  min-height: var(--tap);
  padding: 0 var(--sp-5);
  border: 1px solid var(--secondary);
  border-radius: var(--radius-full);
  background: var(--secondary);
  color: #FFF;
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--dur) var(--ease);
}
.btn-estado-vazio:hover { background: #000; }

.estado-vazio-icone {
  font-size: 1.9rem;
  color: var(--border-glass);
  margin-bottom: 10px;
  line-height: 1;
}

.estado-vazio-titulo {
  font-family: var(--font-display);
  font-size: var(--fs-xl);
  font-weight: 500;
  line-height: var(--lh-tight);
  color: var(--text-main);
  margin-bottom: var(--sp-2);
}

.estado-vazio-texto {
  font-size: var(--fs-sm);
  max-width: 42ch;
  line-height: 1.6;
  color: var(--text-dim);
  max-width: 34ch;
  margin: 0 auto;
}

.estado-vazio-texto code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.68rem;
  background: var(--bg-card-hover);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-sm);
  padding: 1px 5px;
}

/* Categoria não determinada (V4, item 2.4.2) — fallback sem IA que não
   inventa categoria nenhuma (ver public/app.js:pecasAproximadas). Mesma
   base de .estado-vazio, só alinha a lista de sinais reais à esquerda
   (fica ilegível centralizada) e destaca o convite pra correção humana. */
.estado-categoria-indeterminada { text-align: left; }
.estado-categoria-indeterminada .estado-vazio-titulo { text-align: center; }
.estado-categoria-indeterminada .diagnostico-linha { border-bottom: 1px dashed var(--border-glass); }
/* Ausência de match forte (V5, item 1.3/3) — mesmo tom âmbar de aviso já
   usado em .estado-categoria-indeterminada-aviso, nunca um vermelho de
   erro (não é uma falha, é uma resposta honesta: a base atual não tem
   nada com semelhança forte pra esta busca). */
.aviso-sem-match-forte {
  grid-column: 1 / -1;
  margin-bottom: 12px;
  padding: 10px 12px;
  background: rgba(180, 131, 9, 0.1);
  border: 1px solid rgba(180, 131, 9, 0.25);
  border-radius: var(--radius-sm);
  font-size: 0.72rem;
  font-weight: 600;
  color: var(--warning);
  text-align: center;
}
/* Continua sendo o convite explícito à correção exigido pelo item 2.4.2 —
   só deixou de ser uma caixa âmbar. Logo abaixo dele há agora um BOTÃO que
   faz o que a frase pede (antes o convite não tinha ação nenhuma), então a
   frase pode voltar a ser o que é: uma legenda. */
.estado-categoria-indeterminada-aviso {
  margin-top: var(--sp-3);
  padding: 0;
  background: none;
  border: 0;
  border-radius: 0;
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--text-dim);
  text-align: center;
}

/* --------------------------------------------------------------------------
   Passo com falha no stepper — o pipeline antigo não tinha estado de erro
   -------------------------------------------------------------------------- */
.step-item.failed .step-indicator {
  background: var(--warning);
  border-color: var(--warning);
  color: #fff;
}

.step-item.failed .step-desc { color: var(--warning); }

/* --------------------------------------------------------------------------
   Tuner
   -------------------------------------------------------------------------- */
.tuner-ajuda {
  font-size: 0.66rem;
  line-height: 1.55;
  color: var(--text-dim);
  margin-bottom: 12px;
  padding-bottom: 10px;
  border-bottom: 1px solid var(--border-glass);
}

.tuner-chip {
  font-family: var(--font-sans);
  cursor: pointer;
}

/* --------------------------------------------------------------------------
   Catálogo: marca e atributos derivados visíveis no card
   -------------------------------------------------------------------------- */
/* A marca do varejista era o elemento MAIS colorido do card (Ouro UI, peso
   700, 8px em caixa alta) — mais forte que o preço e que o próprio produto,
   e ilegível no tamanho em que estava. Vira legenda cinza, depois do nome. */
.catalog-card-brand {
  display: block;
  font-size: var(--fs-micro);
  font-weight: 600;
  letter-spacing: var(--tracking-caps);
  text-transform: uppercase;
  color: var(--text-dim);
  margin-bottom: 0;
  order: 2;
}

/* Metadado de indexação não é conteúdo de vitrine — continua pesquisável
   pelo campo de busca acima, só não é mais impresso em cada card. */
.catalog-card-attrs { display: none; }

/* A ordem de leitura do card: nome (1), loja (2), preço + ação (3).
   Sem este `order`, o rodapé herda 0 e o preço sobe acima do produto. */
.catalog-card-footer { order: 3; margin-top: var(--sp-1); }

.mini-chip {
  font-size: 0.5rem;
  font-weight: 500;
  color: var(--text-dim);
  border: 1px solid var(--border-glass);
  border-radius: var(--radius-full);
  padding: 1px 6px;
  white-space: nowrap;
}

/* --------------------------------------------------------------------------
   Drop zone ativa durante arrastar
   -------------------------------------------------------------------------- */
.drop-zone-ativa {
  border-color: var(--primary) !important;
  background: var(--primary-glow) !important;
}

/* --------------------------------------------------------------------------
   Log: níveis de severidade
   -------------------------------------------------------------------------- */
.log-aviso .log-event { color: var(--warning); }
.log-erro  .log-event { color: #b91c1c; }

/* --------------------------------------------------------------------------
   Acessibilidade: respeita quem pediu menos movimento
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
  }
  .bounding-box { opacity: 1; transform: none; }
}

/* --------------------------------------------------------------------------
   Cabeçalho: com o badge de estado da IA somado à marca e ao botão Admin,
   a linha estourava 375px de largura. Em telas estreitas a marca encolhe
   e o Admin fica só com o ícone — o rótulo é redundante com o ícone de grade.
   -------------------------------------------------------------------------- */
/* A marca deixou de ser o elemento que trunca (ver .brand acima): quem cede
   espaço agora é o badge de estado, não o wordmark. */
header .brand { min-width: 0; }

header .brand-name {
  overflow: visible;
  text-overflow: clip;
  white-space: nowrap;
}

/* O botão de painel interno é ferramenta de operação: só ícone, sempre,
   com alvo de 44px e nome acessível no aria-label (ver index.html).
   BUG REAL corrigido (achado ao vivo na integração desta rodada, 3ª
   instância da mesma classe de bug nesta rodada): um seletor por ID
   sempre vence um seletor de classe, então este `display` batia
   `.somente-homologacao { display: none; }` mesmo com a classe presente
   no elemento — confirmado com getComputedStyle na home pública,
   display:flex, body sem a classe homolog-mode. `display` sai daqui,
   só existe na versão travada por `body.homolog-mode` logo abaixo. */
#header-admin-btn {
  flex-shrink: 0;
  width: var(--tap);
  height: var(--tap);
  padding: 0;
  gap: 0;
  font-size: 0;
  align-items: center;
  justify-content: center;
  border: 0;
  background: none;
  border-radius: var(--radius-full);
  color: var(--text-dim);
  text-transform: none;
  letter-spacing: 0;
}
body.homolog-mode #header-admin-btn { display: inline-flex; }
#header-admin-btn:hover { background: var(--bg-card-hover); color: var(--text-main); }
#header-admin-btn svg { width: 18px; height: 18px; margin: 0; stroke: currentColor; }

/* ==========================================================================
   ACESSIBILIDADE — foco e alvos de toque
   --------------------------------------------------------------------------
   A base não tinha NENHUM :focus-visible fora de .bounding-box: navegar por
   teclado era invisível. Um anel só, em Tinta, no app inteiro.
   ========================================================================== */
:where(a, button, input, select, textarea, summary, [tabindex]):focus-visible {
  outline: 2px solid var(--secondary);
  outline-offset: 2px;
  border-radius: var(--radius-sm);
}

input:focus-visible + .slider {
  outline: 2px solid var(--secondary);
  outline-offset: 2px;
}

/* Piso de alvo de toque nos controles compactos. Os chips do painel de
   ajuste ficam em 36px + 8px de gap = 44px de alvo efetivo. */
.tuner-chip,
.btn-avaliar-real,
.btn-detalhes-tecnicos { min-height: 36px; }

/* ==========================================================================
   BREAKPOINTS
   --------------------------------------------------------------------------
   Mobile é o padrão. 640px traz colunas à vitrine; 960px sobe a navegação
   para o topo — sem a moldura de celular, o desktop deixa de ser um telefone
   de 440px no meio de um fundo vazio.
   ========================================================================== */
@media (max-width: 439px) {
  :root { --gutter: var(--sp-4); }

  .brand-name { font-size: var(--fs-lg); }

  /* Só o ponto de estado: o rótulo por extenso continua na tela de
     resultados, que é onde ele muda alguma decisão. */
  .ia-badge { font-size: 0; gap: 0; }

  /* QA real em iPhone (Rodada 2.1): a foto sozinha já ocupava quase a tela
     inteira, empurrando headline/CTA abaixo da dobra. `max-height` é quem
     de fato governa a altura renderizada aqui (o aspect-ratio de 4/5 nunca
     "ganha" em nenhuma largura de celular real — a altura que ele pediria
     é sempre maior que o teto abaixo) — só esse valor precisa mudar, sem
     tocar aspect-ratio/object-fit/object-position. Não afeta 640px/960px:
     lá o aspect-ratio (16/9) ou o grid de 2 colunas já governam a altura,
     independente deste max-height. min-height (260px) fica abaixo de
     44vh em qualquer largura de celular real, então nunca entra em jogo. */
  .landing-hero { max-height: 44vh; min-height: 260px; }

  /* Presets ficaram grandes/protagonistas demais, competindo com o CTA.
     Os 3 assets são nativamente 1024×1024 (quadrados, confirmado byte a
     byte) — 1/1 não só encolhe o cartão como REMOVE o corte que o 3/4
     atual já faz (hoje corta as laterais da foto quadrada); escopado só a
     esta faixa, tablet/desktop continuam com o 3/4 de sempre. */
  .presets-section { margin-top: var(--sp-6); }
  .presets-grid { gap: var(--sp-2); }
  .preset-card { aspect-ratio: 1 / 1; }
}

@media (min-width: 640px) {
  :root { --gutter: var(--sp-6); }

  .presets-grid { grid-template-columns: repeat(4, 1fr); }
  /* Grid de cards de resultado (não mais .matches-grid, que só empilha
     seções — ver comentário acima da regra base): tablet ganha uma
     terceira coluna. .match-card-image-box fica sempre a 100% da coluna
     (Rodada 3), então não precisa de largura própria por breakpoint. */
  .resultados-secao { grid-template-columns: repeat(3, 1fr); gap: var(--sp-5); }
  .catalog-grid { grid-template-columns: repeat(3, 1fr); }

  /* Tela mais larga: uma foto tão alta (4/5) quanto a do mobile ficaria
     desproporcional — mais baixa, ainda editorial, sem virar faixa fina. */
  .landing-hero { aspect-ratio: 16 / 9; }
}

@media (min-width: 960px) {
  :root { --gutter: var(--sp-7); }

  /* A navegação sai do rodapé e vira uma linha sob o cabeçalho. */
  .bottom-nav {
    position: static;
    height: auto;
    order: 1;
    border-top: 0;
    border-bottom: 1px solid var(--border-glass);
    justify-content: flex-start;
    gap: var(--sp-5);
    padding: 0 var(--gutter);
    flex-shrink: 0;
  }
  .app-content {
    order: 2;
    padding-bottom: var(--sp-8);
  }
  .nav-item {
    flex: 0 0 auto;
    max-width: none;
    flex-direction: row;
    gap: var(--sp-2);
    min-height: 46px;
    font-size: var(--fs-sm);
    letter-spacing: 0;
    border-radius: 0;
    border-bottom: 2px solid transparent;
  }
  .nav-item.active { border-bottom-color: var(--secondary); }

  /* Desktop: quarta coluna no grid de resultados — --shell-max (1120px)
     já limita a largura, então 4 colunas ficam ~230-250px cada, ainda
     confortáveis pra foto 3:4 + nome + preço. */
  .resultados-secao { grid-template-columns: repeat(4, 1fr); gap: var(--sp-6); }

  .catalog-grid { grid-template-columns: repeat(4, 1fr); }

  /* Busca e filtros na mesma linha: um campo de 1120px de largura para
     digitar "blazer" é desperdício de linha e de atenção. */
  .search-filter-box { flex-direction: row; align-items: center; gap: var(--sp-4); }
  .search-input-wrapper { max-width: 360px; }
  .catalog-tabs { margin-left: auto; }
  .catalog-tabs .tab-btn { flex: 0 0 auto; }

  /* Landing: composição editorial de duas colunas, NUNCA a versão mobile
     esticada — mesma grade (--shell-max/--gutter) que o resto do app já
     usa nesta largura, nenhum "celular falso" no meio da tela. */
  #view-landing {
    display: grid;
    grid-template-columns: 1.1fr 1fr;
    align-items: center;
    gap: var(--sp-8);
  }
  .landing-hero {
    /* cancela o breakout mobile: aqui a foto é uma coluna contida, não
       full-bleed — o "sangramento" até a borda da tela só fazia sentido
       empilhada sobre o texto. */
    margin-inline: 0;
    width: 100%;
    aspect-ratio: auto;
    height: 100%;
    max-height: none;
    min-height: 560px;
    border-radius: var(--radius-lg);
  }
  .landing-hero::after { display: none; }
  .hero-text,
  .upload-card,
  .upload-actions,
  .text-search-divider,
  .text-search-card { text-align: left; align-items: flex-start; margin-inline: 0; }
  .presets-section,
  .privacy-toast { grid-column: 1 / -1; }
  .presets-section { margin-top: var(--sp-9); }

  /* Único breakpoint em que arrastar a foto é uma ação real (mouse,
     não touch) — reaparece só aqui. */
  .upload-desc { display: block; }
}
