/* ══════════════════════════════════════════════════════════════════════════
   SITE PÚBLICO /{slug} — folha de estilo
   Fase 1 do projeto de reescrita em SSR (23/08/2026).

   ⚠️ CONTRATO DE PARIDADE
   Cada valor aqui foi transcrito de lib/main.dart (a tela AgendamentoPage) e
   de lib/utils/site_themes.dart. O objetivo NÃO é "ficar parecido": é ficar
   igual. Onde houver número mágico, o comentário diz de onde ele veio.

   Conversões de Flutter pra CSS usadas aqui:
     Colors.black12  → rgba(0,0,0,.12)      Colors.grey[200] → #EEEEEE
     Colors.grey     → #9E9E9E              Colors.grey[300] → #E0E0E0
     Text sem size   → 14px                 Icon sem size    → 24px
     BoxShadow(blurRadius: b, offset: (x,y)) → box-shadow: x y b cor
   ══════════════════════════════════════════════════════════════════════════ */

/* ─── FONTES ─── (caminho ABSOLUTO: a página vive em /{slug} e em /_novo/{slug}) ───────────────────────────────────────────────────────────────
   Self-hosted, subset latin, 61 KB no total. `swap` é essencial: o texto
   aparece na hora com a fonte do sistema e troca quando a real chega — ao
   contrário do CanvasKit de hoje, onde NADA pinta antes da fonte baixar.
   Nunito é fonte VARIÁVEL: um arquivo só cobre 400→700.                     */
@font-face {
  font-family: 'Nunito';
  src: url('/fonts/nunito-latin-var.20fc9b6f.woff2') format('woff2-variations');
  font-weight: 400 700;
  font-display: swap;
}
@font-face {
  font-family: 'Poppins';
  src: url('/fonts/poppins-latin-500.289e0afc.woff2') format('woff2');
  font-weight: 500;
  font-display: swap;
}
@font-face {
  font-family: 'Poppins';
  src: url('/fonts/poppins-latin-600.872e8629.woff2') format('woff2');
  font-weight: 600;
  font-display: swap;
}
@font-face {
  font-family: 'Poppins';
  src: url('/fonts/poppins-latin-700.197a3cbd.woff2') format('woff2');
  font-weight: 700;
  font-display: swap;
}

/* ─── OS 4 TEMAS ───────────────────────────────────────────────────────────
   Espelho exato de lib/utils/site_themes.dart. O `-rgb` existe pra permitir
   rgba(var(--primary-rgb), .15) — evitei color-mix() de propósito: parte da
   base usa Android antigo, e rgba() funciona em tudo.                       */
:root,
[data-tema='classic_pink'] {
  --primary: #e5989b;
  --primary-rgb: 229, 152, 155;
  --bg: #fff9f9;
  --bg-rgb: 255, 249, 249;
  --text: #5d4b4b;
  --text-rgb: 93, 75, 75;
  --card: #ffffff;
}
[data-tema='rose_gold'] {
  --primary: #c5a059;
  --primary-rgb: 197, 160, 89;
  --bg: #fffdf5;
  --bg-rgb: 255, 253, 245;
  --text: #423d33;
  --text-rgb: 66, 61, 51;
  --card: #ffffff;
}
[data-tema='lavender'] {
  --primary: #9e9eca;
  --primary-rgb: 158, 158, 202;
  --bg: #f8f8ff;
  --bg-rgb: 248, 248, 255;
  --text: #444460;
  --text-rgb: 68, 68, 96;
  --card: #ffffff;
}
[data-tema='marrom_luxo'] {
  --primary: #e0c68e;
  --primary-rgb: 224, 198, 142;
  --bg: #1e1410;
  --bg-rgb: 30, 20, 16;
  --text: #f2f0e9;
  --text-rgb: 242, 240, 233;
  --card: #2c1e16;
}

/* ─── BASE ─────────────────────────────────────────────────────────────── */
/* ⚠️ SEM O FLASH AZUL DO TOQUE (só aparece no celular).
   `-webkit-tap-highlight-color` vem por padrão em rgba(51,181,229,.4) e pinta
   todo <button> tocado — serviço, dia e horário são todos <button>. O app usa
   GestureDetector, que NÃO tem feedback visual nenhum: o retorno é a própria
   mudança de estado (o card fica rosa, o + vira check). Deixar o azul do
   navegador seria uma divergência visível, e só em celular — foi assim que o
   Artur achou, em 24/08/2026. */
* { -webkit-tap-highlight-color: transparent; }

* { box-sizing: border-box; }

/* ⚠️ PARIDADE COM O CANVASKIT — não tirar sem decidir de novo.
   O site de hoje é Flutter/CanvasKit: ele DESENHA o próprio texto num canvas,
   então ignora o "escalonamento de texto" do Chrome e o tamanho de fonte do
   Android. Uma página HTML obedece aos dois. Sem esta linha, a cliente que
   aumentou a fonte do celular veria, no dia da virada, uma página de repente
   maior, com o texto quebrando em outro lugar e um horário a menos por linha —
   exatamente o "elas percebem" que este projeto promete não acontecer.
   Foi assim que o print do Artur deu 3 chips no site novo e 4 no app, no MESMO
   celular: impossível pela geometria (o chip do app é o MAIS largo), só
   explicável por o HTML estar inchado e o CanvasKit não.
   ⚖️ O CUSTO: quem aumentou a fonte por necessidade continua sem o aumento
   nesta página — igual a hoje, nem melhor nem pior. O pinça-pra-ampliar segue
   funcionando (não usamos user-scalable=no). Se um dia a decisão for HONRAR a
   preferência, é só apagar estas duas linhas — mas aí muda pras 693 de uma vez
   e deixa de ser paridade. */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* A folha do navegador reseta letter-spacing/line-height dentro de form
   controls — e serviço, dia e horário são todos <button>. Sem isto, a
   herança do bodyMedium para justo nos elementos que decidem o layout. */
button, input, textarea, select { letter-spacing: inherit; line-height: inherit; }
html, body { margin: 0; padding: 0; }
body {
  background: var(--bg);
  color: var(--text);
  font-family: 'Nunito', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  /* ⚠️ O DEFAULT DO Text() NÃO É SÓ O 14px.
     `Text(style: X)` não usa X puro: ele é MERGEADO no DefaultTextStyle, que
     dentro de um Scaffold é o `bodyMedium` do tema. No Material 3 esse
     bodyMedium é `fontSize 14 · letterSpacing 0.25 · height 1.43`, e TODO
     texto que não declara os seus herda os três — inclusive os que declaram
     só fontSize/fontWeight, como o chip de horário e o nome do serviço.
     A transcrição original pegou os letterSpacing/height ESCRITOS no
     main.dart e não os herdados: a página inteira saía 0,25 px por caractere
     mais estreita e ~1 px por linha mais baixa que o app. Medido em
     test/tipografia_site_test.dart; com estas três linhas os 10 rótulos
     medidos batem com o Flutter a ±0,03 px. */
  font-size: 14px;
  letter-spacing: .25px;
  line-height: 1.43;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}
.bb-ico { width: 24px; height: 24px; fill: currentColor; flex: none; display: block; }

/* ── TEXTO DIGITADO PELA PROFISSIONAL ─────────────────────────────────────
   Todo `Text` do Flutter preserva `\n`, tabulação e espaços consecutivos; o
   CSS colapsa os três. Medido em test/aviso_site_test.dart: "Antes depois"
   86,55 × "Antes  depois" 90,50, e cada `\n` acrescenta uma linha inteira.

   Não é caso raro: varredura de 26/08/2026 em produção achou **676 descrições
   de serviço com quebra de linha** (várias com listas de tópicos em `\t`),
   223 avisos com quebra, 90 nomes de serviço com espaço duplo — **178 das 724
   vitrines**. Sem isto, a descrição da profissional vira um parágrafo corrido
   e o card encolhe.

   `break-word` junto porque o Flutter QUEBRA DENTRO DA PALAVRA quando ela não
   cabe (200 letras sem espaço = 6 linhas, medido), em vez de estourar.

   ⚠️ NÃO subir isto pro `body`: o site.html tem quebra de linha e indentação
   ENTRE as tags, e com pre-wrap elas virariam espaço visível. Só vale em
   elemento cujo conteúdo é escrito por `textContent`/template, sem markup em
   volta — que é o caso de todos os daqui. */
