/* =====================================================================
   TOKENS · Sistema de Reservas de Ônibus — OliveiraTur
   ---------------------------------------------------------------------
   v4 do design system (2026-08-26 — Ronaldo aprovou o visual explorado em
   Visualizar/OliveiraTur_Moderno_v4_icones.html: paleta mais institucional
   (azul menos "SaaS vibrante"), cantos e sombra ainda mais discretos, e
   sobretudo hierarquia por CARTÃO em vez de fio de 1px — .bloco e .linha
   ganham borda+raio+sombra própria e margem lateral, no lugar da faixa
   full-width dividida por linha. Inputs também deixam de ser sublinhados
   e viram caixa fechada. Ícone de amenidade (Água/Lanche/Starlink/Leito)
   ganha componente próprio com arte ilustrada (ver .icone-amenidade).
   Tipografia mantida (Archivo + IBM Plex Mono) — v4 usa "Inter" no
   rascunho de Visualizar/, mas o arquivo nunca carrega a fonte (sem
   <link>), então não é pra levar isso adiante — não fazia parte do que
   foi aprovado.

   Histórico: v3 (2026-08-25) trocou o fundo bege-envelhecido por
   branco/cinza neutro, cantos arredondados e sombra suave — v4 refina
   essa direção, não a substitui.

   Toda tela nova usa SÓ estas variáveis e classes, nunca cor hardcoded
   num style="" (exceção: protótipo em rascunho, antes de aprovado).
   ===================================================================== */

:root {
  /* Cores */
  --cor-tinta: #27323c;          /* navy institucional — faixas, botão primário invertido */
  --cor-tinta-escura: #181e24;   /* fundo externo / hover */
  --cor-primaria: #1d5c9e;       /* azul institucional — acento, links, seleção, CTA */
  --cor-primaria-escura: #164a82;
  --cor-sucesso: #178f55;
  --cor-erro: #c94848;
  --cor-aviso: #b36b00;
  --cor-destaque: #d98a2b;       /* âmbar — contador, toast de aviso */
  --cor-fundo: #f3f2ef;          /* cinza neutro com um toque de papel */
  --cor-superficie: #ffffff;
  --cor-superficie-alt: #eef1f5; /* hover de linha */
  --cor-borda: #dfe2e4;
  --cor-borda-forte: #cdd7e2;    /* borda de destaque em superfície clara (era quase-preto na v3) */
  --cor-texto: #2a3036;
  --cor-texto-suave: #6f7881;
  --cor-texto-inativo: #9aa1ac;
  --cor-inverso: #f7f8fa;        /* texto sobre grafite */
  --cor-inverso-suave: #b8bfc9;
  --cor-ocupado: #e5e7eb;

  /* Tipografia */
  --fonte-base: Archivo, system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --fonte-titulo: "Archivo Narrow", Archivo, system-ui, sans-serif;
  --fonte-dado: "IBM Plex Mono", ui-monospace, SFMono-Regular, Menlo, monospace;

  /* Espaçamento */
  --espaco-1: 4px;
  --espaco-2: 8px;
  --espaco-3: 16px;
  --espaco-4: 24px;
  --espaco-5: 32px;

  /* Bordas — cantos arredondados (v4: ainda mais discreto que a v3) */
  --raio: 10px;
  --raio-pequeno: 7px;
  --largura-app: 428px;
  /* Altura da barra da marca. O .appbar cola exatamente abaixo dela
     (top: var(--altura-marca)) — os dois valores NUNCA se separam. */
  --altura-marca: 48px;

  /* Sombra suave — v4 usa uma sombra bem mais discreta que a v3, e agora
     vale também pra cartão de lista (.bloco, .linha), não só flutuante. */
  --sombra: 0 2px 8px rgba(21, 31, 41, .045);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: var(--fonte-base);
  background: var(--cor-tinta-escura);
  color: var(--cor-texto);
  /* COLUNA VERTICAL (2026-08-31): marca no topo, conteudo no meio, rodape no
     fim. O `.app` cresce para ocupar a sobra (`flex: 1`), entao numa pagina
     curta o rodape encosta no fim da janela em vez de ficar empurrado para
     fora dela. Antes, `.app { min-height: 100vh }` reservava a altura toda da
     janela para o CONTEUDO, e o rodape comecava so depois disso — em tela de
     desktop, com o layout de 2 colunas, isso virava uma faixa vazia enorme
     antes do rodape. Era "o espaco na home". */
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

.app {
  max-width: var(--largura-app);
  margin: 0 auto;
  background: var(--cor-fundo);
  /* `min-height: 100vh` saiu daqui e virou `flex: 1` — ver o comentario no
     `body`. Manter os dois faria a pagina ter sempre 100vh de conteudo MAIS o
     rodape, criando barra de rolagem em tela que nao precisava. */
  flex: 1 0 auto;
  width: 100%;
}

/* Variante larga para telas de uso interno/desktop (Painel Admin) — mesmo
   design system, sem a largura de celular do App do Passageiro. */
.app-admin {
  max-width: 960px;
  margin: 0 auto;
  background: var(--cor-fundo);
  /* `flex: 1` no lugar de `min-height: 100vh` pelo mesmo motivo do `.app` —
     ver o comentario no `body`. O painel nao tem rodape, mas os dois juntos
     produziriam rolagem em pagina que cabe na tela. */
  flex: 1 0 auto;
  width: 100%;
}

/* Tabela simples (Admin) — sem zebra/hover chamativo, fios de 1px (a
   tabela não virou cartão — não faz sentido "empilhar cards" numa grade
   de dados; v4 não cobre tela de admin, então a decisão aqui é nossa). */
.tabela-admin { width: 100%; border-collapse: collapse; }
.tabela-admin th {
  text-align: left;
  font-family: var(--fonte-dado);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .8px;
  color: var(--cor-texto-suave);
  padding: 10px 12px;
  border-bottom: 1px solid var(--cor-borda-forte);
}
.tabela-admin td {
  padding: 12px;
  border-bottom: 1px solid var(--cor-borda);
  font-size: 13px;
}
.tabela-admin tr:hover td { background: var(--cor-superficie-alt); }

/* Scroll horizontal em vez de quebrar layout em telas estreitas (Admin
   também precisa funcionar em celular — só não é o alvo principal). */
.tabela-scroll { overflow-x: auto; }

/* Grade de campos do formulário — 4 colunas no desktop, colapsa em telas
   estreitas. Usar esta classe (não repetir grid-template-columns inline). */
.campo-grade-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--espaco-3); }
@media (max-width: 720px) {
  .campo-grade-4 { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 480px) {
  .campo-grade, .campo-grade-4 { grid-template-columns: 1fr; }
}

/* Utilitários de texto ------------------------------------------------ */
.dado {
  font-family: var(--fonte-dado);
  font-size: 10px;
  letter-spacing: .6px;
  color: var(--cor-texto-suave);
}
.rotulo {
  font-family: var(--fonte-dado);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: 1.2px;
  color: var(--cor-texto-suave);
  margin: 0;
}
.titulo-secao {
  margin: 0;
  font-size: 16px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: -.2px;
}
.display {
  font-family: var(--fonte-titulo);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: -1px;
  line-height: .94;
  margin: 0;
}

/* Cabeçalho da marca ------------------------------------------------- */
.marca {
  background: var(--cor-tinta);
  color: var(--cor-inverso);
  padding: 14px 18px 12px;
}
.marca-linha { display: flex; align-items: flex-end; justify-content: space-between; gap: 12px; }
.marca-nome { margin: 0; font-size: 23px; font-weight: 900; letter-spacing: -.6px; line-height: 1; }
.marca-regua { height: 2px; background: var(--cor-primaria); margin-top: 10px; }
.marca-tarja { margin: 8px 0 0; font-family: var(--fonte-dado); font-size: 10px; letter-spacing: .8px; color: #8b96a3; }

/* Cabeçalho de tela interna ------------------------------------------ */
.tela-header {
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 16px 18px;
  border-bottom: 1px solid var(--cor-borda);
}
.tela-header h1 { font-size: 16px; margin: 0; text-transform: uppercase; }

/* Botões ------------------------------------------------------------- */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--espaco-1);
  border: 1px solid transparent;
  border-radius: var(--raio-pequeno);
  padding: 14px 18px;
  font-family: inherit;
  color: var(--cor-texto);
  font-size: 12px;
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 1.2px;
  cursor: pointer;
}
.btn-bloco { width: 100%; }
.btn:disabled { opacity: .45; cursor: not-allowed; }
.btn-primary { background: var(--cor-primaria); color: var(--cor-inverso); }
.btn-primary:hover:not(:disabled) { background: var(--cor-primaria-escura); }
.btn-secundario { background: transparent; color: var(--cor-texto); border-color: var(--cor-borda-forte); }
.btn-secundario:hover:not(:disabled) { background: var(--cor-superficie-alt); }
.btn-voltar {
  border: 1px solid var(--cor-borda-forte);
  background: transparent;
  color: var(--cor-texto);
  font-family: inherit;
  width: 30px; height: 30px;
  font-size: 15px; line-height: 1;
  cursor: pointer;
  flex: none;
}

/* Campos — v4: caixa fechada em vez de sublinhado. -------------------- */
.campo { display: block; }
.campo input, .campo select {
  margin-top: var(--espaco-1);
  width: 100%;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio-pequeno);
  background: var(--cor-superficie);
  padding: 12px 14px;
  font-family: inherit;
  font-size: 15px;
  font-weight: 700;
  color: var(--cor-texto);
  outline: none;
}
.campo input:focus, .campo select:focus {
  border-color: var(--cor-primaria);
  box-shadow: 0 0 0 3px rgba(29, 92, 158, .12);
}
.campo input.mono { font-family: var(--fonte-dado); font-size: 14px; font-weight: 400; }
.campo-grade { display: grid; grid-template-columns: 1fr 1fr; gap: var(--espaco-3); }

/* Linha de lista (quadro de saídas, viagens, pontos) — v4: cartão próprio
   com margem lateral, em vez de faixa full-width dividida por fio. ---- */
.linha {
  display: flex;
  align-items: center;
  gap: 14px;
  margin: 0 18px 10px;
  padding: 14px 16px;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio-pequeno);
  background: var(--cor-superficie);
  box-shadow: var(--sombra);
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
}
.linha:hover { background: var(--cor-superficie-alt); }
.linha-hora { margin: 0; font-family: var(--fonte-dado); font-size: 19px; font-weight: 600; min-width: 62px; }
.linha-titulo { margin: 0; font-size: 16px; font-weight: 800; text-transform: uppercase; letter-spacing: -.2px; }
.linha-preco { margin: 0; font-size: 19px; font-weight: 900; letter-spacing: -.5px; color: var(--cor-primaria); }

/* Bloco/cartão — v4: cartão com borda+raio+sombra própria e margem
   lateral, no lugar da faixa plana com fio inferior. ------------------ */
.bloco {
  margin: 0 18px var(--espaco-3);
  padding: var(--espaco-4);
  background: var(--cor-superficie);
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio);
  box-shadow: var(--sombra);
}
/* Faixa invertida (hero, cabeçalho de confirmação) continua full-width —
   não é um cartão, é uma faixa de destaque. */
.bloco-invertido {
  background: var(--cor-tinta); color: var(--cor-inverso);
  margin: 0; border: 0; border-radius: 0; box-shadow: none;
}
.bloco-duplo { display: grid; grid-template-columns: 1fr 1fr; }
.bloco-duplo > * + * { border-left: 1px solid var(--cor-borda); }