.bb-nome,
.bb-categoria span,
.bb-servico-nome,
.bb-servico-desc,
.bb-resumo-servicos,
.bb-sinal-sub,
.bb-sinal-prof,
#sinal-politica-txt,
#recibo-servicos,
#recibo-estudio {
  white-space: pre-wrap;
  overflow-wrap: break-word;
}
[hidden] { display: none !important; }

/* ⚠️ SEM max-width, de propósito. Eu tinha posto 520px porque no desktop fica
   mais bonito — mas o app NÃO tem limite de largura, ele ocupa a tela toda.
   Isso é divergência visual, e a regra deste projeto é paridade sem exceção.
   Se um dia isso mudar, muda no Flutter e aqui, no mesmo commit. */
.bb-wrap { margin: 0 auto; }

/* ─── ESTADO: CARREGANDO ───────────────────────────────────────────────────
   main.dart: Column[ LinearProgressIndicator(minHeight:3), Center(Opacity(.5,
   Icon(auto_awesome, size:40))) ] sobre fundo do tema.                      */
.bb-carregando { position: fixed; inset: 0; background: var(--bg); display: flex; flex-direction: column; }
.bb-barra { height: 3px; background: rgba(var(--primary-rgb), .1); overflow: hidden; flex: none; }
.bb-barra::after {
  content: ''; display: block; height: 100%; width: 40%;
  background: var(--primary); animation: bb-indeterminada 1.2s ease-in-out infinite;
}
@keyframes bb-indeterminada {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(350%); }
}
.bb-carregando-centro { flex: 1; display: flex; align-items: center; justify-content: center; }
.bb-carregando-centro .bb-ico { width: 40px; height: 40px; color: var(--primary); opacity: .5; }

/* ─── HEADER ───────────────────────────────────────────────────────────────
   padding: only(top:40,bottom:30,left:20,right:20) · card · radius bottom 32
   shadow: black12 blur10 offset(0,2) — no marrom_luxo, black@30%           */
.bb-header {
  background: var(--card);
  border-radius: 0 0 32px 32px;
  padding: 40px 20px 30px;
  box-shadow: 0 2px 10px rgba(0, 0, 0, .12);
  text-align: center;
  position: relative;
}
[data-tema='marrom_luxo'] .bb-header { box-shadow: 0 2px 10px rgba(0, 0, 0, .3); }

/* "BEAUTY BOOK": Row(center)[ Icon(menu_book_rounded,18), SizedBox(6),
   Text(poppins 12 w600 ls2 text@50%) ] */
.bb-marca {
  display: flex; align-items: center; justify-content: center; gap: 6px;
  color: rgba(var(--text-rgb), .5);
}
.bb-marca .bb-ico { width: 18px; height: 18px; color: var(--primary); }
.bb-marca span {
  font-family: 'Poppins', sans-serif; font-size: 12px; font-weight: 600; letter-spacing: 2px;
}

/* SizedBox(height: 25) depois da marca */
.bb-header-corpo { margin-top: 25px; }

/* IconButton(arrow_back_ios, size:20) alinhado à esquerda — só aparece nas
   telas de data/hora e formulário. */
/* No Flutter é Align(centerLeft, IconButton(...)) DENTRO da Column, ou seja,
   ocupa uma linha própria e empurra a foto pra baixo. Ficar absoluto deixaria
   o header ~48px mais curto quando o botão aparece — não é paridade. */
.bb-voltar {
  display: block; margin: 0 auto 0 0;
  background: none; border: 0; padding: 12px; cursor: pointer;
  color: var(--text); line-height: 0; border-radius: 50%;
}
.bb-voltar .bb-ico { width: 20px; height: 20px; }
.bb-voltar:active { background: rgba(var(--text-rgb), .08); }

/* Container 135x135, padding 4, border 2.5 primary, ClipOval
   `position: relative` é só a âncora do indicador de carregando abaixo — não
   muda geometria nenhuma (o elemento já era estático e continua no fluxo).  */
.bb-foto {
  width: 135px; height: 135px; margin: 0 auto; padding: 4px;
  border: 2.5px solid var(--primary); border-radius: 50%;
  position: relative;
}
/* loadingBuilder do Image.network (main.dart ~1447):
   CircularProgressIndicator(strokeWidth: 2, color: Color(0xFFE5989B)).
   ⚠️ O ROSA É CRAVADO no main.dart, não é `tema.primary` — no tema marrom ou
   no dourado o app gira rosa aqui mesmo assim. Esquisitice preservada.
   ⚠️ E sem trilha: aquele CircularProgressIndicator não passa
   `backgroundColor`, então o resto do anel é transparente — ao contrário do
   `.bb-spinner` dos horários, que passa. 36px é o tamanho padrão do widget.
   Sai da tela no `load` (e a caixa inteira vira silhueta no `error`).       */
.bb-foto-carregando {
  position: absolute; top: 50%; left: 50%; width: 36px; height: 36px;
  margin: -18px 0 0 -18px; border-radius: 50%;
  border: 2px solid transparent; border-top-color: #e5989b;
  animation: bb-girar .9s linear infinite;
}
.bb-foto img, .bb-foto .bb-foto-vazia {
  width: 100%; height: 100%; border-radius: 50%; object-fit: cover; display: block;
}
.bb-foto .bb-foto-vazia {
  display: flex; align-items: center; justify-content: center; color: #9e9e9e;
}
.bb-foto .bb-foto-vazia .bb-ico { width: 55px; height: 55px; }

/* SizedBox(20) · poppins 22 bold ls1.1 · uppercase
   ⚠️ NÃO devolver o `line-height: 1.25` que estava aqui: era invenção do
   esqueleto (53f09b1), não veio do main.dart. O Text do nome não declara
   height nenhum, então herda o 1.43 do bodyMedium — e o nome do estúdio é
   justamente um dos textos que mais quebram em duas linhas. 22×1.25 = 27,5
   contra os 31,46 do app: 4 px por linha, na primeira coisa que a cliente vê. */
.bb-nome {
  margin: 20px 0 0; font-family: 'Poppins', sans-serif; font-size: 22px; font-weight: 700;
  letter-spacing: 1.1px; color: var(--text); text-transform: uppercase;
  /* 22 × 1.43 = 31,46 → 31,00 no Flutter — aqui ele arredonda pra BAIXO
     (test/tipografia_site_test.dart). O nome do estúdio quebra em 2 linhas em
     boa parte das contas, então o desvio dobrava. */
  line-height: 31px;
}
/* SizedBox(6) · nunito w500 text@60% */
.bb-subtitulo { margin: 6px 0 0; font-weight: 500; color: rgba(var(--text-rgb), .6); }

/* ─── AVISO DO SITE ────────────────────────────────────────────────────────
   margin fromLTRB(20,0,20,20) · padding 16 · radius 16
   bg primary@10% (marrom: card) · border 1 primary@30% (marrom: 1.5 @60%)  */
.bb-aviso {
  display: flex; align-items: center; gap: 12px;
  margin: 0 20px 20px; padding: 16px; border-radius: 16px;
  background: rgba(var(--primary-rgb), .1);
  border: 1px solid rgba(var(--primary-rgb), .3);
}
[data-tema='marrom_luxo'] .bb-aviso {
  background: var(--card); border-width: 1.5px; border-color: rgba(var(--primary-rgb), .6);
}
.bb-aviso .bb-ico { color: var(--primary); }
/* `pre-wrap` porque o aviso é digitado num campo de VÁRIAS LINHAS e o widget
   original é um `Text` simples: o Flutter respeita o `\n` E preserva espaços
   consecutivos (medido em test/aviso_site_test.dart — "Antes depois" 86,55 vs
   "Antes  depois" 90,50). Sem esta linha o CSS colapsava tudo e o recado dela
   virava um parágrafo corrido — divergência achada em 26/08/2026 comparando
   /agendaglau nos dois sites. `pre-line` NÃO serve: colapsa os espaços. */
.bb-aviso p {
  margin: 0; font-size: 14px; font-weight: 600; color: var(--text);
  /* 14 × 1.43 = 20,02 → o Flutter desenha 20,00 (test/aviso_site_test.dart) */
  line-height: 20px;
  white-space: pre-wrap;
  overflow-wrap: break-word;
}

/* ─── LISTA DE SERVIÇOS ────────────────────────────────────────────────── */
.bb-servicos { padding: 20px; }

/* Título de categoria: padding fromLTRB(8,32,0,16) · poppins 13 bold ls2
   primary · Divider primary@20% com SizedBox(12) antes */
.bb-categoria { display: flex; align-items: center; gap: 12px; padding: 32px 0 16px 8px; }
.bb-categoria span {
  font-family: 'Poppins', sans-serif; font-size: 13px; font-weight: 700;
  letter-spacing: 2px; color: var(--primary); text-transform: uppercase;
  /* 13 × 1.43 = 18,59 → 19,00 no Flutter (test/tipografia_site_test.dart) */
  line-height: 19px;
}
.bb-categoria i { flex: 1; height: 1px; background: rgba(var(--primary-rgb), .2); }

/* Card do serviço: margin bottom 16 · padding 20 · radius 20 · border 2
   (transparent → primary quando selecionado) · bg card → primary@15%
   shadow black@3% blur 10 · AnimatedContainer 200ms                        */
/* ⚠️ SEM `gap`. O Row do original é `[Expanded(Column), Icon]` e não tem
   SizedBox nenhum entre os dois — a coluna de texto vai até encostar no (+).
   Um `gap: 12px` aqui (era o que estava no ar até 26/08/2026) tira 12 px de
   TODO card: coluna 271 em vez de 283, e nome/descrição quebram mais cedo.
   Achado comparando /agendanails lado a lado, medido em
   test/card_servico_test.dart. Todos os outros `gap` do arquivo têm um
   SizedBox correspondente citado no comentário — este não tinha. */
.bb-servico {
  display: flex; align-items: center; width: 100%; text-align: left;
  margin-bottom: 16px; padding: 20px; border-radius: 20px;
  /* ⚠️ `background-color` (forma LONGA), não `background`. Com a abreviada
     contendo var(), o navegador trata a troca como substituição pendente e
     NÃO ANIMA — a seleção fica seca, sem o deslize pro rosa que o
     AnimatedContainer do app faz. Descoberto em 23/08 porque o Artur reparou
     na diferença; zero evento de transição disparava. */
  background-color: var(--card); border: 2px solid transparent;
  box-shadow: 0 0 10px rgba(0, 0, 0, .03);
  cursor: pointer; font: inherit; color: inherit;
  /* AnimatedContainer(duration: 200ms) do app. A curva padrão dele é
     Curves.linear, não a `ease` do CSS — por isso `linear` explícito. */
  transition: background-color .2s linear, border-color .2s linear;
}
.bb-servico[aria-pressed='true'] {
  background-color: rgba(var(--primary-rgb), .15); border-color: var(--primary);
}
.bb-servico-info { flex: 1; min-width: 0; }
/* ⚠️ `line-height` em PX, não em razão. O Flutter ARREDONDA a altura de linha
   pro pixel inteiro: 16 × 1.43 (o height do bodyMedium do M3) = 22,88 e ele
   desenha 23,00. O CSS não arredonda, e a diferença some sozinha em cada
   elemento mas SOMA na coluna do card. Medido em test/card_servico_test.dart
   e test/tipografia_site_test.dart. */
.bb-servico-nome {
  display: block;
  font-family: 'Poppins', sans-serif; font-size: 16px; font-weight: 600; color: var(--text);
  line-height: 23px;
}
/* padding only(top:4,bottom:8) · nunito 13 text@60% height 1.2 */
.bb-servico-desc {
  display: block;
  /* 13 × 1.2 = 15,6 → o Flutter desenha 16,00 por linha. Era o maior desvio
     da página (0,4 px POR LINHA): numa descrição de 4 linhas o card ficava
     1,6 px mais baixo, e a vitrine inteira subia. */
  padding: 4px 0 8px; font-size: 13px; line-height: 16px; color: rgba(var(--text-rgb), .6);
}
.bb-servico-preco { display: block; font-weight: 700; color: var(--primary); line-height: 20px; }
.bb-servico > .bb-ico { color: var(--primary); }

/* Selo de sinal: padding sym(h:8,v:3) · radius 8 · bg primary@15%
   Icon(verified_outlined,13) + SizedBox(4) + poppins 11 w600 primary        */
/* ⚠️ `flex` + `fit-content`, NÃO `inline-flex`. O selo é um filho de
   `Column(crossAxisAlignment: start)` no Flutter: encolhe pro conteúdo e
   ocupa uma faixa própria. Como `inline-flex` ele vira conteúdo de linha e o
   navegador reserva o STRUT da fonte do pai embaixo dele — 1,02 px a mais na
   coluna do card, que o Flutter não tem. Regra geral do projeto: o
   shrink-to-fit do Flutter é `width: fit-content; max-width: 100%`. */
.bb-selo {
  display: flex; width: fit-content; max-width: 100%;
  align-items: center; gap: 4px; margin-top: 6px;
  padding: 3px 8px; border-radius: 8px; background: rgba(var(--primary-rgb), .15);
  font-family: 'Poppins', sans-serif; font-size: 11px; font-weight: 600; color: var(--primary);
  /* 11 × 1.43 = 15,73 → 16,00 no Flutter. Com o ícone de 13 no meio, a caixa
     do selo fecha em 22 (3 + 16 + 3). */
  line-height: 16px;
}
.bb-selo .bb-ico { width: 13px; height: 13px; }

/* ─── DATA E HORA ──────────────────────────────────────────────────────── */
.bb-datahora { padding-top: 20px; }

/* SizedBox(height:90) com ListView horizontal.
   ⚠️ A borda vale 20, NÃO 15. São DOIS recuos somados no Flutter:
   ListView(padding: sym(h:15)) + Container(margin: sym(h:5)) = 20 nas pontas.
   O mesmo margin de 5 dos DOIS lados vira o gap de 10 entre os cards — a
   primeira transcrição pegou o gap certo e a ponta errada, e o dia 24 nascia
   5 px pra esquerda do que nasce no app. Travado em test/geometria_site_test.mjs. */
/* ─── O MÊS DA FAIXA (01/09/2026, junto com a faixa de 60 → 90 dias) ──────
   Duas peças, as duas em tokens do tema (funcionam nos 4, inclusive no
   marrom_luxo escuro):
   · .bb-mes-faixa  — rótulo acima da faixa; o site.js troca o texto conforme
     o scroll (mês do primeiro chip visível). min-height segura o layout
     ANTES do texto entrar — sem ele a tela pula 1 linha no primeiro paint.
   · .bb-mes-virada — separador fininho na virada de mês, dentro da faixa,
     com o mês novo na vertical. Decorativo (aria-hidden); não é botão.     */
.bb-mes-faixa {
  display: inline-block; margin: 0 20px 8px; padding: 5px 14px; height: 26px;
  box-sizing: border-box; line-height: 16px;
  font-family: 'Poppins', sans-serif; font-size: 11px; font-weight: 700;
  letter-spacing: .08em; text-transform: uppercase;
  color: var(--primary);
  background: rgba(var(--primary-rgb), .10);
  border-radius: 999px;
}
/* Antes do primeiro render o texto ainda não entrou: esconde o pill (senão
   vira uma bolinha vazia) mas SEGURA a altura, pra tela não pular. */
.bb-mes-faixa:empty { visibility: hidden; }
.bb-mes-virada {
  flex: none; align-self: stretch;
  display: flex; align-items: center; justify-content: center;
  writing-mode: vertical-rl;
  font-family: 'Poppins', sans-serif; font-size: 11px; font-weight: 700;
  letter-spacing: .14em; color: rgba(var(--primary-rgb), .60);
  border-left: 1px solid rgba(var(--text-rgb), .14);
  padding: 0 2px; margin-left: 2px;
}

.bb-dias {
  display: flex; gap: 10px; height: 90px; padding: 0 20px; overflow-x: auto;
  scrollbar-width: none; -webkit-overflow-scrolling: touch;
}
.bb-dias::-webkit-scrollbar { display: none; }
/* Container 60 de largura, margin sym(h:5) → virou gap 10 · radius 15
   border grey[200] · bg card → primary quando selecionado                   */