/* Status — pill colorido ------------------------------------------- */
.status {
  display: inline-block;
  font-family: var(--fonte-dado);
  font-size: 9px;
  font-weight: 600;
  letter-spacing: .8px;
  margin: 0;
  padding: 3px 10px;
  border-radius: 999px;
}
.status-sucesso { color: var(--cor-sucesso); background: #e5f5ec; }
.status-aviso { color: var(--cor-aviso); background: #f7ecdb; }
.status-erro { color: var(--cor-erro); background: #fbebeb; }
.status-neutro { color: var(--cor-texto-inativo); background: var(--cor-superficie-alt); }

/* Foto larga (frota, imagens de apoio) -------------------------------- */
.foto-larga {
  display: block;
  width: 100%; height: 180px;
  object-fit: cover;
  border: 1px solid var(--cor-borda);
  border-radius: var(--raio-pequeno);
}

/* Estados de tela (carregando / vazio / erro) ------------------------ */
.tela-estado { text-align: center; padding: var(--espaco-5) var(--espaco-3); color: var(--cor-texto-suave); }
.spinner {
  width: 24px; height: 24px;
  margin: 0 auto var(--espaco-3);
  border: 3px solid var(--cor-borda);
  border-top-color: var(--cor-primaria);
  border-radius: 50%;
  animation: girar .8s linear infinite;
}
@keyframes girar { to { transform: rotate(360deg); } }

/* Toast -------------------------------------------------------------- */
.toast {
  position: fixed;
  left: 50%; bottom: var(--espaco-4);
  transform: translateX(-50%);
  padding: 12px 18px;
  border-radius: var(--raio-pequeno);
  box-shadow: var(--sombra);
  font-family: var(--fonte-dado);
  font-size: 11px; font-weight: 600; letter-spacing: 1px;
  z-index: 1000;
}
.toast[data-tipo="sucesso"] { background: var(--cor-sucesso); color: #fff; }
.toast[data-tipo="erro"] { background: var(--cor-erro); color: #fff; }
.toast[data-tipo="info"] { background: var(--cor-destaque); color: var(--cor-tinta); }

/* =====================================================================
   COMPONENTES V4 · fluxo do passageiro (Home, Saídas, Assentos, Checkout,
   Pix, Bilhete) — 2026-08-26
   ---------------------------------------------------------------------
   Portado quase literal de Visualizar/OliveiraTur_Moderno_v4_icones.html
   (mockup aprovado pelo Ronaldo), só trocando as variáveis próprias do
   rascunho (--blue, --navy, --ink...) pelos tokens deste arquivo, e
   removendo o que era só moldura de protótipo (.phone, .stage,
   .prototype-nav, troca de tela via JS — aqui cada tela é seu próprio
   arquivo, como manda o padrão do projeto). Fidelidade > elegância de
   nomenclatura — as classes ficam em inglês, como no mockup original,
   propositalmente sem tentar unificar com .bloco/.linha/.assento da v3
   (que continuam existindo só para o Painel Admin, que não tem
   equivalente no v4).
   ===================================================================== */

button, input, select { font-family: inherit; }

/* RESET DE LINK — obrigatorio neste projeto, nao preferencia estetica.
   As telas publicas usam <a> como CARTAO CLICAVEL (`.departure-mini`,
   `.card.trip`, chips de dia): a URL carrega o estado do fluxo de compra,
   entao tem de ser link real e nao botao com fetch. Sem este reset, o cartao
   inteiro herda o azul sublinhado do navegador — "Curitiba -> Ciudad del
   Este" e "34 vagas" apareciam como texto de link solto no meio do cartao.
   Quem PRECISA parecer link (`.section-link`, `.rodape-whatsapp`) declara
   isso por conta propria. */
a { color: inherit; text-decoration: none; }

.appbar{position:sticky;top:var(--altura-marca);z-index:20;background:rgba(12,31,54,.97);backdrop-filter:blur(14px);color:#fff;padding:15px 18px 13px;border-bottom:1px solid rgba(255,255,255,.08)}
  .appbar-row{display:flex;align-items:center;gap:12px;min-height:30px}
  .brand{font-weight:950;letter-spacing:-.7px;font-size:21px;flex:1}.brand b{color:#4aa2ff}
  .eyebrow{font-size:13px;letter-spacing:1.3px;text-transform:uppercase;font-weight:800;color:#86bfff}
  .appbar-title{font-size:15px;font-weight:850;flex:1;text-align:center}
  .icon-btn{width:36px;height:36px;display:grid;place-items:center;border:0;border-radius:11px;background:rgba(255,255,255,.08);color:white;cursor:pointer;font-size:19px}
  .header-spacer{width:36px}

  .hero{background:linear-gradient(145deg,#0c1f36 0%,#113a67 55%,#1766b7 100%);color:#fff;padding:24px 18px 22px;position:relative;overflow:hidden}
  .hero:after{content:"";position:absolute;width:250px;height:250px;border-radius:50%;right:-115px;top:-85px;background:radial-gradient(circle,rgba(86,174,255,.34),rgba(86,174,255,0) 70%)}
  .hero h1{font-size:38px;line-height:.98;letter-spacing:-1.5px;margin:8px 0 10px;max-width:325px;position:relative;z-index:1}.hero h1 span{color:#67b5ff}
  .hero p{font-size:13px;line-height:1.5;color:#cfdeee;margin:0;max-width:285px;position:relative;z-index:1}
  .route-pill{display:inline-flex;align-items:center;gap:7px;border:1px solid rgba(255,255,255,.2);background:rgba(255,255,255,.08);border-radius:999px;padding:7px 10px;font-size:13px;font-weight:800;letter-spacing:.3px;position:relative;z-index:1}
  .bus-art{height:118px;margin:20px 0 -18px;position:relative;z-index:1}
  .bus-art svg{width:100%;height:100%;filter:drop-shadow(0 12px 15px rgba(0,0,0,.23))}

  .content{padding:16px 16px 0}
  .section{margin-bottom:18px}
  .section-head{display:flex;align-items:center;justify-content:space-between;margin:0 2px 10px}
  .section-title{font-size:14px;letter-spacing:.7px;text-transform:uppercase;font-weight:900;margin:0}
  .section-link{border:0;background:transparent;color:var(--cor-primaria);font-size:14px;font-weight:850;padding:3px;cursor:pointer}
  .card{background:var(--cor-superficie);border:1px solid var(--cor-borda);border-radius:var(--raio);box-shadow:var(--sombra)}

  .search-card{margin-top:-10px;position:relative;z-index:3;padding:15px}
  .segmented{display:grid;grid-template-columns:1fr 1fr;background:#eef3f8;border-radius:12px;padding:3px;margin-bottom:14px}
  .segmented button{border:0;background:transparent;border-radius:9px;padding:9px 8px;font-size:13px;font-weight:850;color:var(--cor-texto-suave);cursor:pointer}
  .segmented button.active{background:#fff;color:var(--cor-texto);box-shadow:0 2px 8px rgba(0,0,0,.08)}
  .fields{display:grid;grid-template-columns:1fr 1fr;border:1px solid var(--cor-borda);border-radius:14px;overflow:hidden}
  .field-mini{padding:11px 12px;min-height:65px;background:#fff}
  .field-mini:nth-child(odd){border-right:1px solid var(--cor-borda)}
  .field-mini:nth-child(-n+2){border-bottom:1px solid var(--cor-borda)}
  .field-mini small,.label{display:block;color:#8995a5;font-size:13px;text-transform:uppercase;letter-spacing:.6px;font-weight:800;margin-bottom:5px}
  .field-mini strong{font-size:14px;line-height:1.25;display:block}
  .primary{width:100%;border:0;border-radius:13px;background:linear-gradient(135deg,var(--cor-primaria),var(--cor-primaria));color:white;font-weight:900;text-transform:uppercase;letter-spacing:.5px;font-size:14px;padding:14px 16px;cursor:pointer;box-shadow:0 7px 18px rgba(21,92,168,.22)}
  .primary:disabled{opacity:.42;cursor:not-allowed;box-shadow:none}
  .secondary{width:100%;border:1px solid #cdd7e2;border-radius:13px;background:#fff;color:var(--cor-texto);font-weight:850;text-transform:uppercase;letter-spacing:.4px;font-size:13px;padding:13px;cursor:pointer}
  .danger-text{color:var(--cor-erro)}

  .departure-mini{display:grid;grid-template-columns:58px 1fr auto;gap:12px;align-items:center;padding:13px 14px;margin-bottom:9px;cursor:pointer;transition:.18s}
  .departure-mini:active{transform:scale(.99)}
  .date-box{background:#f0f5fa;border-radius:12px;text-align:center;padding:8px 4px}.date-box b{display:block;font-size:25px;line-height:1}.date-box span{font-size:13px;font-weight:900;color:var(--cor-texto-suave)}
  .departure-mini h3{font-size:13px;margin:0 0 4px}.departure-mini p{font-size:13px;color:var(--cor-texto-suave);margin:0}.price{text-align:right}.price b{display:block;color:var(--cor-primaria);font-size:19px}.price span{font-size:13px;color:var(--cor-sucesso);font-weight:800}

  .amenities{display:grid;grid-template-columns:repeat(4,1fr);gap:8px}.amenity{background:#fff;border:1px solid var(--cor-borda);border-radius:14px;padding:11px 7px;text-align:center;min-height:92px}.amenity .ico{font-size:22px;margin-bottom:7px}.amenity b{font-size:13px;display:block}.amenity span{font-size:12px;color:var(--cor-texto-suave);display:block;margin-top:3px;line-height:1.2}
  .fleet{padding:17px;color:#fff;background:linear-gradient(135deg,#0b2038,#13497d);overflow:hidden;position:relative}.fleet:after{content:"";position:absolute;width:190px;height:190px;border-radius:50%;background:#1e69aa;opacity:.22;right:-70px;bottom:-100px}.fleet h3{font-size:24px;margin:4px 0 2px}.fleet p{font-size:14px;color:#b8cee4;margin:0 0 12px}.fleet ul{margin:0;padding:0;list-style:none;font-size:13px;line-height:1.9;color:#dce8f3}.fleet .small-btn{margin-top:12px;border:1px solid rgba(255,255,255,.22);background:rgba(255,255,255,.09);color:#fff;border-radius:10px;padding:9px 11px;font-size:13px;font-weight:900;text-transform:uppercase;letter-spacing:.5px;cursor:pointer}
  .rules{display:grid;grid-template-columns:1fr 1fr;gap:9px}.rule{padding:14px}.rule h4{margin:0 0 6px;font-size:13px;text-transform:uppercase;letter-spacing:.5px}.rule b{font-size:20px}.rule p{font-size:13px;line-height:1.35;color:var(--cor-texto-suave);margin:5px 0 0}.rule.alert{background:#fff8f7;border-color:#f1d1cd}.rule.alert h4{color:var(--cor-erro)}

  .route-strip{background:var(--cor-tinta);color:#fff;padding:14px 16px 16px}.route-line{display:flex;align-items:center;justify-content:center;gap:12px;font-size:14px;font-weight:850}.route-line span{color:#87c3ff}.days{display:flex;gap:8px;overflow:auto;padding:14px 16px 4px;scrollbar-width:none}.days::-webkit-scrollbar{display:none}.day{min-width:66px;border:1px solid var(--cor-borda);background:#fff;border-radius:13px;padding:10px 6px;text-align:center;color:var(--cor-texto-suave);cursor:pointer}.day b{display:block;color:var(--cor-texto);font-size:15px}.day span{font-size:13px;font-weight:800}.day.active{background:var(--cor-tinta);border-color:var(--cor-tinta);color:#cfe2f6}.day.active b{color:#fff}
  .trip{padding:15px;margin-bottom:10px}.trip-top{display:flex;align-items:center;justify-content:space-between;gap:12px}.badge{display:inline-flex;padding:5px 7px;border-radius:999px;background:#fff5e5;color:var(--cor-aviso);font-size:12px;font-weight:900;letter-spacing:.4px}.badge.ok{background:#eaf8f1;color:var(--cor-sucesso)}.badge.sold{background:#eef1f4;color:#8a9299}.badge.erro{background:#fbebeb;color:var(--cor-erro)}.trip h3{font-size:13px;margin:10px 0 4px}.trip .sub{font-size:13px;color:var(--cor-texto-suave)}.times{display:grid;grid-template-columns:1fr 32px 1fr;margin:14px 0;align-items:center}.times strong{font-size:24px}.times small{font-size:12px;color:var(--cor-texto-suave);display:block;margin-top:2px}.arrow{text-align:center;color:var(--cor-primaria);font-size:18px}.trip-meta{display:grid;grid-template-columns:repeat(3,1fr);gap:7px;border-top:1px solid var(--cor-borda);padding-top:10px}.trip-meta div{font-size:13px;color:var(--cor-texto-suave)}.trip-meta b{display:block;color:var(--cor-texto);font-size:13px;margin-top:2px}.trip-actions{display:flex;align-items:center;gap:12px;margin-top:13px}.trip-actions .price{flex:0 0 92px;text-align:left}.trip-actions button{flex:1}

  .progress{padding:13px 16px;background:#fff;border-bottom:1px solid var(--cor-borda);display:grid;grid-template-columns:repeat(3,1fr);gap:6px}.step{height:4px;border-radius:99px;background:#dfe5ec}.step.on{background:var(--cor-primaria)}
  .summary-strip{margin:14px 16px 0;padding:13px 14px;display:flex;gap:10px;align-items:center}.summary-strip .route{flex:1}.summary-strip b{font-size:14px}.summary-strip span{font-size:13px;color:var(--cor-texto-suave);display:block;margin-top:3px}.summary-strip .seat-tag{background:#eaf3fd;color:var(--cor-primaria);border-radius:10px;padding:9px 10px;font-size:14px;font-weight:900}
  .legend{display:flex;gap:12px;justify-content:center;padding:12px 16px;color:var(--cor-texto-suave);font-size:12px;font-weight:800}.legend span{display:flex;align-items:center;gap:5px}.dot{width:10px;height:10px;border-radius:3px;border:1px solid #c9d3de;background:#fff}.dot.sel{background:var(--cor-primaria);border-color:var(--cor-primaria)}.dot.occ{background:#d9dfe6;border-color:#d9dfe6}
  .bus-map{margin:0 16px 14px;padding:16px;border-radius:20px;background:#fff;border:1px solid var(--cor-borda);box-shadow:var(--sombra)}
  .driver{height:40px;margin-bottom:14px;border-bottom:1px dashed #d7dfe7;display:flex;justify-content:flex-end;align-items:flex-start;color:#98a3af;font-size:13px}.steering{width:28px;height:28px;border:2px solid #b7c1cc;border-radius:50%;display:grid;place-items:center;font-size:14px}
  .seat-row{display:grid;grid-template-columns:1fr 1fr 20px 1fr 1fr;gap:8px;margin-bottom:8px}.seat-row.leito{grid-template-columns:1fr 20px 1fr 1fr}.seat{border:1px solid #cdd6df;background:#fff;color:var(--cor-texto);border-radius:9px;height:39px;font-size:13px;font-weight:850;cursor:pointer}.seat.selected{background:var(--cor-primaria);border-color:var(--cor-primaria);color:#fff;box-shadow:0 5px 12px rgba(21,92,168,.22)}.seat.occupied{background:#e1e5ea;border-color:#e1e5ea;color:#a1a8b1;cursor:not-allowed}.aisle{display:block}.seat-total{padding:13px 14px;background:#f6f8fa;border-radius:13px;margin-top:14px;display:flex;align-items:center;justify-content:space-between}.seat-total span{font-size:13px;color:var(--cor-texto-suave)}.seat-total b{font-size:18px}
  .sticky-action{position:sticky;bottom:0;z-index:15;background:linear-gradient(to top,var(--cor-fundo) 72%,rgba(244,247,250,0));padding:18px 16px 14px}

  .form-card{padding:16px;margin:14px 16px}.input-group{margin-bottom:13px}.input-group label{display:block;font-size:13px;color:var(--cor-texto-suave);font-weight:850;text-transform:uppercase;letter-spacing:.6px;margin:0 0 6px 2px}.input-group input,.input-group select{width:100%;height:45px;border:1px solid #d9e0e7;border-radius:12px;background:#fff;padding:0 12px;color:var(--cor-texto);outline:none;font-family:inherit;font-size:13px}.input-group input:focus,.input-group select:focus{border-color:#73aee8;box-shadow:0 0 0 3px rgba(21,92,168,.09)}.input-error{border-color:#e67d7d!important}.error-msg{color:var(--cor-erro);font-size:13px;margin:5px 0 0 2px;display:none}.error-msg.show{display:block}.info-note{font-size:13px;line-height:1.45;color:var(--cor-texto-suave);padding:12px 13px;background:#f4f8fc;border-radius:12px}
  .checkout-summary{margin:0 16px;padding:15px}.summary-line{display:flex;justify-content:space-between;gap:12px;padding:8px 0;border-bottom:1px dashed #dde4eb}.summary-line:last-child{border-bottom:0}.summary-line span{font-size:13px;color:var(--cor-texto-suave)}.summary-line b{font-size:13px;text-align:right}.total-line{display:flex;justify-content:space-between;align-items:flex-end;padding:13px 0 2px}.total-line span{font-size:13px;color:var(--cor-texto-suave)}.total-line b{font-size:25px;color:var(--cor-primaria)}

  .pix-card{margin:14px 16px;padding:17px;text-align:center}.pix-logo{width:44px;height:44px;border-radius:14px;margin:0 auto 8px;background:#eaf8f6;color:#10a58c;display:grid;place-items:center;font-size:22px}.timer{display:inline-flex;align-items:center;gap:6px;margin:8px auto 14px;padding:7px 10px;border-radius:999px;background:#fff4e6;color:#9b5f08;font-size:13px;font-weight:900}.qr{width:204px;height:204px;margin:0 auto 14px;background:#fff;border:12px solid #fff;box-shadow:0 0 0 1px #e0e5eb;border-radius:12px;display:grid;grid-template-columns:repeat(21,1fr);grid-template-rows:repeat(21,1fr);overflow:hidden}.qr i{display:block;background:#0d1723}.copy-wrap{display:flex;border:1px solid #d9e1e9;border-radius:12px;overflow:hidden;background:#f6f8fa;text-align:left}.copy-code{flex:1;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;padding:11px;font-family:ui-monospace,SFMono-Regular,Menlo,monospace;font-size:13px;color:#66717e}.copy-btn{border:0;background:var(--cor-tinta);color:#fff;padding:0 13px;font-size:13px;font-weight:900;cursor:pointer}

  .success{padding:28px 18px 22px;text-align:center;background:linear-gradient(150deg,#0b2037,#155ca8);color:#fff}.check{width:58px;height:58px;border-radius:50%;margin:0 auto 12px;background:#eafaf2;color:var(--cor-sucesso);display:grid;place-items:center;font-size:30px;font-weight:900}.success h2{margin:0 0 5px;font-size:24px}.success p{margin:0;color:#c7dcf0;font-size:14px}
  .ticket{margin:15px 16px;padding:0;overflow:hidden}.ticket-head{padding:16px;border-bottom:1px dashed #cfd8e1}.ticket-time{display:flex;justify-content:space-between;align-items:baseline}.ticket-time strong{font-size:28px}.ticket-time span{font-size:13px;color:var(--cor-texto-suave);font-weight:800}.ticket h3{font-size:16px;margin:8px 0 4px}.ticket p{font-size:13px;color:var(--cor-texto-suave);margin:0}.ticket-grid{display:grid;grid-template-columns:1fr 1fr}.ticket-cell{padding:13px 16px;border-bottom:1px solid var(--cor-borda)}.ticket-cell:nth-child(odd){border-right:1px solid var(--cor-borda)}.ticket-cell small{display:block;font-size:12px;color:var(--cor-texto-suave);font-weight:850;text-transform:uppercase;letter-spacing:.5px}.ticket-cell b{display:block;font-size:14px;margin-top:4px}.barcode{height:54px;margin:14px 16px;background:repeating-linear-gradient(90deg,#101923 0 2px,#fff 2px 5px,#101923 5px 6px,#fff 6px 10px,#101923 10px 13px,#fff 13px 15px);border-radius:3px}
  .ticket-actions{display:grid;grid-template-columns:1fr 1fr;gap:9px;padding:0 16px 18px}

  /* V4 — refinamento visual: menos "UI genérica", mais identidade de operação rodoviária */
  .appbar{background:#27323c;backdrop-filter:none;padding:14px 18px 12px;border-bottom:4px solid var(--cor-primaria)}
  .brand{font-size:20px;font-weight:850;letter-spacing:.1px}.brand b{color:#72a7d5}
  .eyebrow{font-size:13px;letter-spacing:1px;font-weight:700;color:#a8c3da}
  .appbar-title{font-size:14px;font-weight:750;letter-spacing:.05px}
  .icon-btn{width:34px;height:34px;border-radius:7px;background:transparent;border:1px solid rgba(255,255,255,.16)}

  .hero{background:#27323c;padding:22px 18px 20px;border-bottom:0}
  .hero:after{display:none}
  .hero h1{font-size:35px;line-height:1.01;letter-spacing:-1.25px;margin:10px 0 9px;max-width:330px;font-weight:820}
  .hero h1 span{color:#75a9d7}
  .hero p{font-size:14px;line-height:1.5;color:#d4d9de;max-width:300px}
  .route-pill{border-radius:6px;padding:6px 9px;background:transparent;border-color:rgba(255,255,255,.2);font-weight:700;letter-spacing:.15px}
  .bus-art{height:102px;margin:18px 0 -12px}
  .bus-art svg{filter:drop-shadow(0 7px 8px rgba(0,0,0,.18))}

  .content{padding:15px 16px 0}
  .section{margin-bottom:20px}
  .section-head{margin:0 1px 9px}
  .section-title{font-size:14px;letter-spacing:-.1px;text-transform:none;font-weight:760}
  .section-link{font-size:14px;font-weight:700}
  .card{border-radius:10px;box-shadow:var(--sombra)}

  .search-card{margin-top:0;padding:14px;box-shadow:none}
  .segmented{border-radius:8px;padding:2px;background:#eceff1;margin-bottom:12px}
  .segmented button{border-radius:6px;font-size:13px;font-weight:700;padding:8px}
  .segmented button.active{box-shadow:none;border:1px solid #d8dde1}
  .fields{border-radius:8px}
  .field-mini{min-height:62px;padding:10px 11px}
  .field-mini small,.label{font-size:12px;letter-spacing:.55px;font-weight:700}
  .field-mini strong{font-size:14px;font-weight:700;line-height:1.3;overflow-wrap:anywhere}
  .primary{border-radius:7px;background:var(--cor-primaria);font-size:14px;font-weight:760;text-transform:none;letter-spacing:.05px;padding:13px 15px;box-shadow:none}
  .secondary{border-radius:7px;font-size:13px;font-weight:720;text-transform:none;letter-spacing:.02px;padding:12px}

  .departure-mini{grid-template-columns:54px minmax(0,1fr) auto;gap:11px;padding:12px 13px;border-radius:9px}
  .departure-mini>div{min-width:0}
  .date-box{border-radius:7px;background:#edf1f4;padding:8px 3px}
  .date-box b{font-size:23px}.date-box span{font-size:12px}
  .departure-mini h3{font-size:14px;line-height:1.25;margin-bottom:4px;overflow-wrap:anywhere}
  .departure-mini p{font-size:13px;line-height:1.3}
  .price{min-width:0}.price b{font-size:17px;white-space:nowrap}.price span{font-size:12px}

  .amenities{grid-template-columns:1fr 1fr;gap:8px}
  .amenity{min-height:66px;border-radius:8px;padding:10px 11px;display:flex;align-items:center;gap:10px;text-align:left}
  .amenity .ico{margin:0;flex:0 0 40px;height:34px;border:1px solid #cfd6dc;border-radius:6px;display:grid;place-items:center;font-size:21px;line-height:1;background:#f7f8f8}
  .amenity b{font-size:13px}.amenity span{font-size:12px;margin-top:2px}
  .fleet{border-radius:9px;background:#293844;padding:16px;box-shadow:none;border-left:4px solid var(--cor-primaria)}
  .fleet:after{display:none}.fleet h3{font-size:22px;font-weight:780}.fleet p{color:#c3ced7}.fleet ul{font-size:13px;line-height:1.85}.fleet .small-btn{border-radius:6px;text-transform:none;letter-spacing:0;font-weight:720}
  .rules{gap:8px}.rule{padding:13px;border-radius:9px}.rule h4{font-size:13px;letter-spacing:.3px}.rule b{font-size:18px}.rule p{font-size:12px}

  .route-strip{background:#e8ecef;color:var(--cor-texto);padding:12px 16px;border-bottom:1px solid #d5dadd}
  .route-line{font-size:14px;font-weight:760}.route-line span{color:var(--cor-primaria)}
  .days{gap:7px;padding-top:12px}.day{min-width:64px;border-radius:7px;padding:9px 6px}.day.active{background:#27323c;border-color:#27323c}.day b{font-size:14px}
  .trip{padding:14px;border-radius:9px}.badge{border-radius:5px;padding:4px 7px;font-size:12px}.trip h3{font-size:14px}.times{margin:13px 0}.times strong{font-size:22px}.trip-meta{gap:5px}.trip-actions{gap:10px}.trip-actions .price{flex-basis:88px}

  .progress{padding:10px 16px;background:var(--cor-fundo);border-bottom:1px solid var(--cor-borda);gap:4px}.step{height:3px;background:#d7dadd}.step.on{background:var(--cor-primaria)}
  .summary-strip{margin:12px 16px 0;padding:12px 13px;display:grid;grid-template-columns:minmax(0,1fr) auto;gap:12px;border-radius:9px;box-shadow:none}
  .summary-strip .route{min-width:0}.summary-strip b{font-size:14px;line-height:1.25;display:block;overflow-wrap:anywhere}.summary-strip span{font-size:12px}.summary-strip .seat-tag{min-width:38px;text-align:center;border-radius:6px;padding:8px;background:#edf3f8;font-size:13px}
  .legend{padding:11px 16px;font-size:12px;gap:10px}.dot{border-radius:2px}
  .bus-map{margin:0 16px 12px;padding:14px;border-radius:9px;box-shadow:none}
  .seat-row{gap:7px;margin-bottom:7px}.seat{height:38px;border-radius:6px;font-size:13px;font-weight:760}.seat.selected{box-shadow:none}.driver{margin-bottom:12px}.steering{border-radius:50%}
  .seat-total{padding:12px 13px;border-radius:7px;margin-top:13px;display:grid;grid-template-columns:minmax(0,1fr) minmax(82px,auto);gap:18px;align-items:end;background:#f2f3f3}
  .seat-total>div{min-width:0}.seat-total>div:last-child{text-align:right!important}
  .seat-total span{display:block;font-size:12px;line-height:1.2;margin-bottom:4px;white-space:normal}
  .seat-total b{display:block;font-size:16px;line-height:1.15;white-space:normal;overflow-wrap:anywhere}
  #seatPrice{white-space:nowrap;font-size:17px}
  .sticky-action{background:linear-gradient(to top,var(--cor-fundo) 78%,rgba(243,242,239,0));padding:16px 16px 13px}

  .form-card{padding:15px;margin:12px 16px;border-radius:9px;box-shadow:none}
  .input-group{margin-bottom:12px}.input-group label{font-size:12px;font-weight:720;text-transform:none;letter-spacing:.1px}
  .input-group input,.input-group select{height:43px;border-radius:7px;background:#fff}.input-group input:focus,.input-group select:focus{box-shadow:0 0 0 2px rgba(29,92,158,.08)}
  .info-note{border-radius:7px;background:#f1f3f4;font-size:13px}
  .checkout-summary{margin:0 16px;padding:14px;border-radius:9px;box-shadow:none}
  .summary-line{gap:16px;padding:8px 0}.summary-line span{font-size:12px;flex:0 0 78px}.summary-line b{font-size:13px;line-height:1.3;min-width:0;overflow-wrap:anywhere}
  .total-line{padding-top:12px}.total-line b{font-size:23px}

  .pix-card{margin:12px 16px;padding:16px;border-radius:9px;box-shadow:none}
  .pix-logo{width:48px;height:28px;border-radius:5px;margin-bottom:10px;background:#0a9b81;color:#fff;font-size:13px;font-weight:850;letter-spacing:.8px}
  .timer{border-radius:6px;font-size:13px;padding:6px 9px}
  .qr{width:196px;height:196px;border-radius:3px;box-shadow:0 0 0 1px #d9dcdf}
  .copy-wrap{border-radius:7px}.copy-btn{font-size:12px;font-weight:760}

  .success{padding:25px 18px 20px;background:#27323c;border-bottom:4px solid var(--cor-primaria)}
  .check{width:52px;height:52px;font-size:26px}.success h2{font-size:22px;font-weight:780}.success p{font-size:13px;color:#d1d7dc}
  .ticket{margin:13px 16px;border-radius:9px;box-shadow:none}.ticket-head{padding:15px}.ticket-time strong{font-size:26px}.ticket h3{font-size:15px;line-height:1.25}.ticket p{line-height:1.35}
  .ticket-grid{grid-template-columns:minmax(0,1fr) minmax(0,1fr)}
  .ticket-cell{padding:12px 14px;min-width:0}.ticket-cell small{font-size:12px}.ticket-cell b{font-size:14px;line-height:1.3;overflow-wrap:anywhere;min-width:0}
  .barcode{height:48px;margin:13px 16px}.ticket-actions{gap:8px;padding:0 16px 16px}

.amenity .ico img { width: 24px; height: 24px; object-fit: contain; display: block; }

/* =====================================================================
   MULTI-ASSENTO · 2026-08-28
   ---------------------------------------------------------------------
   Uma reserva passa a ter VÁRIOS assentos e UM Pix só (decisão do Ronaldo:
   "não deve fazer vários Pix"). Teto de 6 por reserva.
   As poltronas escolhidas viram "fichas" removíveis: no mapa de 40 lugares
   o passageiro perde de vista o que já marcou, e conferir isso rolando o
   mapa é pior do que ler uma lista curta.
   ===================================================================== */
.seat-chips { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 14px; }
.seat-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: #eaf3fd; color: var(--cor-primaria);
  border: 1px solid #cfe2f7; border-radius: 10px;
  padding: 7px 8px 7px 10px;
  font-family: var(--fonte-dado); font-size: 11px; font-weight: 600;
}
/* O × é o jeito de desmarcar sem procurar a poltrona no mapa. Alvo de 20px
   porque é toque em celular, não clique de mouse. */
.seat-chip button {
  width: 20px; height: 20px; flex: none;
  display: grid; place-items: center;
  border: 0; border-radius: 6px; cursor: pointer;
  background: rgba(29, 92, 158, .12); color: var(--cor-primaria);
  font-size: 13px; line-height: 1; font-weight: 700;
}
.seat-chip button:hover { background: rgba(29, 92, 158, .22); }

/* Limite atingido: o contador vira âmbar em vez de desabilitar poltrona
   livre — poltrona desabilitada fica visualmente idêntica a "indisponível",
   e o passageiro pensaria que o ônibus lotou. */
.seat-total.limite { background: #fdf3e3; }
.seat-total.limite b { color: var(--cor-aviso); }

/* Etiqueta de campo não obrigatório (e-mail no checkout). */
.rotulo-opcional {
  display: inline-block; margin-left: 6px;
  font-family: var(--fonte-dado); font-size: 7.5px; font-weight: 600;
  letter-spacing: .5px; color: var(--cor-texto-inativo);
  border: 1px solid var(--cor-borda); border-radius: 4px; padding: 1px 4px;
  vertical-align: middle; text-transform: uppercase;
}

/* Linha "Poltrona 04A · você" — o titular já é o passageiro do 1º assento,
   então ele não redigita o próprio nome. */
.passageiro-fixo {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 8px;
  padding: 11px 12px; margin-bottom: 13px;
  background: var(--cor-superficie-alt); border-radius: var(--raio-pequeno);
}
.passageiro-fixo .poltrona {
  font-family: var(--fonte-dado); font-size: 11px; font-weight: 600;
  color: var(--cor-primaria);
}
.passageiro-fixo .quem { font-size: 12px; font-weight: 700; flex: 1; min-width: 0; }

/* =====================================================================
   BILHETE · ESTADO DO PAGAMENTO · 2026-08-28
   ---------------------------------------------------------------------
   O cabecalho `.success` (gradiente azul + check verde) dizia sempre
   "Pagamento confirmado". Nesta etapa o Checkout manda o passageiro direto
   pro bilhete SEM passar pelo Pix (que e do Bloco 3), entao o bilhete de
   reserva nao paga precisa avisar — e este e o UNICO aviso que ele recebe
   de que a poltrona nao esta garantida. Cor diferente e obrigatoria: um
   cabecalho azul-com-check num bilhete nao pago faria o passageiro achar
   que esta tudo resolvido e aparecer no embarque sem ter pago.
   ===================================================================== */
.success.aguardando { background: linear-gradient(150deg, #6b4708, #b36b00); }
.success.aguardando .check { background: #fff4e6; color: var(--cor-aviso); }
.success.expirada { background: linear-gradient(150deg, #2a3036, #6b3535); }
.success.expirada .check { background: #fbebeb; color: var(--cor-erro); }

/* Selo de embarque no bilhete — o passageiro ve que aquele trecho ja foi
   usado, em vez de descobrir na porta do onibus. */
.ticket-embarcado {
  display: inline-flex; align-items: center; gap: 6px;
  margin-top: 10px; padding: 6px 10px; border-radius: 999px;
  background: #eaf8f1; color: var(--cor-sucesso);
  font-family: var(--fonte-dado); font-size: 9px; font-weight: 600;
  letter-spacing: .4px; text-transform: uppercase;
}

/* =====================================================================
   COMPONENTES DO PAINEL ADMIN · 2026-08-27
   ---------------------------------------------------------------------
   Vieram dos <style> inline dos protótipos AdminViagens/AdminEmbarque.
   Com as telas virando views Handlebars, todo estilo passa a morar aqui —
   tokens.css é a fonte da verdade (CLAUDE.md), nunca <style> por tela.
   ===================================================================== */

/* Abas do painel (Viagens / Validar embarque) */
.painel-abas {
  display: flex; gap: 2px;
  padding: 0 18px;
  background: var(--cor-superficie);
  border-bottom: 1px solid var(--cor-borda);
}
.painel-abas button {
  border: 0; background: transparent; cursor: pointer;
  font-family: var(--fonte-dado);
  font-size: 11px; font-weight: 600; letter-spacing: .6px;
  color: var(--cor-texto-suave);
  padding: 14px 12px; border-bottom: 2px solid transparent;
}
.painel-abas button:hover { color: var(--cor-texto); }
.painel-abas button.on { color: var(--cor-primaria); border-bottom-color: var(--cor-primaria); }

/* Tabela do admin: mais respiro que o padrão (é a única tela de dado denso) */
.tabela-admin td, .tabela-admin th { padding: 12px 14px; }

/* Botões de ação na linha da tabela */
.linha-acoes { display: flex; gap: 6px; justify-content: flex-end; }
.linha-acoes button { width: auto; padding: 6px 10px; font-size: 10px; }

/* Linha expansível com as reservas de uma viagem */
.linha-reservas td { background: var(--cor-superficie-alt); padding: 0; }
.linha-reservas .miolo { padding: 14px 18px; }
.mini-tabela { width: 100%; border-collapse: collapse; }
.mini-tabela th {
  text-align: left; font-family: var(--fonte-dado); font-size: 9px;
  color: var(--cor-texto-suave); padding: 6px 10px; border-bottom: 1px solid var(--cor-borda);
}
.mini-tabela td {
  padding: 7px 10px; font-size: 12px; vertical-align: middle;
  border-bottom: 1px solid var(--cor-borda);
}
.qr-caixa { background: #fff; padding: 8px; border-radius: 6px; display: inline-block; line-height: 0; }

/* Visor de câmera da validação de embarque — molduras de canto são a
   convenção universal de leitor de QR, ajudam a reconhecer a tela. */
.visor {
  position: relative;
  aspect-ratio: 1 / 1;
  background: var(--cor-tinta);
  border-radius: var(--raio);
  overflow: hidden;
  display: grid; place-items: center;
}
.visor video { width: 100%; height: 100%; object-fit: cover; }
.visor-legenda {
  position: absolute; inset: auto 24px 16px;
  color: var(--cor-inverso-suave);
  font-family: var(--fonte-dado); font-size: 11px; letter-spacing: .6px;
  text-align: center; pointer-events: none;
}
.visor-moldura { position: absolute; inset: 32px; pointer-events: none; }
.visor-moldura i { position: absolute; width: 28px; height: 28px; border: 3px solid var(--cor-primaria); }
.visor-moldura i:nth-child(1) { top: 0; left: 0; border-right: 0; border-bottom: 0; border-radius: 8px 0 0 0; }
.visor-moldura i:nth-child(2) { top: 0; right: 0; border-left: 0; border-bottom: 0; border-radius: 0 8px 0 0; }
.visor-moldura i:nth-child(3) { bottom: 0; left: 0; border-right: 0; border-top: 0; border-radius: 0 0 0 8px; }
.visor-moldura i:nth-child(4) { bottom: 0; right: 0; border-left: 0; border-top: 0; border-radius: 0 0 8px 0; }

/* =====================================================================
   MAPA DE ASSENTOS DO ADMIN · 2026-08-27
   ---------------------------------------------------------------------
   O mapa do PASSAGEIRO tem 3 estados (livre / escolhido / ocupado) e usa
   .seat + .seat.selected + .seat.occupied, já existentes.
   O mapa do ADMIN precisa de 4, porque ele distingue o que o passageiro
   não precisa saber: reservado (pendente de pagamento) × pago (confirmado)
   × bloqueado (fora de uso). Classes novas, sem tocar as do passageiro.
   ===================================================================== */
.mapa-admin .seat { cursor: pointer; }

/* Reservado — pendente de pagamento. Âmbar: pode virar livre se expirar. */
.seat.reservado {
  background: #fdf3e3; border-color: #e8cfa0; color: var(--cor-aviso);
}
/* Pago — confirmado. Verde: é venda firme, não some sozinha. */
.seat.pago {
  background: #e5f5ec; border-color: #a9d9be; color: var(--cor-sucesso);
}
/* Bloqueado — fora de uso (quebrado, removido). Hachura diagonal deixa
   inequívoco mesmo pra quem não distingue cor bem. */
.seat.bloqueado {
  background: repeating-linear-gradient(45deg, #e1e5ea 0 4px, #cfd5dc 4px 8px);
  border-color: #b9c2cb; color: #6f7881;
}
/* Assento que o admin não pode bloquear (tem reserva viva) — o cursor
   avisa antes do clique, não só o erro depois. */
.seat.reservado, .seat.pago { cursor: not-allowed; }

/* Legenda do mapa — obrigatória com 4 estados. */
.legenda-mapa {
  display: flex; flex-wrap: wrap; gap: 14px;
  padding: 12px 0 16px;
}
.legenda-mapa span {
  display: flex; align-items: center; gap: 7px;
  font-family: var(--fonte-dado); font-size: 9.5px; letter-spacing: .4px;
  color: var(--cor-texto-suave);
}
.legenda-mapa i {
  width: 15px; height: 15px; border-radius: 4px; display: block; flex: none;
  border: 1px solid #cdd6df; background: #fff;
}
.legenda-mapa i.reservado { background: #fdf3e3; border-color: #e8cfa0; }
.legenda-mapa i.pago { background: #e5f5ec; border-color: #a9d9be; }
.legenda-mapa i.bloqueado {
  background: repeating-linear-gradient(45deg, #e1e5ea 0 4px, #cfd5dc 4px 8px);
  border-color: #b9c2cb;
}

/* =====================================================================
   REMANEJAMENTO DE PASSAGEIRO · 2026-08-28 (Bloco B1)
   ---------------------------------------------------------------------
   O mapa do admin passou a ter DOIS MODOS, e a distinção é visual porque a
   mesma poltrona livre significa coisas diferentes em cada um:
     • modo normal    → clicar livre BLOQUEIA
     • modo remanejar → clicar livre RECEBE quem está sendo movido
   Estado invisível aqui seria erro caro: o admin bloquearia uma poltrona
   achando que estava movendo alguém. Por isso `.mapa-admin.remanejando`
   muda a cara do mapa inteiro, não só do destino.
   ===================================================================== */

/* Faixa de modo: fica FIXA no topo do corpo do modal enquanto o
   remanejamento está ativo, então o admin nunca perde de vista em que passo
   está — mesmo depois de rolar o mapa de 40 poltronas. */
.barra-remanejo {
  position: sticky; top: 0; z-index: 5;
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin: 0 0 14px; padding: 12px 14px;
  background: var(--cor-primaria); color: #fff;
  border-radius: var(--raio-pequeno);
}
.barra-remanejo .quem-move {
  flex: 1; min-width: 0;
  font-size: 13px; font-weight: 700; line-height: 1.3;
}
.barra-remanejo .quem-move small {
  display: block; margin-top: 2px;
  font-family: var(--fonte-dado); font-size: 9px; font-weight: 400;
  letter-spacing: .5px; color: rgba(255, 255, 255, .78);
}
.barra-remanejo button {
  width: auto; padding: 8px 14px; font-size: 10px; flex: none;
  background: rgba(255, 255, 255, .14); color: #fff;
  border: 1px solid rgba(255, 255, 255, .3);
}
.barra-remanejo button:hover { background: rgba(255, 255, 255, .24); }

/* Modo remanejar: destino possível ganha destaque, o resto esmaece — o
   admin vê de relance para onde PODE mover, sem tentar e errar. */
.mapa-admin.remanejando .seat { opacity: .35; cursor: not-allowed; }
.mapa-admin.remanejando .seat:not(.reservado):not(.pago):not(.bloqueado) {
  opacity: 1; cursor: pointer;
  border-color: var(--cor-primaria); border-width: 2px;
  box-shadow: 0 0 0 3px rgba(29, 92, 158, .15);
}
/* A poltrona de ORIGEM continua visível e destacada: ela é a referência de
   onde a pessoa está saindo. */
.mapa-admin.remanejando .seat.origem-remanejo {
  opacity: 1; cursor: pointer;
  background: var(--cor-primaria); border-color: var(--cor-primaria); color: #fff;
}

/* Lista "precisa remanejar" — quem PAGOU e está sem poltrona. Vermelho de
   propósito: é dívida da empresa com o passageiro, não aviso operacional. */
.painel-remanejar {
  margin: 0 0 16px; padding: 14px;
  border: 1px solid #e6b5b5; border-left-width: 4px;
  border-radius: var(--raio-pequeno); background: #fdf1f1;
}
.painel-remanejar h3 {
  margin: 0 0 4px; font-size: 13px; color: var(--cor-erro);
  text-transform: uppercase; letter-spacing: .4px;
}
.painel-remanejar .explica {
  margin: 0 0 12px; font-size: 12px; line-height: 1.5; color: var(--cor-texto-suave);
}
.linha-remanejar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 10px;
  padding: 9px 0; border-top: 1px solid #f0dada;
}
.linha-remanejar .nome { flex: 1; min-width: 0; font-size: 13px; font-weight: 700; }
.linha-remanejar .antes {
  font-family: var(--fonte-dado); font-size: 10px; color: var(--cor-texto-suave);
}
.linha-remanejar button { width: auto; padding: 7px 12px; font-size: 10px; flex: none; }

/* "Quem está a bordo" — a resposta pra "de quem é cada poltrona".
   Agrupado POR RESERVA de propósito: uma família é uma reserva com N
   poltronas, e é isso que o admin precisa ver junto pra não separá-la.
   Nome dentro da poltrona de 39px não cabe; lista agrupada cabe e informa
   mais. */
.grupo-bordo {
  padding: 10px 0; border-top: 1px solid var(--cor-borda);
}
.grupo-bordo:first-child { border-top: 0; }
.grupo-bordo .cabeca {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px 10px;
  margin-bottom: 7px;
}
.grupo-bordo .cabeca b { font-size: 12px; }
.grupo-bordo .lugares { display: flex; flex-wrap: wrap; gap: 6px; }
/* Cada lugar é clicável: selecionar aqui é o MESMO caminho de selecionar no
   mapa — dois pontos de entrada, uma interação. */
.lugar-bordo {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 6px 8px 6px 9px; border-radius: var(--raio-pequeno);
  border: 1px solid var(--cor-borda); background: var(--cor-superficie);
  font-size: 12px; cursor: pointer;
}
.lugar-bordo:hover { border-color: var(--cor-primaria); background: #f4f9ff; }
.lugar-bordo .poltrona {
  font-family: var(--fonte-dado); font-size: 11px; font-weight: 600;
  color: var(--cor-primaria);
}
.lugar-bordo.selecionado {
  border-color: var(--cor-primaria); border-width: 2px; background: #eaf3fd;
}

/* Aviso no BILHETE do passageiro pago que está sem poltrona. Separado do
   cabeçalho de propósito: o pagamento dele ESTÁ confirmado (o cabeçalho
   verde continua verdadeiro) — o que está pendente é a poltrona. Misturar
   os dois faria o bilhete parecer inválido. */
.aviso-reacomodando {
  margin: 0; padding: 12px 13px;
  border-radius: var(--raio-pequeno);
  background: #f4f9ff; border: 1px solid #cfe2f7; border-left-width: 4px;
  color: var(--cor-texto);
}
.aviso-reacomodando b { display: block; margin-bottom: 4px; font-size: 13px; }
.aviso-reacomodando span {
  font-size: 12px; line-height: 1.5; color: var(--cor-texto-suave);
}

/* Prévia do mapa no cadastro de ônibus — só conferência de contagem, não
   é clicável, então poltrona menor e sem cursor. */
.previa-mapa { padding: 14px; background: var(--cor-superficie-alt); border-radius: var(--raio-pequeno); }
.previa-mapa .seat-row { gap: 5px; margin-bottom: 5px; }
.previa-mapa .seat { height: 26px; font-size: 8.5px; cursor: default; }
.previa-total {
  display: flex; align-items: baseline; justify-content: space-between;
  margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--cor-borda);
}
.previa-total b { font-size: 20px; font-weight: 900; }

/* =====================================================================
   MODAL · 2026-08-27
   ---------------------------------------------------------------------
   Toda ação do Painel Admin (nova viagem, editar, ver reservas, ver QR,
   confirmar cancelamento) abre em modal flutuante — decisão do Ronaldo.
   Antes: formulário expandia no meio da página e a lista de reservas
   abria como linha da tabela, o que empurrava o conteúdo pra baixo.
   ===================================================================== */
.modal {
  position: fixed; inset: 0; z-index: 900;
  display: grid; place-items: center;
  padding: 24px;
  background: rgba(24, 30, 36, .55);
  overflow-y: auto;
}
.modal[hidden] { display: none; }
.modal-caixa {
  background: var(--cor-superficie);
  border-radius: var(--raio);
  box-shadow: 0 18px 48px rgba(16, 22, 28, .32);
  width: 100%; max-width: 720px;
  max-height: calc(100vh - 48px);
  display: flex; flex-direction: column;
  overflow: hidden;
}
.modal-caixa.estreita { max-width: 420px; }
.modal-caixa.larga { max-width: 940px; }

.modal-cab {
  display: flex; align-items: center; justify-content: space-between; gap: 14px;
  padding: 16px 20px;
  border-bottom: 1px solid var(--cor-borda);
}
.modal-cab h2 {
  margin: 0; font-size: 15px; font-weight: 800;
  text-transform: uppercase; letter-spacing: -.2px;
}
.modal-fechar {
  border: 1px solid var(--cor-borda-forte); background: transparent;
  color: var(--cor-texto-suave);
  width: 30px; height: 30px; border-radius: var(--raio-pequeno);
  font-family: inherit; font-size: 16px; line-height: 1;
  cursor: pointer; flex: none;
}
.modal-fechar:hover { background: var(--cor-superficie-alt); color: var(--cor-texto); }

.modal-corpo { padding: 20px; overflow-y: auto; }
.modal-rodape {
  display: flex; gap: 10px; justify-content: flex-end;
  padding: 14px 20px;
  border-top: 1px solid var(--cor-borda);
  background: var(--cor-superficie-alt);
}
.modal-rodape button { width: auto; padding: 11px 18px; }
/* Ação destrutiva (confirmar cancelamento de viagem) */
.modal-rodape .destrutivo { background: var(--cor-erro); border-color: var(--cor-erro); color: #fff; }
.modal-rodape .destrutivo:hover:not(:disabled) { filter: brightness(.92); }

/* Dentro de modal o formulário não precisa da moldura de cartão nem da
   margem lateral — o modal já é o cartão. */
.modal-corpo .form-card,
.modal-corpo .card { margin: 0; box-shadow: none; border: 0; padding: 0; }

@media (max-width: 560px) {
  .modal { padding: 0; place-items: stretch; }
  .modal-caixa { max-width: none; max-height: 100vh; border-radius: 0; }
}

/* Resultado da validação (sucesso / já validado / recusado) */
.resultado { margin: 14px 0 0; padding: 16px; display: flex; gap: 12px; align-items: flex-start; }
.resultado-icone {
  flex: 0 0 40px; height: 40px; border-radius: 50%;
  display: grid; place-items: center;
  font-size: 19px; font-weight: 900; color: #fff;
}
.resultado h3 { margin: 0; font-size: 14px; text-transform: uppercase; }
.resultado p { margin: 3px 0 0; font-size: 12px; color: var(--cor-texto-suave); line-height: 1.4; }
/* Ação dentro do cartão de resultado (desfazer embarque). O cartão deixou
   de ser só aviso e passou a carregar ação — por isso a ação ganha linha
   própria embaixo do texto, em vez de disputar espaço com ele. */
.resultado-acao { margin-top: 12px; }
.resultado-acao button { width: auto; padding: 9px 14px; font-size: 10px; }

/* =====================================================================
   CONFERÊNCIA DE EMBARQUE · 2026-08-28
   ---------------------------------------------------------------------
   Exigência do /Cyber: ler o QR passou a APENAS CONSULTAR — quem grava o
   embarque é a confirmação humana, depois de comparar o nome com o
   documento.
   Por isso este cartão NÃO pode se parecer com o .resultado de sucesso: se
   parecesse, o operador leria o verde, liberaria o passageiro e o embarque
   nunca seria gravado. Azul institucional = ação pendente; verde continua
   reservado pra desfecho concluído.
   ===================================================================== */
.conferencia {
  margin: 14px 0 0; padding: 16px;
  border: 1px solid var(--cor-primaria);
  border-left-width: 4px;
  background: #f4f9ff;
}
.conferencia-rotulo {
  margin: 0 0 8px;
  font-family: var(--fonte-dado); font-size: 9.5px; font-weight: 600;
  letter-spacing: .8px; text-transform: uppercase; color: var(--cor-primaria);
}
/* Nome grande de propósito: é o único dado que o operador compara com o
   documento — de braço estendido, numa fila, possivelmente sob sol. */
.conferencia-nome {
  margin: 0; font-family: var(--fonte-titulo);
  font-size: 23px; font-weight: 700; line-height: 1.15; color: var(--cor-texto);
}
.conferencia-meta {
  display: flex; flex-wrap: wrap; gap: 6px 10px; align-items: center;
  margin: 10px 0 0; font-size: 12px; color: var(--cor-texto-suave);
}
.conferencia-meta b { color: var(--cor-texto); font-size: 12px; }
/* Alvos grandes e empilhados: o erro caro aqui é confirmar sem olhar, e
   botão pequeno lado a lado no celular convida ao toque errado. */
.conferencia-acoes { display: grid; gap: 8px; margin-top: 16px; }
.conferencia-acoes .primary { padding: 16px; font-size: 12px; }

/* Leitura pausada enquanto espera a decisão humana. Sem isso a câmera relê
   o mesmo QR e reabre o cartão em loop. */
.visor.pausado video,
.visor.pausado .visor-legenda { opacity: .3; }
.visor.pausado .visor-moldura i { border-color: var(--cor-texto-inativo); }
.visor-pausa {
  position: absolute; left: 0; right: 0; bottom: 0;
  background: rgba(24, 30, 36, .86); color: var(--cor-inverso);
  font-family: var(--fonte-dado); font-size: 9.5px; letter-spacing: .7px;
  text-align: center; padding: 10px 12px;
}

/* =====================================================================
   TROCA DE ÔNIBUS DA VIAGEM · 2026-08-28 (Bloco B2)
   ---------------------------------------------------------------------
   A tela existe pra impedir que o admin troque o carro ÀS CEGAS: a troca
   pode deixar passageiro pago sem poltrona, e o sistema não remaneja
   ninguém sozinho. Por isso a simulação vem ANTES do botão de confirmar,
   e os três desfechos têm aparências deliberadamente diferentes —
   "trocar sem impacto" e "10 pessoas perdem a poltrona" não podem
   parecer a mesma operação.
   ===================================================================== */

/* De/para dos veículos. Duas colunas com a seta no meio; em tela estreita
   empilha e a seta gira (o fluxo passa a ser de cima pra baixo). */
.troca-comparativo {
  display: grid; grid-template-columns: 1fr auto 1fr; align-items: stretch;
  gap: 10px; margin: 0 0 16px;
}
.troca-veiculo {
  border: 1px solid var(--cor-borda); border-radius: var(--raio-pequeno);
  background: var(--cor-superficie-alt); padding: 12px 14px;
}
.troca-veiculo.destino {
  background: #f4f9ff; border-color: var(--cor-primaria); border-width: 2px;
}
.troca-veiculo .rotulo {
  font-family: var(--fonte-dado); font-size: 9px; letter-spacing: .7px;
  color: var(--cor-texto-suave); margin: 0 0 6px;
}
.troca-veiculo .frota { margin: 0; font-size: 16px; font-weight: 800; }
.troca-veiculo .detalhe {
  margin: 3px 0 0; font-size: 12px; color: var(--cor-texto-suave); line-height: 1.4;
}
.troca-veiculo .lugares {
  margin: 8px 0 0; font-family: var(--fonte-dado); font-size: 11px; font-weight: 600;
}
.troca-seta {
  display: grid; place-items: center; font-size: 20px;
  color: var(--cor-texto-inativo); padding: 0 2px;
}
/* Placeholder enquanto nenhum ônibus foi escolhido — o comparativo já
   ocupa o espaço, então a tela não "salta" quando a simulação chega. */
.troca-veiculo.vazio {
  display: grid; place-items: center; text-align: center;
  border-style: dashed; background: transparent; color: var(--cor-texto-inativo);
  font-size: 12px; line-height: 1.4;
}
@media (max-width: 560px) {
  .troca-comparativo { grid-template-columns: 1fr; }
  .troca-seta { transform: rotate(90deg); padding: 2px 0; }
}

/* Os três números que resumem o diff. Mesma linguagem do .seat-total do
   mapa, mas aqui o terceiro valor (vagas depois) é o que o admin mais
   precisa: 40 → 30 é menos receita possível nessa saída. */
.troca-numeros {
  display: grid; grid-template-columns: repeat(3, 1fr); gap: 1px;
  background: var(--cor-borda); border: 1px solid var(--cor-borda);
  border-radius: var(--raio-pequeno); overflow: hidden; margin: 0 0 16px;
}
.troca-numeros > div {
  background: var(--cor-superficie); padding: 11px 12px; text-align: center;
}
.troca-numeros span {
  display: block; font-family: var(--fonte-dado); font-size: 9px;
  letter-spacing: .6px; color: var(--cor-texto-suave); margin-bottom: 4px;
}
.troca-numeros b { font-size: 19px; font-weight: 800; }
.troca-numeros .remove b { color: var(--cor-erro); }
.troca-numeros .cria b { color: var(--cor-sucesso); }

/* PAINEL DE DESFECHO — a cor da borda esquerda é o sinal primário, igual
   ao .painel-remanejar. Três variantes obrigatórias e distintas. */
.troca-desfecho {
  padding: 14px; margin: 0 0 16px;
  border: 1px solid var(--cor-borda); border-left-width: 4px;
  border-radius: var(--raio-pequeno); background: var(--cor-superficie-alt);
}
.troca-desfecho h3 {
  margin: 0 0 5px; font-size: 14px;
  display: flex; align-items: center; gap: 8px;
}
.troca-desfecho p { margin: 0; font-size: 12px; line-height: 1.55; color: var(--cor-texto-suave); }
.troca-desfecho .icone {
  width: 22px; height: 22px; flex: none; border-radius: 50%;
  display: grid; place-items: center; font-size: 12px; color: #fff;
}

.troca-desfecho.limpa { border-color: #a9d9be; border-left-color: var(--cor-sucesso); background: #f2faf5; }
.troca-desfecho.limpa h3 { color: var(--cor-sucesso); }
.troca-desfecho.limpa .icone { background: var(--cor-sucesso); }

.troca-desfecho.remanejar { border-color: #e8cfa0; border-left-color: var(--cor-aviso); background: #fdf3e3; }
.troca-desfecho.remanejar h3 { color: var(--cor-aviso); }
.troca-desfecho.remanejar .icone { background: var(--cor-aviso); }

.troca-desfecho.bloqueada { border-color: #e6b5b5; border-left-color: var(--cor-erro); background: #fdf1f1; }
.troca-desfecho.bloqueada h3 { color: var(--cor-erro); }
.troca-desfecho.bloqueada .icone { background: var(--cor-erro); }

/* Ciência explícita do que o admin está assumindo. Só aparece no desfecho
   "com remanejamento", e o confirmar depende dela: o admin está criando
   uma dívida com N passageiros pagos que NÃO serão avisados (não existe
   canal até o Bloco 3). Um clique a mais é proporcional ao dano. */
.troca-ciencia {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 12px 13px; margin: 0 0 4px;
  border: 1px solid var(--cor-borda-forte); border-radius: var(--raio-pequeno);
  background: var(--cor-superficie); cursor: pointer;
}
.troca-ciencia input { width: 17px; height: 17px; flex: none; margin: 1px 0 0; cursor: pointer; }
.troca-ciencia span { font-size: 12px; line-height: 1.5; }
.troca-ciencia b { display: block; margin-bottom: 2px; font-size: 12px; }

/* =====================================================================
   VAGAS + OCUPAÇÃO na tabela de viagens · 2026-08-31
   ---------------------------------------------------------------------
   A coluna VAGAS mostrava um número solto. Sem o denominador, `36` e `39`
   parecem quase iguais quando uma viagem tem 4 vendidas e a outra 1; e
   num carro 2+1 (30 lugares) o mesmo número significa outra coisa.
   A barra mostra o quanto está OCUPADO — o admin varre a lista procurando
   o que está enchendo, não o que está vazio.
   ===================================================================== */
.vagas-ocupacao { display: flex; flex-direction: column; gap: 5px; min-width: 74px; }
.vagas-ocupacao .numeros {
  font-family: var(--fonte-dado); font-size: 13px; font-weight: 600;
  color: var(--cor-texto); line-height: 1; white-space: nowrap;
}
/* O total em tom fraco: o número que importa é o de vagas livres — o
   denominador é referência, não protagonista. */
.vagas-ocupacao .numeros small {
  font-size: 11px; font-weight: 400; color: var(--cor-texto-inativo);
}
.vagas-ocupacao .barra {
  display: block; height: 4px; border-radius: 2px;
  background: var(--cor-borda); overflow: hidden;
}
.vagas-ocupacao .barra i {
  display: block; height: 100%; background: var(--cor-primaria);
  border-radius: 2px; transition: width .2s ease;
}

/* =====================================================================
   QR REAL DO PIX (2026-08-31, Bloco 3)
   ---------------------------------------------------------------------
   A classe `.qr` acima é o QR ILUSTRATIVO dos protótipos: um grid 21x21 de
   divs, desenhado em JS só para a referência visual. Na tela real o QR é uma
   IMAGEM PNG em base64 que o Mercado Pago devolve — precisa de caixa própria,
   porque `.qr` impõe grid e sobrescreveria o tamanho da imagem.

   Mesma moldura branca e mesma dimensão do protótipo, para a tela convertida
   ficar idêntica ao que foi aprovado.
   ===================================================================== */
.qr-img {
  display: block; width: 204px; height: 204px; margin: 0 auto 14px;
  background: #fff; border: 12px solid #fff; border-radius: 12px;
  box-shadow: 0 0 0 1px #e0e5eb; image-rendering: pixelated;
}
@media (max-width: 400px) {
  .qr-img { width: 196px; height: 196px; }
}

/* =====================================================================
   CABECALHO DA MARCA — App do Passageiro (2026-08-31)
   ---------------------------------------------------------------------
   Pedido do Ronaldo: a marca visivel em TODAS as telas, e nao so na Home.
   Reusa o desenho que JA existia no appbar da Home (.brand com o TUR em
   azul + .eyebrow EXPRESSO) — a primeira versao desta mudanca criou uma
   faixa propria, centralizada, e foi descartada: identidade visual nao se
   reinventa por tela.

   Fundo e regua iguais ao appbar para as duas barras lerem como uma peca
   so. `sticky` no topo, e o .appbar de cada tela cola LOGO ABAIXO
   (top:52px, ajustado adiante).
   ===================================================================== */
.marca-app {
  position: sticky; top: 0; z-index: 30;
  /* MESMO tom do .appbar e do .hero (#27323c), nao o azul-marinho do V3.
     Com tons diferentes as duas barras liam como duas faixas empilhadas, cada
     uma com sua regua azul — era a falha que aparecia em toda tela MENOS a
     Home, que nao tem .appbar e por isso escapava. */
  background: #27323c;
  color: #fff;
  /* Altura FIXA, e nao padding: o .appbar cola em `top` e esse valor precisa
     ser exatamente a altura desta barra. Com padding + fonte, a altura real
     dava ~48px contra um top:52px chutado, e ao rolar passava uma listra de
     4px de conteudo entre as duas barras. */
  height: var(--altura-marca);
  padding: 0 18px;
  display: flex; align-items: center;
  /* Sem regua propria: quem separa o escuro do conteudo claro e o .appbar
     (telas internas) ou o .hero (Home) — ambos do mesmo tom, entao as duas
     barras leem como uma peca so. */
}
.marca-app-row { display: flex; align-items: center; gap: 12px; width: 100%; }
/* O .brand vira link para a Home: em tela interna, tocar a marca e o jeito
   natural de voltar ao inicio. Sem sublinhado nem cor de link. */
.marca-app .brand { color: inherit; text-decoration: none; }

@media (max-width: 360px) {
  /* Redefinir o token faz o `top` do .appbar acompanhar automaticamente. */
  :root { --altura-marca: 44px; }
  .marca-app { padding: 0 14px; }
  .marca-app .brand { font-size: 18px; }
  .marca-app .eyebrow { font-size: 11px; letter-spacing: .9px; }
}

/* =====================================================================
   CAMBIO PARA A VIAGEM — bloco da Home (2026-08-31)
   ---------------------------------------------------------------------
   ⚠️ Estas classes FALTAVAM: a view foi escrita usando .cambio,
   .cambio-linha e .cambio-dica, e nenhuma existia aqui. O resultado era o
   bloco em texto corrido (o <span> e o <b> colados, a dica em negrito
   emendada no paragrafo) — foi o que o Ronaldo viu como "desformatado".
   Lição: view e CSS entram na MESMA passada; classe usada e nao definida
   nao da erro, so aparece torta.
   ===================================================================== */
.cambio { padding: 14px; }

/* Rotulo a esquerda, valor a direita — como uma linha de extrato. O valor
   em fonte de DADO (mono) porque e numero para comparar, nao texto. */
.cambio-linha {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: 12px; padding: 3px 0;
}
.cambio-linha span { font-size: 13px; color: var(--cor-texto-suave); }
.cambio-linha b {
  font-family: var(--fonte-dado); font-size: 19px; font-weight: 600;
  color: var(--cor-texto); white-space: nowrap;
}

/* A DICA e o ponto do bloco, nao um rodape: o numero e so o gancho. Por
   isso ela tem fundo proprio e barra na lateral, em vez de virar mais um
   paragrafo cinza que ninguem le. */
.cambio-dica {
  margin-top: 12px; padding: 11px 12px;
  background: #f1f5f9; border-left: 3px solid var(--cor-primaria);
  border-radius: 0 7px 7px 0;
  font-size: 13px; line-height: 1.5; color: var(--cor-texto);
}
.cambio-dica b { display: block; margin-bottom: 3px; font-weight: 760; }

.cambio-fonte {
  margin: 10px 0 0; font-size: 12px; line-height: 1.4;
  color: var(--cor-texto-inativo);
}

/* Aviso que aparece SO quando a cotacao exibida e a comercial (a fonte de
   turismo tem cota por IP e o VPS chegou com ela esgotada). Sem este aviso,
   o numero pareceria ser o da casa de cambio. */
.cambio-aviso-fonte {
  margin: 8px 0 0; font-size: 12px; line-height: 1.45;
  color: var(--cor-texto-inativo);
}

/* =====================================================================
   COTACAO NA BARRA DO TOPO + RODAPE INSTITUCIONAL — 2026-08-31
   ---------------------------------------------------------------------
   Decisao do Ronaldo, no lugar da faixa fixa de cotacao no rodape: a barra
   da marca ja existe e ja e sticky, entao a cotacao fica visivel em toda
   tela sem custar altura. Faixa fixa embaixo brigaria com o
   `.sticky-action` (botao principal de Assentos/Checkout/Pix).
   ===================================================================== */

/* A cotacao e DADO, entao fonte mono e alinhamento estavel: o numero muda
   sozinho a cada 30 min e nao pode empurrar a marca de lugar. */
.cotacao-topo {
  display: inline-flex; align-items: baseline; gap: 6px;
  padding: 5px 9px; border-radius: 7px;
  background: rgba(255,255,255,.09);
  border: 1px solid rgba(255,255,255,.14);
  color: #dfe7ee; text-decoration: none;
  white-space: nowrap;
}
.cotacao-topo span { font-size: 11px; font-weight: 800; letter-spacing: .8px; color: #a8c3da; }
.cotacao-topo b { font-family: var(--fonte-dado); font-size: 14px; font-weight: 600; color: #fff; }
.cotacao-topo:hover { background: rgba(255,255,255,.15); }

/* Em tela estreita a barra fica com tres coisas (marca + EXPRESSO +
   cotacao) e nao cabe. Some o EXPRESSO, que e enfeite de identidade, nao
   informacao — a marca ao lado ja diz o mesmo. A cotacao fica. */
@media (max-width: 430px) {
  .marca-app .eyebrow { display: none; }
}

/* ---------------------------------------------------------------------
   RODAPE — rola com a pagina (NAO e fixo; ver views/partials/rodape).
   Mesmo tom escuro da barra do topo, para a pagina fechar onde comecou.
   --------------------------------------------------------------------- */
.rodape {
  margin-top: 26px;
  background: #27323c;
  border-top: 4px solid var(--cor-primaria);
  color: #d4d9de;
  padding: 22px 16px 26px;
}
.rodape-conteudo {
  max-width: var(--largura-app);
  margin: 0 auto;
  display: grid;
  gap: 18px;
}
.rodape-nome { display: block; font-size: 18px; font-weight: 850; letter-spacing: .1px; color: #fff; }
.rodape-nome b { color: #72a7d5; }
.rodape-nota { display: block; margin-top: 5px; font-size: 13px; line-height: 1.45; color: #aab6c0; }
.rodape-rotulo {
  display: block; margin-bottom: 5px;
  font-size: 12px; font-weight: 700; letter-spacing: .7px;
  text-transform: uppercase; color: #8fa5b8;
}
.rodape-whatsapp {
  display: inline-block; font-size: 14px; font-weight: 760;
  color: #fff; text-decoration: none;
  border-bottom: 1px solid rgba(255,255,255,.3); padding-bottom: 2px;
}
.rodape-cotacao { display: block; font-family: var(--fonte-dado); font-size: 16px; color: #fff; }
/* O aviso e obrigatorio, nao decoracao: numero de dolar ao lado do nome da
   empresa pode ser lido como preco da passagem. */
.rodape-cotacao em {
  display: block; margin-top: 4px;
  font-family: var(--fonte-base); font-style: normal;
  font-size: 12px; color: #93a3b1;
}

/* No desktop o rodape acompanha a largura de conteudo das outras faixas e
   os blocos ficam lado a lado. */
@media (min-width: 700px) {
  .rodape-conteudo {
    max-width: 700px;
    grid-template-columns: minmax(0, 1.4fr) repeat(2, minmax(0, 1fr));
    gap: 26px;
    align-items: start;
  }
}


/* =====================================================================
   LAYOUT DE DESKTOP DO APP DO PASSAGEIRO — 2026-08-31
   ---------------------------------------------------------------------
   O app era mobile-first com `--largura-app: 428px` em `max-width` e NAO
   havia uma media query para tela grande — so para telas menores (720,
   560, 480, 400, 360). Num monitor de 1920px o site usava 22% da largura.
   O CLAUDE.md descrevia o app como "cresce naturalmente em tela maior", o
   que o `max-width` impedia: a intencao estava escrita, nunca implementada.

   Importa porque a venda vem de celular, mas o DONO DA OLIVEIRATUR avalia
   o sistema no computador — e a primeira impressao dele era a tira
   estreita com fundo escuro dos dois lados.

   ⛔ O CELULAR NAO REGRIDE: tudo aqui esta dentro de `min-width`. Abaixo de
   768px este bloco e inteiramente inerte e o desenho v4 aprovado continua
   como esta.
   ===================================================================== */
/* =====================================================================
   FAIXA DO MEIO · 768–1023px — só cresce, não reorganiza
   ===================================================================== */
@media (min-width: 768px) and (max-width: 1023.98px) {
  /* Redefinir o TOKEN em vez de mexer no `.app`: qualquer outra regra que
     dependa da largura do app acompanha sozinha. */
  :root { --largura-app: 660px; }

  /* Com 660px o hero tem espaço para o texto respirar sem virar 2 colunas. */
  .hero h1 { font-size: 42px; max-width: 460px; }
  .hero p  { max-width: 420px; }

  /* 4 amenidades numa linha só (no celular são 2×2). */
  .amenities { grid-template-columns: repeat(4, 1fr); }
}


/* =====================================================================
   DESKTOP · >= 1024px
   ===================================================================== */
@media (min-width: 1024px) {

  /* -------------------------------------------------------------------
     A MOLDURA
     -------------------------------------------------------------------
     O `.app` deixa de ter teto e passa a ocupar a janela; quem centraliza
     o CONTEÚDO é o gutter calculado abaixo. Assim as faixas escuras
     (marca, appbar, hero, faixa de rota) vão de borda a borda — o que faz
     a página parecer um site, e não um celular esticado — enquanto o
     texto continua numa medida legível.

     `--gutter-desktop` é a mesma conta em todos os blocos: metade da
     sobra, com um piso de 28px para a janela de 1024px não colar o texto
     na borda. Está numa variável porque SEIS blocos dependem dela — foi
     exatamente um valor repetido à mão (a altura da barra da marca) que
     produziu a fresta de 4px corrigida hoje. */
  :root {
    --largura-conteudo: 1180px;
    --gutter-desktop: max(28px, (100% - var(--largura-conteudo)) / 2);
  }

  .app { max-width: none; }

  /* As barras do topo: fundo de borda a borda, conteúdo alinhado à mesma
     coluna do resto da página. */
  .marca-app { padding: 0 var(--gutter-desktop); height: 60px; }
  .marca-app-row,
  .appbar-row { max-width: var(--largura-conteudo); margin: 0 auto; }
  .marca-app .brand { font-size: 23px; }

  .appbar { top: 60px; padding: 15px var(--gutter-desktop) 13px; }
  /* No celular o título fica centralizado entre a seta e um espaçador. Numa
     barra de 1180px, centralizar joga o título para longe do botão voltar e
     ele perde a relação com a página. Alinhado à esquerda, logo depois da
     seta, continua sendo o rótulo daquela tela. */
  .appbar-title { text-align: left; flex: 0 1 auto; font-size: 16px; }
  .header-spacer { display: none; }

  /* -------------------------------------------------------------------
     HERO EM DUAS COLUNAS
     -------------------------------------------------------------------
     No celular o ônibus fica ABAIXO do texto porque não há largura para
     os dois. No desktop essa ordem desperdiça a altura da tela: o
     visitante rola antes de ver a primeira saída. Texto à esquerda,
     ônibus à direita, e as "Próximas saídas" já aparecem sem rolagem em
     tela de 900px de altura.

     Feito com `grid-template-areas` e não com wrapper novo: o HTML do
     hero é o mesmo que o mobile usa, então nada muda lá. */
  .hero {
    padding: 52px var(--gutter-desktop) 44px;
    display: grid;
    grid-template-columns: minmax(0, 1.05fr) minmax(0, 1fr);
    grid-template-areas:
      "pilula  arte"
      "titulo  arte"
      "texto   arte";
    grid-template-rows: auto auto 1fr;
    column-gap: 48px;
    align-content: center;
    max-width: none;
  }
  .hero > .route-pill { grid-area: pilula; justify-self: start; align-self: end; font-size: 14px; }
  .hero h1 {
    grid-area: titulo;
    /* 56px é grande, mas o hero agora tem 44+52px de respiro vertical e
       metade da largura da página. `max-width` em `ch` mantém a quebra em
       duas linhas ("Compras no / Paraguai.") em qualquer monitor, em vez
       de esticar numa linha só em 2560px. */
    font-size: 56px;
    line-height: .99;
    letter-spacing: -2px;
    max-width: 15ch;
    margin: 14px 0 12px;
  }
  .hero p { grid-area: texto; font-size: 17px; line-height: 1.55; max-width: 44ch; align-self: start; }
  .hero .bus-art { grid-area: arte; height: 100%; min-height: 230px; margin: 0; align-self: center; }
  .hero .bus-art svg { height: 100%; }

  /* -------------------------------------------------------------------
     O CORPO: DUAS COLUNAS COM PAPÉIS DIFERENTES
     -------------------------------------------------------------------
     Não é "dividir em duas para preencher". A divisão é por PAPEL:

       coluna principal (1,55fr) — o que a pessoa veio fazer: escolher uma
       saída. Recebe as próximas saídas e o que a viagem inclui.

       coluna lateral (1fr, mín. 316px) — apoio à decisão, que ela lê UMA
       vez: câmbio, frota, regras da cota. No celular isso fica no fim da
       rolagem, e no desktop ganha lugar fixo ao lado sem empurrar as
       saídas para baixo.

     ⚠️ Isto exige DOIS WRAPPERS no HTML (`.coluna-principal` e
     `.coluna-lateral`) — declarado no protótipo. No mobile os wrappers
     não recebem estilo nenhum: `div` dentro de `div`, fluxo vertical
     idêntico ao de hoje. Foi a alternativa escolhida no lugar de
     posicionar cada `<section>` por `nth-of-type`, que dependia da ORDEM
     do HTML e quebraria em silêncio na primeira seção nova. */
  /* Largura e respiro valem para toda tela publica. */
  .content { padding: 34px var(--gutter-desktop) 64px; }

  /* ⛔ O GRID DE DUAS COLUNAS EXIGE A CLASSE `.content-colunas` no HTML.
     `.content` sozinha tambem e usada por Saidas, Reserva/NaoEncontrado e
     pelo PAINEL ADMIN — sem esta restricao, o admin ganharia um layout de
     duas colunas que ninguem pediu, em cima de tabelas. */
  .content-colunas {
    display: grid;
    grid-template-columns: minmax(0, 1.55fr) minmax(316px, 1fr);
    column-gap: 34px;
    align-items: start;
  }
  .coluna-principal, .coluna-lateral { min-width: 0; }
  /* A lateral acompanha a rolagem: com a coluna principal mais alta, o
     câmbio e as regras sairiam de vista para quem está comparando saídas
     — e o aviso "leve reais e troque no Paraguai" é justamente o que ele
     deveria ler enquanto decide. */
  .coluna-lateral { position: sticky; top: 84px; }

  .section-title { font-size: 15px; }
  .section { margin-bottom: 22px; }

  /* -------------------------------------------------------------------
     SAÍDAS NA HOME: 2 COLUNAS
     -------------------------------------------------------------------
     Os cartões-resumo da Home são curtos (data, rota, hora, preço), então
     duas colunas mostram 6 saídas sem rolagem em vez de 3. Isto vale para
     o cartão MINI da Home; a tela de Saídas usa outra composição (ver
     abaixo), porque lá a tarefa é comparar, não espiar. */
  .coluna-principal .departure-mini { margin-bottom: 0; }
  .lista-saidas-mini {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 10px;
  }
  .departure-mini:hover { border-color: #b9c9d8; box-shadow: 0 4px 14px rgba(39,50,60,.10); }
  .departure-mini h3 { font-size: 15px; }

  .amenities { grid-template-columns: repeat(4, 1fr); }
  .amenity { min-height: 74px; }

  /* A lateral é estreita: as duas regras uma sobre a outra, não lado a lado. */
  .coluna-lateral .rules { grid-template-columns: 1fr; }
  .fleet h3 { font-size: 24px; }

  /* -------------------------------------------------------------------
     TELA DE SAÍDAS: LINHA LARGA, NÃO GRADE DE CARTÕES
     -------------------------------------------------------------------
     Esta é a decisão menos óbvia do arquivo, então o porquê:

     numa GRADE de 2 colunas os preços caem em zigue-zague e a pessoa
     precisa saltar o olho para comparar. A tarefa aqui é exatamente
     comparar — horário, duração e preço entre as saídas do dia. Em LINHA
     LARGA, cada informação fica na mesma coluna em todas as saídas:
     horários sempre à esquerda, preço e botão sempre à direita. Varredura
     vertical, uma coluna de preços alinhada.

     O cartão é o mesmo `.trip` do celular; só as áreas mudam de lugar. */
  .trip {
    display: grid;
    grid-template-columns: 244px minmax(0, 1fr) 216px;
    grid-template-areas:
      "horarios cabecalho acoes"
      "horarios titulo    acoes"
      "horarios detalhes  acoes";
    grid-template-rows: auto auto 1fr;
    column-gap: 26px;
    padding: 18px 20px;
    margin-bottom: 12px;
    align-items: start;
  }
  .trip > .trip-top     { grid-area: cabecalho; }
  .trip > h3            { grid-area: titulo; font-size: 17px; margin: 8px 0 10px; }
  .trip > .times        { grid-area: horarios; margin: 0; align-self: center;
                          background: #f2f4f6; border-radius: 9px; padding: 14px 10px; }
  .trip > .trip-meta    { grid-area: detalhes; border-top: 1px solid var(--cor-borda);
                          padding-top: 11px; align-self: end; }
  .trip > .trip-actions { grid-area: acoes; margin: 0; align-self: center;
                          display: grid; gap: 11px; }
  /* No celular preço e botão ficam lado a lado (`flex`, botão com flex:1).
     Aqui empilham, e o preço sobe para o tamanho de destaque — ele é o
     critério de comparação, não um detalhe do rodapé do cartão. */
  .trip-actions .price { flex: none; text-align: right; }
  .trip-actions .price b { font-size: 26px; }
  .trip-actions .price span { font-size: 12px; }
  .trip-actions .primary,
  .trip-actions .secondary { width: 100%; }
  .times strong { font-size: 25px; }

  /* Os chips de dia: no celular rolam na horizontal porque não cabem.
     Cabem todos aqui, então o scroll sai e a fila fica centralizada — com
     scroll, o dia selecionado podia estar fora de vista ao abrir. */
  .days {
    max-width: var(--largura-conteudo);
    margin: 0 auto;
    padding: 18px var(--gutter-desktop) 6px;
    justify-content: center;
    flex-wrap: wrap;
    overflow: visible;
  }
  .day { min-width: 78px; }
  .day:hover { border-color: #adc0d0; }

  .route-strip { padding: 13px var(--gutter-desktop); }

  /* Estado vazio: numa página larga, um parágrafo solto à esquerda parece
     erro de renderização. Centralizado, parece resposta. */
  .tela-estado { max-width: 520px; margin: 40px auto; text-align: center; }
}


/* =====================================================================
   TELA MUITO LARGA · >= 1500px — apenas mais respiro
   ---------------------------------------------------------------------
   Sem regra própria de composição: a coluna de conteúdo para de crescer e
   a sobra vira margem. Texto de 1400px de largura não se lê.
   ===================================================================== */
@media (min-width: 1500px) {
  :root { --largura-conteudo: 1280px; }
  .hero { padding-top: 64px; padding-bottom: 56px; }
}

/* ---------------------------------------------------------------------
   O RODAPE ACOMPANHA A COLUNA DE CONTEUDO
   ---------------------------------------------------------------------
   Ele e full-width por desenho (fecha a pagina de borda a borda), mas o
   CONTEUDO dele tem de comecar na mesma linha vertical do resto. Sem isto,
   o rodape alinhava em 700px enquanto o app alinhava em 428px, e os dois
   blocos apareciam desencontrados — foi o que o Ronaldo apontou como "o
   espaco na home".
   --------------------------------------------------------------------- */
@media (min-width: 1024px) {
  .rodape { margin-top: 34px; padding: 30px var(--gutter-desktop) 34px; }
  .rodape-conteudo {
    max-width: var(--largura-conteudo);
    grid-template-columns: minmax(0, 1.6fr) repeat(2, minmax(0, 1fr));
    gap: 40px;
  }
}

/* =====================================================================
   DESKTOP · TELAS DE COMPRA (Assentos, Checkout, Pix, Bilhete)
   ---------------------------------------------------------------------
   ⚠️ AQUI O DESKTOP **NAO** OCUPA A TELA INTEIRA, e isso e deliberado.
   A Home e Saidas ocupam porque sao telas de NAVEGACAO — quanto mais
   oferta visivel, melhor. Estas sao telas de FORMULARIO e de MAPA, e
   esticar deixou:
     • cada poltrona com 330px de largura (um onibus de 1440px)
     • um campo de CPF com 1370px
   Largura tem funcao aqui: o mapa precisa parecer um onibus, e campo de
   formulario largo faz o olho perder a linha entre rotulo e valor.

   Entao: coluna centralizada, com a largura que o CONTEUDO pede.
   ===================================================================== */
@media (min-width: 1024px) {

  /* ---- Mapa de poltronas: largura de onibus ---- */
  /* 560px cabe as 4 colunas de poltrona no tamanho em que foram desenhadas
     (39px de altura, ~120px de largura) sem esticar nada. */
  .bus-map,
  .legend,
  .summary-strip,
  .progress {
    max-width: 560px;
    margin-left: auto;
    margin-right: auto;
  }
  .summary-strip { margin-top: 20px; }
  /* O `.progress` (3 passos) e faixa de fundo branco no mobile; centralizado
     e estreito no desktop ele viraria uma ilha. Fica full-width, so as
     BARRAS acompanham a coluna. */
  .progress { background: transparent; border-bottom: 0; padding-top: 16px; }

  /* ---- Formulario do Checkout ---- */
  /* 620px mantem rotulo e campo na mesma linha de leitura. */
  .form-card,
  .checkout-summary,
  .pix-card,
  /* `.pix-aviso` e o "esta tela atualiza sozinha". Era `.card` sem classe e
     por isso atravessava os 1440px inteiros enquanto os cartoes ao redor
     estavam em 620px. Bloco sem classe propria e bloco que ninguem consegue
     posicionar. */
  .pix-aviso,
  .ticket,
  .barcode,
  .ticket-actions {
    max-width: 620px;
    margin-left: auto;
    margin-right: auto;
  }

  /* ---- O botao principal deixa de ser fixo ---- */
  /* No celular `.sticky-action` cola embaixo porque a tela e curta e o botao
     tem de estar ao alcance do dedo. No desktop isso vira defeito: o botao
     flutuava sobre o conteudo e COBRIA o titulo "Resumo da reserva" (visto no
     print de 1440px). Com espaco vertical sobrando, ele volta ao fluxo. */
  .sticky-action {
    position: static;
    background: none;
    /* 560px = a largura do mapa. O botao tem de terminar na MESMA linha
       vertical do bloco que ele confirma; com 620px ele ficava deslocado do
       mapa e parecia pertencer a outra coisa. */
    max-width: 560px;
    margin: 0 auto;
    padding: 18px 0 34px;
  }
  /* O Checkout e mais largo (formulario), entao o botao acompanha ELE. */
  [data-tela="checkout"] .sticky-action,
  [data-tela="pix"] .sticky-action { max-width: 620px; }
}