.bb-dia {
  flex: none; width: 60px; border-radius: 15px; border: 1px solid #eeeeee;
  background: var(--card); color: var(--text);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  cursor: pointer; font: inherit; padding: 0;
}
.bb-dia[aria-pressed='true'] { background: var(--primary); border-color: transparent; color: #fff; }
.bb-dia-semana { font-size: 12px; }                    /* TextStyle(fontSize:12) puro */
.bb-dia-num { font-family: 'Poppins', sans-serif; font-size: 18px; font-weight: 700; }
/* Dia bloqueado: grey[300] + lineThrough (main.dart ~1628/1640). Continua
   CLICÁVEL — o original também é; ver o comentário em renderDias no site.js.
   ⚠️ A ordem das duas regras importa: selecionado ganha do bloqueado na COR
   (o Flutter faz `selecionado ? white : (bloqueado ? grey : text)`), mas o
   risco fica nos DOIS — lá o `decoration` não olha `selecionado`. */
.bb-dia[data-bloqueado='1'] { color: #e0e0e0; text-decoration: line-through; }
.bb-dia[data-bloqueado='1'][aria-pressed='true'] { color: #fff; }

/* SizedBox(30) · poppins w600 primary · SizedBox(15) */
.bb-label-horarios {
  margin: 30px 0 15px; text-align: center;
  font-family: 'Poppins', sans-serif; font-weight: 600; color: var(--primary);
}

/* ⚠️ DIVERGÊNCIA DELIBERADA DO APP — decidida com o Artur em 25/08/2026.
   NÃO "restaurar a paridade" aqui sem ler isto.

   O app usa Wrap(spacing:10, runSpacing:10) dentro de Padding(sym(h:20)), e o
   chip tem largura de conteúdo (texto + 40 de padding + 2 de borda = 80,33).
   Isso faz o número de chips por linha depender de uma conta de sub-pixel:

     tela 384 → 3 por linha, sobrando 83 px  (falta 7,31 pro 4º)
     tela 390 → 3 por linha, sobrando 89 px  (falta 1,31)
     tela 391 → 3 por linha, sobrando 90 px  (falta 0,31)  ← o celular do Artur
     tela 392 → 4 por linha, sobrando 0,7 px

   Em 391 px sobra uma vaga de chip INTEIRA e falta um terço de pixel. O app
   sofre do mesmo (falta 0,28 px nele) — copiar isso seria copiar um defeito, e
   ainda por cima um que muda de resposta a cada aparelho.

   A grade tira a decisão do sub-pixel: 4 colunas iguais que dividem a largura
   exata. Em 391 px cada coluna dá 80,25 px — o chip fica do MESMO tamanho de
   hoje, só que cabem 4 e não sobra buraco. Em telas menores ele encolhe em vez
   de deixar vão. Acima de 480 px volta a fluir, pra tablet/desktop não virarem
   4 chips gigantes.

   O padding horizontal do chip vira 4 porque quem manda na largura passa a ser
   a coluna; o vertical continua 12, então a altura segue 46,02 — igual ao app.
   Reverter é trocar este bloco pelo `display:flex; flex-wrap:wrap` e o padding
   de volta pra 20. */
.bb-horarios {
  display: grid; grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px; padding: 0 20px;
}
@media (min-width: 480px) {
  .bb-horarios { grid-template-columns: repeat(auto-fill, minmax(80px, 1fr)); }
}
/* radius 12 · border grey[200] · nunito bold · padding vertical 12 (do app) */
.bb-horario {
  padding: 12px 4px; text-align: center; border-radius: 12px; border: 1px solid #eeeeee;
  background: var(--card); color: var(--text); font: inherit; font-weight: 700; cursor: pointer;
}
.bb-horario[aria-pressed='true'] { background: var(--primary); border-color: transparent; color: #fff; }

/* Spinner rosa no lugar dos chips enquanto a function busca
   (SizedBox 36 · strokeWidth 3 · padding sym(h:20,v:24))                    */
.bb-horarios-carregando { display: flex; justify-content: center; padding: 24px 20px; }
.bb-spinner {
  width: 36px; height: 36px; border-radius: 50%;
  border: 3px solid rgba(var(--primary-rgb), .15); border-top-color: var(--primary);
  animation: bb-girar .9s linear infinite;
}
@keyframes bb-girar { to { transform: rotate(360deg); } }

.bb-datahora-fim { height: 100px; }                    /* SizedBox(height:100) */

/* ─── FORMULÁRIO ───────────────────────────────────────────────────────── */
.bb-form { padding: 25px; }

/* TextField do Material, variante `filled`:
     fillColor: card · radius 15 · borderSide: none · prefixIcon primary
     labelText → label FLUTUANTE POR DENTRO do campo (não é label acima!)
     hintText  → só aparece quando o label já subiu (foco ou preenchido)
     labelStyle: Colors.grey
   Altura 56px = padrão do Material pra campo filled com label.            */
.bb-campo { position: relative; margin-bottom: 20px; }
.bb-campo > .bb-ico {
  position: absolute; left: 12px; top: 28px; transform: translateY(-50%);
  color: var(--primary); pointer-events: none; z-index: 1;
}
.bb-campo input {
  width: 100%; height: 56px; padding: 22px 16px 6px 48px;
  border: 0; border-radius: 15px;
  background: var(--card); color: var(--text); font: inherit; font-size: 16px;
  /* TextField usa `bodyLarge` (16 · ls 0.5 · height 1.5), NÃO o bodyMedium
     do resto da página. Conferido em test/tipografia_site_test.dart. */
  letter-spacing: .5px; line-height: 1.5;
}
.bb-campo input:focus { outline: 2px solid var(--primary); outline-offset: -2px; }
/* O hint só existe depois que o label sobe — igual ao Material. */
.bb-campo input:placeholder-shown:not(:focus)::placeholder { color: transparent; }
.bb-campo input::placeholder { color: #9e9e9e; }
.bb-campo label {
  position: absolute; left: 48px; top: 28px; transform: translateY(-50%);
  color: #9e9e9e; font-size: 16px; letter-spacing: .5px; pointer-events: none;
  transition: top .15s ease, font-size .15s ease;
}
.bb-campo input:focus + label,
.bb-campo input:not(:placeholder-shown) + label { top: 15px; font-size: 12px; }
/* Medido em test/form_site_test.dart: a mensagem nasce **16 px à direita** da
   borda do campo (é o contentPadding do InputDecorator, não os 4 da dica) e
   **4 px abaixo** da caixa de 56. A altura é 16 e não 17,16 porque o
   `errorStyle` do Material 3 é `bodySmall` (12 × 1.33), não o bodyMedium
   (1.43) do resto da página — com erro, o campo vai de 56 pra 76. */
.bb-campo-erro {
  margin: 4px 0 0 16px; color: #d32f2f; font-size: 12px; line-height: 16px;
}
/* `Padding(symmetric(horizontal: 4))` no original — 4 dos DOIS lados, então
   333 de largura, não 337. Com 337 a dica quebrava em ponto diferente.
   11 × 1.43 = 15,73 → o Flutter desenha 16 (dica de 2 linhas = 32). */
.bb-campo-dica {
  margin: 8px 4px 0; font-size: 11px; line-height: 16px;
  color: rgba(var(--text-rgb), .55);
}

/* SizedBox(40) · padding 20 · radius 20 · bg primary@10%
   ⚠️ `width: fit-content` NÃO é enfeite — é o que reproduz o app.
   No main.dart (~1772) esse Container NÃO tem `width`. Ele é filho de um
   Column com crossAxisAlignment default (center), então recebe restrição
   FROUXA e encolhe pro tamanho do conteúdo: quem estica a coluna pra largura
   toda são os TextField, e o resumo é o único filho que encolhe. Virou <div>
   no CSS, que é bloco, e passou a ocupar a linha inteira — o Artur reparou na
   hora ("no original era quadradinho, não retângulo").
   Medido em test/resumo_largura_test.dart, tela de 391 px:
     conteúdo curto  → 169,01 px, centrado (esquerda 111)
     conteúdo longo  → cresce até os 341 disponíveis e quebra a linha
   `fit-content` = min(max-content, disponível), que é exatamente isso. */
.bb-resumo {
  width: fit-content; max-width: 100%; margin: 40px auto 0;
  padding: 20px; border-radius: 20px;
  background: rgba(var(--primary-rgb), .1); text-align: center;
}
.bb-resumo-titulo {
  font-family: 'Poppins', sans-serif; font-size: 12px; font-weight: 700; color: var(--text);
  line-height: 17px;
}
/* Alturas em px medido (test/form_site_test.dart): RESUMO 17 · data 26 ·
   serviços 20 · total 20 — os desvios de razão se cancelavam no total do card
   (137,92 contra 138) e escondiam que cada linha estava errada. */
.bb-resumo-quando {
  margin-top: 10px; line-height: 26px;
  font-family: 'Poppins', sans-serif; font-size: 18px; font-weight: 700; color: var(--text);
}
.bb-resumo-servicos { color: rgba(var(--text-rgb), .7); line-height: 20px; }
.bb-resumo-total { margin-top: 5px; font-weight: 700; color: var(--primary); line-height: 20px; }

/* ElevatedButton minimumSize(∞,55) · radius 15 · branco bold ls1.1 */
/* ⚠️ TERCEIRA DIVERGÊNCIA DELIBERADA DO PROJETO (25/08/2026).
   O app troca o rótulo por um `CircularProgressIndicator` branco e mais nada.
   O site escrevia "ENVIANDO..." — isso era invenção, e sai.
   Mas medi a espera nos logs do Cloud Run: `iniciarSinalAgendamento` leva
   2,47–2,61 s de servidor, que no celular viram 3–4 s. Spinner MUDO por 4 s
   num botão de pagamento é onde a cliente acha que travou. Então além do
   spinner vai um rótulo que diz o PRÓXIMO PASSO, não o estado interno:
   "Gerando seu Pix…" / "Reservando seu horário…".
   O spinner é 20 px (e não os 36 do Material) porque agora divide espaço com
   texto — é o mesmo ritmo do .bb-btn-whats, ícone 20 + gap 10.
   Reverter: apagar o span do spinner e voltar a trocar o textContent. */
.bb-btn-confirmar {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  width: 100%; height: 55px; margin-top: 40px; border: 0; border-radius: 15px;
  background: var(--primary); color: #fff; cursor: pointer;
  font: inherit; font-weight: 700; letter-spacing: 1.1px;
}

/* Ação secundária do modal de "não consegui confirmar": discreta de
   propósito. A saída que resolve é o WhatsApp; tentar de novo por aqui
   quase sempre falha de novo, mas não pode ser uma porta trancada. */
.bb-modal-link {
  display: block;
  margin: 14px auto 0;
  padding: 6px 10px;
  background: none;
  border: 0;
  font: inherit;
  font-size: 13px;
  color: rgba(var(--text-rgb), .6);
  text-decoration: underline;
  cursor: pointer;
}
.bb-spinner-botao {
  display: none; flex: none; width: 20px; height: 20px; border-radius: 50%;
  border: 2.5px solid rgba(255, 255, 255, .35); border-top-color: #fff;
  animation: bb-girar .9s linear infinite;
}
.bb-btn-confirmar[data-enviando='1'] .bb-spinner-botao { display: block; }
/* Rede de proteção do pior caso (cold start, 4G ruim): só aparece passados 6 s.
   O botão já fica desabilitado, então o risco não é o clique duplo — é ela
   DESISTIR e sair achando que quebrou. */
.bb-btn-demora {
  margin: 10px 0 0; text-align: center; font-size: 13px;
  color: rgba(var(--text-rgb), .7);
}
.bb-btn-confirmar[disabled] { opacity: .7; cursor: default; }

/* ─── BOTÃO FLUTUANTE ──────────────────────────────────────────────────────
   AnimatedContainer(height: 85 ↔ 0, 300ms easeOut) · padding sym(h:45,v:15)
   bg background@95% · shadow black@5% blur10 offset(0,-5)
   ElevatedButton minimumSize(∞,48) · radius 25 · poppins bold 13 ls1.2      */
.bb-rodape {
  position: sticky; bottom: 0; height: 0; overflow: hidden;
  transition: height .3s cubic-bezier(0, 0, .58, 1);
}
.bb-rodape[data-visivel='1'] { height: 85px; }
.bb-rodape-int {
  padding: 15px 45px;
  background: rgba(var(--bg-rgb), .95);
  box-shadow: 0 -5px 10px rgba(0, 0, 0, .05);
}
.bb-btn-continuar {
  width: 100%; height: 48px; border: 0; border-radius: 25px;
  background: var(--primary); color: #fff; cursor: pointer;
  font-family: 'Poppins', sans-serif; font-size: 13px; font-weight: 700; letter-spacing: 1.2px;
}

/* ─── PERFIL NÃO ENCONTRADO ────────────────────────────────────────────────
   Porta PaginaErroPerfil (main.dart ~2461). A versão anterior era escrita do
   zero — "Ops!" com uma frase, sem ícone e sem botão. Todos os valores abaixo
   estão medidos em test/telas_erro_test.dart, tela de 391 px.

   ⚠️ O FUNDO É FIXO `#FCF9F8`, NÃO `var(--bg)`. O Scaffold dessa página usa
   `backgroundColor: const Color(0xFFFCF9F8)` — cor cravada, enquanto o ícone e
   o título usam `tema.primary`/`tema.text`. Fica assim de propósito: é o que o
   app faz. (Na prática o tema aqui é sempre o classic_pink, porque o tema vem
   do perfil e nesta tela o perfil não existe.)                              */
.bb-tela-erro {
  min-height: 100vh; display: flex; flex-direction: column;
  align-items: center; justify-content: center; text-align: center; padding: 40px;
  background: #FCF9F8;
}
/* Icon(search_off_rounded, size: 100, color: tema.primary) */
.bb-erro-ico { width: 100px; height: 100px; fill: var(--primary); display: block; }
/* SizedBox(32) · poppins 22 bold · tema.text */
.bb-tela-erro h1 {
  margin: 32px 0 0; font-family: 'Poppins', sans-serif; font-size: 22px;
  font-weight: 700; color: var(--text);
}
/* SizedBox(16) · nunito 16 · Colors.grey[600] = #757575 (NÃO é text@70%) */
.bb-tela-erro p { margin: 16px 0 0; font-size: 16px; color: #757575; }
/* SizedBox(40) · OutlinedButton: borda primary, padding sym(h:30,v:15),
   radius 20. O rótulo herda o labelLarge do M3 (14 · ls 0.1) e o `Text` só
   troca cor e peso — por isso ls 0.1 explícito, não o 0.25 do body.
   Medido: 219,95 x 50.                                                      */
.bb-erro-voltar {
  display: inline-flex; align-items: center; justify-content: center;
  margin-top: 40px; padding: 15px 30px; border: 0; border-radius: 20px;
  /* ⚠️ `outline` com deslocamento negativo, NÃO `border`. O OutlinedButton
     pinta a borda SOBRE a forma: ela não soma ao tamanho. Com `border: 1px` o
     botão saía 221,95 x 52,02 contra os 219,95 x 50,00 do app — 2 px em cada
     dimensão, que é exatamente a borda dos dois lados. O outline pinta pra
     dentro e não entra no layout, então os números de padding continuam sendo
     os do main.dart (sym(h:30,v:15)) em vez de virarem 29/14 compensados. */
  outline: 1px solid var(--primary); outline-offset: -1px;
  color: var(--primary); font-size: 14px; font-weight: 700; letter-spacing: .1px;
  text-decoration: none; cursor: pointer; background: none;
}

/* ─── SEM CONEXÃO ──────────────────────────────────────────────────────────
   Porta _buildTelaOffline (main.dart ~1155). A tela é só o ícone; o texto e a
   ação vivem num SnackBar FLUTUANTE persistente (duration: 1 dia).           */
.bb-tela-offline {
  min-height: 100vh; background: var(--bg);
  display: flex; align-items: center; justify-content: center;
}
/* Opacity(0.5) > Icon(wifi_off, primary, size: 40) */
.bb-offline-ico { width: 40px; height: 40px; fill: var(--primary); opacity: .5; display: block; }

/* SnackBar `behavior: floating` do Material 3. Medido na tela de 391:
   caixa 361x68 (margem 15 dos lados, 10 do fundo) · radius 4 · fundo #322F35
   (`colorScheme.inverseSurface`) · texto #F5EFF7 (`onInverseSurface`) 14 px,
   a 16 do bordo e 14 do topo · ação #D0BCFF (`inversePrimary`).
   ⚠️ A ação é ROXA mesmo. Não vem da cor da marca: `ThemeData(primaryColor:)`
   não semeia o colorScheme, então o M3 usa a paleta base. É o que a cliente
   vê no app hoje.                                                            */
.bb-snack {
  position: fixed; left: 15px; right: 15px; bottom: 10px; z-index: 200;
  min-height: 68px; box-sizing: border-box;
  display: flex; align-items: flex-start; gap: 8px;
  padding: 14px 16px; border-radius: 4px;
  background: #322F35; color: #F5EFF7; font-size: 14px; text-align: left;
  box-shadow: 0 3px 5px rgba(0, 0, 0, .2);
}
.bb-snack span { flex: 1; min-width: 0; }
.bb-snack button {
  flex: none; align-self: center; margin: 0; padding: 8px;
  background: none; border: 0; cursor: pointer;
  /* `labelLarge` do M3, que é o que o SnackBarAction usa: peso 500, não 700.
     Com 700 o rótulo saía 2,7 px mais largo e começava em 253,7 em vez dos
     256,4 medidos no original (test/telas_erro_test.dart). O recuo da direita
     já batia: 24 px até a borda da caixa nos dois. */
  color: #D0BCFF; font: inherit; font-weight: 500; letter-spacing: .1px;
}

/* ─── DIALOG DE SUCESSO ────────────────────────────────────────────────────
   Dialog(barrierDismissible:false) · radius 30 · padding 30 · bg branco
   selo: padding 20 · círculo · primary@10% · Icon(check_rounded, 50) primary
   título: poppins bold · texto: Color(0xFF4A4A4A)
   botão: 0xFF25D366 (verde do WhatsApp) · altura 55 · branco             */
/* ⚠️ O recuo é 40 na horizontal e 24 na vertical: é o `insetPadding` PADRÃO do
   Dialog do Material (EdgeInsets.symmetric(horizontal: 40, vertical: 24)), não
   um número escolhido. Estava 24 dos quatro lados e a caixa saía 32 px mais
   larga que a do app. */
.bb-modal {
  position: fixed; inset: 0; z-index: 100;
  display: flex; align-items: center; justify-content: center; padding: 24px 40px;
  background: rgba(0, 0, 0, .45);
}
/* ⚠️ SEM max-width, de propósito — medido: o Dialog do app não tem teto.
   Numa tela de 1280 a caixa dele dá 1200 px (sempre tela − 80), porque o botão
   do WhatsApp usa minimumSize(double.infinity, 55) e estica a caixa até o
   limite. O `max-width: 380px` que estava aqui era invenção. Mesma regra do
   .bb-wrap: se um dia limitar, limita no Flutter e aqui, no mesmo commit.
   Medido em test/modais_largura_test.dart. */
.bb-modal-caixa {
  /* 30 nos quatro lados + o SizedBox(12) que fecha a Column = 42 embaixo. */
  background: #fff; border-radius: 30px; padding: 30px 30px 42px; text-align: center;
  width: 100%;
}
.bb-modal-selo {
  width: 90px; height: 90px; margin: 0 auto; border-radius: 50%;
  background: rgba(var(--primary-rgb), .1);
  display: flex; align-items: center; justify-content: center;
}
.bb-modal-selo .bb-ico { width: 50px; height: 50px; color: var(--primary); }
/* SizedBox(24) · poppins 20 bold · cor CRAVADA #4A4A4A (não var(--text)) */
.bb-modal-titulo {
  margin: 24px 0 0; font-family: 'Poppins', sans-serif; font-size: 20px;
  font-weight: 700; color: #4A4A4A;
}
/* SizedBox(12) · nunito 15 · Colors.grey[600] = #757575 (estava #4a4a4a) */
.bb-modal-texto { margin: 12px 0 0; font-size: 15px; color: #757575; }
/* ⚠️ `width: fit-content` — mesmo caso do .bb-resumo. No main.dart (~2172) é
   um Container sem `width` com `Row(mainAxisSize: min)` dentro: encolhe pro
   conteúdo e fica centrado. Como <div> flex de bloco ocupava a caixa inteira.
   Medido: 228,33 px numa tela de 391 (e 180 numa de 320, onde o texto quebra
   em duas linhas — o `Flexible` do original é o que evita o estouro).
   Padding horizontal é 16 (sym(h:16,v:10)), estava 14. */
.bb-modal-sinal {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  width: fit-content; max-width: 100%; margin: 16px auto 0;
  padding: 10px 16px; border-radius: 12px;
  background: rgba(76, 175, 80, .12); color: #2e7d32; font-weight: 700;
}
.bb-modal-sinal .bb-ico { width: 20px; height: 20px; color: #4caf50; }
.bb-btn-whats {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  /* SizedBox(32) antes · minimumSize(∞,55) · radius 15 · elevation 0.
     ls 0.1 é o do labelLarge que o ElevatedButton injeta — estava .5px. */
  width: 100%; height: 55px; margin-top: 32px; border: 0; border-radius: 15px;
  background: #25d366; color: #fff; cursor: pointer;
  font: inherit; font-weight: 700; letter-spacing: .1px;
}
.bb-btn-whats .bb-ico { width: 20px; height: 20px; }

/* ─── TELA DE PARABÉNS ─────────────────────────────────────────────────────
   fundo do tema · Icon(stars_rounded, 80) primary sobre primary@10%
   título 26 · subtítulo 16                                                */
/* Recuo 30 na horizontal — é o `SingleChildScrollView(padding: sym(h:30))` do
   _buildTelaParabens. Estava 40 dos quatro lados. */
.bb-tela-parabens {
  min-height: 100vh; background: var(--bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center; padding: 0 30px;
}
/* 120 = Icon(stars_rounded, size: 80) + padding all(20) dos dois lados.
   Estava 140, que não vem de lugar nenhum. Medido em modais_largura_test.dart. */
.bb-parabens-selo {
  width: 120px; height: 120px; border-radius: 50%;
  background: rgba(var(--primary-rgb), .1);
  display: flex; align-items: center; justify-content: center;
}
.bb-parabens-selo .bb-ico { width: 80px; height: 80px; color: var(--primary); }
/* SizedBox(32) · poppins 26 bold ls 1.5 (o ls estava faltando) */
.bb-tela-parabens h1 {
  margin: 32px 0 0; font-family: 'Poppins', sans-serif; font-size: 26px;
  font-weight: 700; letter-spacing: 1.5px; color: var(--text);
}
/* SizedBox(8) · nunito 16 W500 text@70% (era margin 12 e peso normal) */
.bb-tela-parabens p {
  margin: 8px 0 0; font-size: 16px; font-weight: 500; color: rgba(var(--text-rgb), .7);
}

/* ⚠️ CARD DE RECIBO — não existia no site. main.dart ~2297:
   SizedBox(48) · Container(width: infinity, maxWidth 450, padding 25,
   radius 28, bg tema.card, shadow black@20% blur 20 offset(0,10)) ·
   3 linhas [Icon 20 primary + 15 + nunito 16 w600 text@90%] separadas por
   Padding(sym(v:15)) > Divider(text@10%). SizedBox(60) no fim.
   Medido: card 331x211 numa tela de 391, linhas a 46 px uma da outra
   (15 + os 16 de altura padrão do Divider + 15) e linha de 23.            */
.bb-recibo {
  width: 100%; max-width: 450px; margin: 48px 0 60px; padding: 25px;
  border-radius: 28px; background: var(--card);
  box-shadow: 0 10px 20px rgba(0, 0, 0, .2); text-align: left;
}
.bb-recibo-linha { display: flex; align-items: center; gap: 15px; }
.bb-recibo-linha .bb-ico { width: 20px; height: 20px; fill: var(--primary); flex: none; }
.bb-recibo-linha span {
  flex: 1; min-width: 0; font-size: 16px; font-weight: 600; color: rgba(var(--text-rgb), .9);
}
/* O Divider do Material tem 16 de ALTURA com a linha de 1 px no meio — não é
   só uma borda. Com o Padding(sym(v:15)) dá os 46 medidos entre as linhas. */
.bb-recibo-divisor { height: 16px; margin: 15px 0; display: flex; align-items: center; }
.bb-recibo-divisor::before {
  content: ''; display: block; width: 100%; height: 1px; background: rgba(var(--text-rgb), .1);
}

/* SnackBar do Material com `behavior` PADRÃO (fixed) — é o que o app usa pra
   "Horário ocupado!", "Erro: …" e "Preencha seu nome e telefone corretamente.".
   ⚠️ FIXED NÃO É A PÍLULA FLUTUANTE. Medido: barra de 391x48 colada no fundo,
   da borda esquerda à direita, `shape: null` (SEM cantos arredondados),
   elevação 6, texto a 24 do bordo e 14 do topo. A versão anterior daqui era
   uma pílula centrada com radius 4 e `#323232` — do Material 2.
   O fundo certo é #322F35 (`colorScheme.inverseSurface` do M3).             */
.bb-toast {
  position: fixed; left: 0; right: 0; bottom: 0; z-index: 200;
  min-height: 48px; box-sizing: border-box;
  display: flex; align-items: flex-start;
  padding: 14px 24px; border-radius: 0;
  background: #322F35; color: #F5EFF7; font-size: 14px; text-align: left;
  box-shadow: 0 -1px 6px rgba(0, 0, 0, .2);
}
/* Só o "Ops, algo deu errado" troca o fundo: `backgroundColor: _temaAtivo.text`
   (main.dart ~2113). Os outros dois usam o padrão.                          */
.bb-toast[data-fundo="tema"] { background: var(--text); }
/* O QUARTO fundo de SnackBar do app: `Colors.redAccent` no erro de gerar novo
   código do Pix (sinal_pagamento_page.dart ~261). Medido em
   test/telas_erro_test.dart: #ff5252 com o mesmo texto #F5EFF7 do M3 — a cor
   do texto não muda quando o backgroundColor é custom. */
.bb-toast[data-fundo="erro"] { background: #FF5252; }

/* ═══════════════════════════════════════════════════════════════════════════
   SINAL PAGO — a animação de confirmação
   Porta _SucessoSinalView (sinal_pagamento_page.dart ~610). O site ia DIRETO do
   Pix pro modal de sucesso: esta tela não existia.

   Um AnimationController de 1500 ms com cinco intervalos. As curvas são as do
   Flutter, transcritas: Curves.easeOutBack = cubic-bezier(.175,.885,.32,1.275),
   easeInOut = (.42,0,.58,1), easeOut = (0,0,.58,1),
   easeOutCubic = (.215,.61,.355,1).

     círculo  0.00–0.45 easeOutBack   →   0 ms, 675 ms   (estoura passando de 1)
     anel     0.12–0.62 easeOut       → 180 ms, 750 ms   (pulsa e some)
     check    0.34–0.62 easeInOut     → 510 ms, 420 ms   (é RISCADO, não aparece)
     texto op 0.50–0.82 easeOut       → 750 ms, 480 ms
     texto dy 0.50–0.85 easeOutCubic  → 750 ms, 525 ms

   O check riscado é `stroke-dashoffset` — o equivalente exato do
   `PathMetrics.extractPath(0, len * progresso)` do app. Comprimento do traço
   M28,52 L44,67 L73,36 = 64.38 (medido, não chutado).                        */
.bb-sinal-ok {
  min-height: 100vh; background: var(--bg);
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  padding: 28px; text-align: center;
}
.bb-ok-palco { width: 140px; height: 140px; display: grid; place-items: center; }
.bb-ok-palco > * { grid-area: 1 / 1; }

/* Anel: opacidade (1−t)·0.5 e escala 0.55 + t·1.25 → termina em 1.8 e some. */
.bb-ok-anel {
  width: 100px; height: 100px; border-radius: 50%;
  border: 2.5px solid var(--primary);
  animation: bb-ok-anel 750ms cubic-bezier(0, 0, .58, 1) 180ms both;
}
@keyframes bb-ok-anel {
  from { opacity: .5; transform: scale(.55); }
  to   { opacity: 0;  transform: scale(1.8); }
}

/* Círculo principal: BoxShadow(primary@45%, blur 24, spread 1). */
.bb-ok-circulo {
  width: 100px; height: 100px; border-radius: 50%;
  background: var(--primary);
  box-shadow: 0 0 24px 1px rgba(var(--primary-rgb), .45);
  display: grid; place-items: center;
  animation: bb-ok-circulo 675ms cubic-bezier(.175, .885, .32, 1.275) both;
}
@keyframes bb-ok-circulo { from { transform: scale(0); } to { transform: scale(1); } }

.bb-ok-check { width: 100px; height: 100px; display: block; }
.bb-ok-check path {
  fill: none; stroke: #fff; stroke-width: 8.5;         /* size.width * 0.085 */
  stroke-linecap: round; stroke-linejoin: round;
  stroke-dasharray: 64.38; stroke-dashoffset: 64.38;
  animation: bb-ok-check 420ms cubic-bezier(.42, 0, .58, 1) 510ms both;
}
@keyframes bb-ok-check { to { stroke-dashoffset: 0; } }
/* No marrom o círculo é dourado claro: o check usa a cor do FUNDO, não branco
   (`corCheck = isDark ? t.background : Colors.white`). */
[data-tema='marrom_luxo'] .bb-ok-check path { stroke: var(--bg); }

/* SizedBox(22) · os textos sobem 12 px e aparecem. Duas animações na mesma
   caixa porque no app opacidade e deslocamento têm curvas e fins diferentes. */
.bb-ok-textos {
  margin-top: 22px;
  animation: bb-ok-fade 480ms cubic-bezier(0, 0, .58, 1) 750ms both,
             bb-ok-sobe 525ms cubic-bezier(.215, .61, .355, 1) 750ms both;
}
@keyframes bb-ok-fade { from { opacity: 0; } to { opacity: 1; } }
@keyframes bb-ok-sobe { from { transform: translateY(12px); } to { transform: none; } }
.bb-ok-t1 {
  margin: 0; font-family: 'Poppins', sans-serif; font-size: 20px;
  font-weight: 600; color: var(--text);
}
.bb-ok-t2 { margin: 6px 0 0; font-size: 14.5px; color: rgba(var(--text-rgb), .7); }

/* ═══════════════════════════════════════════════════════════════════════════
   MOVIMENTO REDUZIDO — `prefers-reduced-motion: reduce`
   Quem liga isso no sistema costuma ter enxaqueca vestibular, labirintite ou
   sensibilidade a movimento: animação não é enfeite pra essa pessoa, é
   sintoma.

   ⚠️ DIVERGÊNCIA DELIBERADA DO APP, e é a segunda do projeto (a primeira foi a
   grade dos chips). O CanvasKit DESENHA a tela: a preferência do sistema não
   chega nele, e o `AnimatedContainer` anima igual pra todo mundo. Então honrar
   aqui é fazer diferente do app — mas:
     · o estado FINAL é idêntico ao pixel; só a transição some;
     · quem não pediu nada não vê diferença nenhuma;
     · e TODAS as outras páginas deste mesmo site já honram há tempos
       (home.html, suporte.html, avaliacao.html, redefinir-senha.html).
       O site novo era o único fora do padrão da casa.

   Os dois indicadores de carregamento NÃO somem, só desaceleram — é o que o
   suporte.html já faz. Matar o spinner tira o único sinal de "está
   acontecendo alguma coisa" e troca um problema por outro pior.
   ═══════════════════════════════════════════════════════════════════════════ */
@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  /* AnimatedContainer(200ms) do card de serviço, rótulo flutuante do campo e
     o rodapé que sobe: o resultado é o mesmo, só chega na hora. */
  .bb-servico,
  .bb-campo label,
  .bb-rodape { transition: none; }

  /* Carregando: desacelera, não apaga. */
  .bb-spinner { animation-duration: 1.8s; }
  .bb-barra::after { animation-duration: 2.4s; }
  /* Mesma regra pro giro da foto: ele é o único sinal de que ela está vindo. */
  .bb-foto-carregando { animation-duration: 1.8s; }

  /* Sinal pago: mostra o ESTADO FINAL na hora. A confirmação continua sendo
     dada (círculo, check e textos aparecem), só sem o estouro, o pulso e o
     traço sendo riscado. O anel some porque ele é PURO movimento — no fim da
     animação ele já é invisível de qualquer forma. */
  .bb-ok-anel { display: none; }
  .bb-ok-circulo, .bb-ok-textos { animation: none; }
  .bb-ok-check path { animation: none; stroke-dashoffset: 0; }

  /* O spinner do botão segue a mesma regra dos outros dois: desacelera, não
     some — ele é o único sinal de que o pagamento está sendo criado. */
  .bb-spinner-botao { animation-duration: 1.8s; }
}

/* ─── PAGAMENTO DO SINAL ───────────────────────────────────────────────────
   Porta sinal_pagamento_page.dart. Reescrito em 25/08/2026: a versão anterior
   tinha outra ORDEM de tela inteira (ver o comentário no site.html).
   Scaffold(bg: tema.background) + AppBar(elevation 0, bg do tema, título
   poppins w600 18 centrado, ícone na cor do texto) + SafeArea > Center >
   SingleChildScrollView(padding: all 20) > ConstrainedBox(maxWidth: 420).   */
.bb-sinal {
  min-height: 100vh; background: var(--bg);
  display: flex; flex-direction: column;
}
/* AppBar do Material: 56 de altura, sem sombra, título centrado. */
.bb-sinal-appbar {
  position: relative; flex: none; height: 56px;
  display: flex; align-items: center; background: var(--bg);
}
.bb-sinal-appbar h1 {
  position: absolute; left: 0; right: 0; margin: 0; text-align: center;
  font-family: 'Poppins', sans-serif; font-size: 18px; font-weight: 600; color: var(--text);
  pointer-events: none;
}
/* IconButton do AppBar: 48x48 de área de toque, ícone 24 na cor do texto. */
.bb-sinal-voltar {
  position: relative; z-index: 1; width: 48px; height: 48px; margin-left: 4px;
  display: flex; align-items: center; justify-content: center;
  background: none; border: 0; padding: 0; cursor: pointer; color: var(--text);
}
.bb-sinal-voltar .bb-ico { fill: currentColor; }
/* Center + SingleChildScrollView(padding: all(20)) */
.bb-sinal-rolagem {
  flex: 1; display: flex; align-items: center; justify-content: center; padding: 20px;
}
.bb-sinal-caixa { width: 100%; max-width: 420px; }

/* Countdown: padding sym(v:10,h:16) · margin bottom 20 · radius 12 ·
   bg primary@10% · Icon(timer_outlined,20) + 8 + poppins w600 16 primary */
.bb-sinal-relogio {
  display: flex; align-items: center; justify-content: center; gap: 8px;
  margin-bottom: 20px; padding: 10px 16px; border-radius: 12px;
  background: rgba(var(--primary-rgb), .1);
  font-family: 'Poppins', sans-serif; font-size: 16px; font-weight: 600; color: var(--primary);
}
.bb-sinal-relogio .bb-ico { width: 20px; height: 20px; fill: var(--primary); }

/* Card do QR: padding 20 · SEMPRE branco (QR precisa de fundo claro) ·
   radius 20 · BORDA 2px primary@30%. A dica fica DENTRO do card.            */
.bb-qr {
  padding: 20px; border-radius: 20px; background: #fff;
  border: 2px solid rgba(var(--primary-rgb), .3);
  display: flex; flex-direction: column; align-items: center;
}
.bb-qr-img { width: 220px; height: 220px; }          /* Image.memory(220x220) */
.bb-qr-img img { width: 100%; height: 100%; image-rendering: pixelated; display: block; }
/* SizedBox(16) · nunito 13 · Colors.grey.shade600 = #757575 */
.bb-sinal-dica { margin: 16px 0 0; font-size: 13px; color: #757575; text-align: center; }

/* SizedBox(16) · nunito 13 text@70% · alinhado à ESQUERDA (Column stretch) */
.bb-sinal-rotulo {
  margin: 16px 0 8px; font-size: 13px; color: rgba(var(--text-rgb), .7); text-align: left;
}
/* InkWell > Container: padding sym(h:14,v:12) · bg card · radius 12 ·
   borda 1 primary@30% · Row[ Expanded(código 1 linha, ellipsis, mono 12,
   text@80%), SizedBox(8), Icon(copy_rounded,20,primary) ]                   */
.bb-copia {
  display: flex; align-items: center; gap: 8px; width: 100%;
  padding: 12px 14px; border-radius: 12px; background: var(--card);
  border: 1px solid rgba(var(--primary-rgb), .3);
  cursor: pointer; text-align: left; font: inherit;
}
.bb-copia code {
  flex: 1; min-width: 0; font-family: ui-monospace, 'Roboto Mono', monospace;
  font-size: 12px; color: rgba(var(--text-rgb), .8);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.bb-copia-ico { width: 20px; height: 20px; fill: var(--primary); flex: none; }
.bb-copia-ico[data-copiado="1"] { fill: #4caf50; }
/* Padding(top: 6) · nunito 12 w600 verde */
.bb-copiado { margin: 6px 0 0; font-size: 12px; font-weight: 600; color: #4caf50; }

/* SizedBox(24) · Center > Column [ valor poppins 32 bold primary, 4,
   "Sinal para agendar X" nunito 14 text@80%, 2, estúdio nunito 13 text@60% ] */
.bb-sinal-resumo { margin-top: 24px; text-align: center; }
.bb-sinal-valor {
  margin: 0; font-family: 'Poppins', sans-serif; font-size: 32px;
  font-weight: 700; color: var(--primary);
}
.bb-sinal-sub { margin: 4px 0 0; font-size: 14px; color: rgba(var(--text-rgb), .8); }
.bb-sinal-prof { margin: 2px 0 0; font-size: 13px; color: rgba(var(--text-rgb), .6); }

/* SizedBox(20) · padding 14 · radius 12 · bg primary@7% ·
   Row(start)[ Icon(info_outline,18,primary), SizedBox(10), texto nunito 13
   text@85% height 1.4 ]                                                     */
.bb-sinal-politica {
  display: flex; align-items: flex-start; gap: 10px;
  margin: 20px 0 0; padding: 14px; border-radius: 12px;
  background: rgba(var(--primary-rgb), .07);
  /* 13 × 1.4 = 18,2 → 18,00 no Flutter (test/tipografia_site_test.dart) */
  font-size: 13px; line-height: 18px; color: rgba(var(--text-rgb), .85); text-align: left;
}
.bb-sinal-politica .bb-ico { width: 18px; height: 18px; fill: var(--primary); flex: none; }

/* SizedBox(28) · Row centrado [ spinner 16x16 strokeWidth 2, 10,
   "Aguardando confirmação…" nunito 14 text@70% ]                            */
.bb-sinal-aguardando {
  display: flex; align-items: center; justify-content: center; gap: 10px;
  margin-top: 28px; font-size: 14px; color: rgba(var(--text-rgb), .7);
}
.bb-spinner-mini {
  width: 16px; height: 16px; border-radius: 50%; flex: none;
  border: 2px solid rgba(var(--primary-rgb), .15); border-top-color: var(--primary);
  animation: bb-girar .9s linear infinite;
}
/* SizedBox(12) · TextButton: nunito 14 BOLD primary SUBLINHADO — não é botão
   cheio. Altura mínima 48 é a do TextButton do Material.                    */
.bb-sinal-japaguei {
  display: block; width: 100%; min-height: 48px; margin-top: 12px;
  background: none; border: 0; cursor: pointer;
  font: inherit; font-size: 14px; font-weight: 700; color: var(--primary);
  text-decoration: underline;
}
.bb-sinal-japaguei[disabled] { opacity: .6; cursor: default; }
.bb-sinal-aviso { margin: 14px 0 0; font-size: 13px; color: rgba(var(--text-rgb), .75); }

/* ── Estado EXPIRADO ──────────────────────────────────────────────────────
   SizedBox(40) · Icon(timer_off_outlined,64,primary@70%) · 20 ·
   poppins 20 bold text · 8 · nunito 15 text@70% height 1.4 · 32 ·
   botão largura total altura 52 radius 14 poppins bold 15 · 12 ·
   TextButton "Cancelar agendamento" nunito 14 text@60%                      */
#sinal-expirado { text-align: center; padding-top: 40px; }
.bb-expirado-ico { width: 64px; height: 64px; margin: 0 auto; fill: rgba(var(--primary-rgb), .7); }
.bb-sinal-expirado-t {
  margin: 20px 0 0; font-family: 'Poppins', sans-serif; font-size: 20px;
  font-weight: 700; color: var(--text);
}
.bb-sinal-expirado-p {
  margin: 8px 0 0; font-size: 15px; line-height: 1.4; color: rgba(var(--text-rgb), .7);
}
.bb-btn-novo-codigo {
  width: 100%; height: 52px; margin-top: 32px; border: 0; border-radius: 14px;
  background: var(--primary); color: #fff; cursor: pointer;
  font-family: 'Poppins', sans-serif; font-size: 15px; font-weight: 700;
}
.bb-btn-novo-codigo[disabled] { opacity: .7; cursor: default; }
.bb-sinal-cancelar {
  display: block; width: 100%; min-height: 48px; margin-top: 12px;
  background: none; border: 0; cursor: pointer;
  font: inherit; font-size: 14px; color: rgba(var(--text-rgb), .6);
}
