/* Componentes do Hub. Mobile first: as regras soltas valem para o celular, e os blocos
   @media (min-width: 768px) ajustam tablet e desktop. Efeitos de mouse ficam em @media (hover: hover),
   para o toque não deixar um botão "preso" no estado de hover. */

/* Estrutura */
/* Barra superior: fixa no topo em todo o Hub, só o conteúdo rola (como no Robust CRM). Sticky, e
   não fixed, para seguir ocupando o lugar dela: nada fica escondido embaixo. O z-index fica acima
   do conteúdo (que vai até 1) e abaixo do fundo do menu do usuário (49), que mora dentro dela.
   Dialog, aside e toast ficam na camada de cima do navegador e não dependem disso. */
.hub-header {
  position: sticky; top: 0; z-index: 40;
  display: flex; align-items: center; justify-content: space-between; gap: var(--hub-md);
  height: calc(var(--hub-header-height) + var(--hub-safe-top));
  padding: var(--hub-safe-top) max(var(--hub-gutter), var(--hub-safe-right)) 0 max(var(--hub-gutter), var(--hub-safe-left));
  background: var(--hub-surface); border-bottom: 1px solid var(--hub-border);
}
/* Início da barra: dentro de um app, a seta de voltar e a linha antes do logo (ui.AppBarProps). */
.hub-header-start { display: flex; align-items: center; gap: var(--hub-sm); min-width: 0; }
/* A seta encosta na margem como o logo: o ícone, e não o círculo de toque, fica no alinhamento. */
.hub-header-back { margin-left: calc((var(--hub-touch) - var(--hub-icon-size)) / -2); }
.hub-header-divider { flex: none; width: 1px; height: 24px; background: var(--hub-border); }
/* Ações da barra, à direita: opções do app e botões globais (.hub-icon-button) antes do avatar. */
.hub-header-actions { display: flex; align-items: center; gap: var(--hub-sm); }
.hub-home { display: flex; align-items: center; min-height: var(--hub-touch); }
.hub-logo { display: block; width: auto; height: var(--hub-logo-height); }
.hub-logo-light { display: var(--hub-logo-light); }
.hub-logo-dark { display: var(--hub-logo-dark); }
/* Logo dentro de um app, no celular: só o ícone, para caber a seta e as opções. Na tela Início e
   do tablet para cima, o logo completo (bloco @media abaixo). */
.hub-logo.hub-logo-icon { display: none; }
.hub-header-app .hub-logo:not(.hub-logo-icon) { display: none; }
.hub-header-app .hub-logo-icon.hub-logo-light { display: var(--hub-logo-light); }
.hub-header-app .hub-logo-icon.hub-logo-dark { display: var(--hub-logo-dark); }

.hub-page {
  display: flex; flex-direction: column; gap: var(--hub-space-stack);
  max-width: var(--hub-max-width); margin: 0 auto;
  padding: var(--hub-page-top) max(var(--hub-gutter), var(--hub-safe-right))
    calc(var(--hub-page-bottom) + var(--hub-safe-bottom)) max(var(--hub-gutter), var(--hub-safe-left));
}
.hub-muted { color: var(--hub-text-secondary); font-size: 14px; line-height: 20px; }

/* Botão só com ícone, como o de opções (três pontos): redondo, com 44px de toque */
.hub-icon-button {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: var(--hub-touch); height: var(--hub-touch); padding: 0;
  background: none; border: 0; border-radius: 50%; color: var(--hub-text); cursor: pointer;
  transition: background-color var(--hub-time-hover) ease-out;
}
.hub-icon-button:active, .hub-icon-button[aria-expanded="true"] { background: var(--hub-surface-muted); }
/* Botões pequenos, cerca de 30% menores: só para ações secundárias, como abrir mais informações.
   A área de toque fica em 30px (abaixo dos 44px do Hub, acima dos 24px do WCAG 2.2). */
.hub-icon-button.hub-icon-button-small { width: var(--hub-touch-small); height: var(--hub-touch-small); }
.hub-icon-button-small .hub-icon { width: var(--hub-icon-size-small); height: var(--hub-icon-size-small); }

/* Botões */
.hub-button {
  display: inline-block; min-height: var(--hub-touch); padding: var(--hub-button-padding);
  border: 1px solid transparent; border-radius: var(--hub-radius-small);
  font: 500 14px/20px var(--hub-font-body); text-align: center; cursor: pointer;
  transition: background-color var(--hub-time-hover) ease-out, box-shadow var(--hub-time-focus) ease-out;
}
/* Botão com ícone: o ícone fica ao lado do texto. */
.hub-button:has(> .hub-icon) { display: inline-flex; align-items: center; justify-content: center; gap: var(--hub-sm); }
.hub-button > .hub-icon { width: 18px; height: 18px; }
.hub-button-primary { background: var(--hub-orange); color: var(--hub-button-primary-text); }
.hub-button-primary:active { background: var(--hub-orange-active); }
.hub-button-primary:disabled { background: var(--hub-disabled); opacity: 0.5; cursor: not-allowed; }
/* Botão cinza: só o fundo, sem borda. Direto sobre o fundo da página, ele é branco
   (--hub-button-secondary-page); dentro das superfícies brancas (cards, janelas, painéis, menus,
   cabeçalho e rodapé do chat), é o cinza-claro. */
.hub-button-secondary { background: var(--hub-button-secondary-page); color: var(--hub-text); }
:is(.hub-card, .hub-list-item, .hub-table, dialog, .hub-menu-panel, .hub-chat-header, .hub-chat-composer) .hub-button-secondary {
  background: var(--hub-button-secondary);
}
/* Janela ou aside com lista de cards: o corpo é cinza como a página, então o botão solto nele
   (Adicionar, Nova...) volta a ser branco; dentro dos cards, segue o cinza-claro. O :where segura
   a especificidade para o hover, que vem depois, continuar valendo. */
:where(.hub-dialog-body, .hub-aside-body):where(:has(.hub-card-list)) .hub-actions .hub-button-secondary {
  background: var(--hub-button-secondary-page);
}
.hub-button-secondary:active { background: var(--hub-surface-active); }
/* Perigo: só para confirmar uma ação destrutiva (como Excluir, no modal de confirmação). */
.hub-button-danger { background: var(--hub-button-danger); color: var(--hub-button-primary-text); }
.hub-button-danger:active { background: var(--hub-button-danger-hover); }
.hub-button-danger:disabled { background: var(--hub-disabled); opacity: 0.5; cursor: not-allowed; }
.hub-button.hub-button-small { min-height: var(--hub-touch-small); padding: 5px 16px; font-size: 13px; line-height: 18px; }
.hub-button-small .hub-icon { width: 16px; height: 16px; }

/* Grupo de botões */
.hub-actions { display: flex; flex-wrap: wrap; gap: var(--hub-sm); }
/* Rodapé de card: todo botão de um card fica aqui, no pé, alinhado à direita, com uma divisória
   fina em cima. Os botões quebram de linha se não couberem, e o texto de um botão não quebra. */
.hub-card-footer {
  display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--hub-sm);
  margin-top: var(--hub-space-divider); padding-top: var(--hub-space-divider); border-top: 1px solid var(--hub-border-subtle);
}
.hub-card-footer .hub-button { white-space: nowrap; }

/* Cartões */
.hub-card {
  display: block; padding: var(--hub-space-inside);
  background: var(--hub-surface); border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius-card); box-shadow: var(--hub-shadow);
}
a.hub-card { height: 100%; transition: box-shadow var(--hub-time-focus) ease, background-color var(--hub-time-hover) ease-out; }
a.hub-card:active { background: var(--hub-surface-muted); }
.hub-card h4 { margin-bottom: var(--hub-sm); }

/* Card de app na tela inicial: ícone num quadrado de fundo suave, título na cor primária e
   descrição. O título fica em 20px e negrito, que conta como texto grande para o contraste. */
.hub-app-card { display: flex; align-items: flex-start; gap: var(--hub-md); }
/* Ícone do app: só o ícone, sem fundo, na cor dos ícones (padrão: ícone nunca vai num container). */
.hub-app-icon { display: flex; flex: none; padding-top: 2px; }
.hub-app-icon .hub-icon { width: var(--hub-app-icon-size); height: var(--hub-app-icon-size); }
.hub-app-text { display: flex; flex-direction: column; min-width: 0; }
.hub-app-card .hub-card-title { margin-bottom: var(--hub-xs); }

.hub-grid {
  display: grid; gap: var(--hub-md);
  grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
}
.hub-appear { animation: hub-rise var(--hub-time-appear) ease-out; }
@keyframes hub-rise {
  from { opacity: 0; transform: translateY(20px); }
  to { opacity: 1; transform: translateY(0); }
}

/* Formulários. Campos em 16px: com menos, o iPhone dá zoom na página ao tocar no campo. */
.hub-form { display: flex; flex-direction: column; gap: var(--hub-space-stack); }
.hub-field { display: flex; flex-direction: column; gap: var(--hub-space-field); font-size: 12px; line-height: 16px; font-weight: 500; }
.hub-field small { color: var(--hub-text-secondary); font-size: 12px; font-weight: 400; }
.hub-field input, .hub-field textarea, .hub-field select {
  min-height: var(--hub-touch); padding: 12px 16px; background: var(--hub-surface); color: var(--hub-text);
  border: 1px solid var(--hub-border); border-radius: var(--hub-radius-small);
  font: 400 16px/20px var(--hub-font-body);
  transition: box-shadow var(--hub-time-focus) ease-out;
}
.hub-field textarea { resize: vertical; }
/* O select mantém a seta do navegador, que acompanha o tema claro e o escuro (color-scheme). */
.hub-field select { width: 100%; min-width: 0; cursor: pointer; }

/* Widget: select do Hub (ui.Select + select.js). O botão tem a mesma cara dos campos; o painel,
   no celular, sobe de baixo como o dialog; no desktop, abre colado ao campo (regras no @media). */
hub-select { display: block; min-width: 0; }
.hub-select-button {
  display: flex; align-items: center; gap: var(--hub-sm); width: 100%; min-width: 0;
  min-height: var(--hub-touch); padding: 12px 12px 12px 16px; background: var(--hub-surface); color: var(--hub-text);
  border: 1px solid var(--hub-border); border-radius: var(--hub-radius-small);
  font: 400 16px/20px var(--hub-font-body); text-align: left; cursor: pointer;
  transition: box-shadow var(--hub-time-focus) ease-out, border-color var(--hub-time-hover) ease-out;
}
.hub-select-value { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hub-select-value .hub-select-hint { margin-left: var(--hub-xs); vertical-align: 1px; }
.hub-select-button[data-empty] .hub-select-value { color: var(--hub-text-secondary); }
.hub-select-button .hub-icon { width: 18px; height: 18px; color: var(--hub-text-tertiary); transition: transform var(--hub-time-hover) ease-out; }
.hub-select-button[aria-expanded="true"] .hub-icon { transform: rotate(180deg); color: var(--hub-orange); }
.hub-select-button:focus-visible, .hub-select-button[aria-expanded="true"] {
  outline: none; border-color: var(--hub-orange);
  box-shadow: 0 0 0 1px var(--hub-orange), 0 0 0 4px var(--hub-focus-glow);
}
.hub-select-button[aria-invalid="true"] { border-color: var(--hub-failure); box-shadow: 0 0 0 1px var(--hub-failure); }
.hub-select-button:disabled { background: var(--hub-surface-muted); color: var(--hub-text-secondary); cursor: not-allowed; }

dialog.hub-select-panel {
  inset: auto 0 0 0; width: 100%; max-width: 100%; max-height: 70dvh;
  margin: 0; padding: 0; overflow: hidden;
  background: var(--hub-surface); color: var(--hub-text);
  border: 1px solid var(--hub-border); border-width: 1px 0 0;
  border-radius: var(--hub-radius-sheet) var(--hub-radius-sheet) 0 0; box-shadow: var(--hub-shadow-floating);
}
dialog.hub-select-panel[open] { display: flex; flex-direction: column; }
/* Sobre um aside, o painel também escurece o fundo: no celular ele é uma folha nova. */
dialog.hub-select-panel::backdrop, dialog.hub-select-panel[data-stacked]::backdrop { background: var(--hub-overlay); }
.hub-select-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--hub-md);
  padding: var(--hub-space-bar) var(--hub-space-inside); border-bottom: 1px solid var(--hub-border-subtle);
}
.hub-select-header h4 { margin: 0; }
.hub-select-search {
  display: flex; align-items: center; gap: var(--hub-sm); flex: none;
  margin: var(--hub-sm) var(--hub-sm) 0; padding: 0 var(--hub-md);
  background: var(--hub-surface-muted); border: 1px solid transparent; border-radius: var(--hub-radius-small);
  transition: box-shadow var(--hub-time-focus) ease-out, border-color var(--hub-time-hover) ease-out;
}
.hub-select-search .hub-icon { width: 18px; height: 18px; color: var(--hub-text-tertiary); }
.hub-select-search input {
  flex: 1; min-width: 0; min-height: var(--hub-touch); padding: 0; background: none; border: 0; outline: none;
  color: var(--hub-text); font: 400 16px/20px var(--hub-font-body);
}
.hub-select-search input::-webkit-search-cancel-button { display: none; -webkit-appearance: none; }
.hub-select-search:focus-within { background: var(--hub-surface); border-color: var(--hub-orange); box-shadow: 0 0 0 3px var(--hub-focus-glow); }
.hub-select-list {
  flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; outline: none;
  padding: var(--hub-sm) var(--hub-sm) calc(var(--hub-sm) + var(--hub-safe-bottom));
}
dialog.hub-select-panel[data-multiple] .hub-select-list { padding-bottom: var(--hub-sm); }
.hub-select-group + .hub-select-group, .hub-select-option + .hub-select-group { margin-top: var(--hub-xs); padding-top: var(--hub-xs); border-top: 1px solid var(--hub-border-subtle); }
.hub-select-group-title {
  padding: var(--hub-sm) var(--hub-md) var(--hub-xs);
  color: var(--hub-text-secondary); font: 600 12px/16px var(--hub-font-body); letter-spacing: 0.02em; text-transform: uppercase;
}
.hub-select-option {
  display: flex; align-items: center; gap: var(--hub-md); min-height: 48px; padding: var(--hub-menu-item-padding);
  border-radius: var(--hub-radius-small); color: var(--hub-text); font: 400 16px/22px var(--hub-font-body);
  cursor: pointer; user-select: none; -webkit-tap-highlight-color: transparent;
  transition: background-color var(--hub-time-hover) ease-out;
}
.hub-select-option[hidden] { display: none; }
.hub-select-text { flex: 1; min-width: 0; overflow-wrap: anywhere; }
/* A opção ativa (setas do teclado) ganha fundo. No celular, só depois de usar o teclado: ao abrir
   pelo toque, nada aparece destacado além da escolhida. */
dialog[data-keys] .hub-select-option.hub-select-active, .hub-select-option:active { background: var(--hub-surface-muted); }
.hub-select-option[aria-selected="true"] { font-weight: 600; }
.hub-select-option > .hub-icon { color: var(--hub-orange); visibility: hidden; }
.hub-select-option[aria-selected="true"] > .hub-icon { visibility: visible; }
.hub-select-option[aria-disabled="true"] { color: var(--hub-text-tertiary); cursor: not-allowed; }
.hub-select-hint {
  display: inline-block; flex: none; min-width: 24px; padding: 0 var(--hub-sm); border-radius: var(--hub-radius-tag);
  background: var(--hub-surface-muted); color: var(--hub-text-secondary);
  font: 600 12px/20px var(--hub-font-body); text-align: center; font-variant-numeric: tabular-nums;
}
dialog[data-keys] .hub-select-active .hub-select-hint { background: var(--hub-surface); }
/* Caixa de marcar da múltipla escolha: desenhada, com o check do sprite. */
.hub-select-box {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 20px; height: 20px; border: 1.5px solid var(--hub-text-tertiary); border-radius: 6px;
  transition: background-color var(--hub-time-hover) ease-out, border-color var(--hub-time-hover) ease-out;
}
.hub-select-box .hub-icon { width: 14px; height: 14px; color: var(--hub-button-primary-text); visibility: hidden; }
.hub-select-option[aria-selected="true"] .hub-select-box { background: var(--hub-switch-on); border-color: var(--hub-switch-on); }
.hub-select-option[aria-selected="true"] .hub-select-box .hub-icon { visibility: visible; }
.hub-select-empty { margin: 0; padding: var(--hub-lg) var(--hub-md); color: var(--hub-text-secondary); font-size: 14px; text-align: center; }
.hub-select-footer {
  display: flex; gap: var(--hub-sm); flex: none;
  padding: var(--hub-space-bar) var(--hub-space-inside) calc(var(--hub-space-bar) + var(--hub-safe-bottom));
  border-top: 1px solid var(--hub-border-subtle);
}
.hub-select-footer > .hub-button { flex: 1 1 0; min-width: 0; }

/* Widget: data e hora do Hub (ui.DateField + date.js). O botão e o painel são os do select; aqui
   ficam o calendário, a faixa do intervalo, as colunas de hora e os atalhos. */
hub-date { display: block; min-width: 0; }
hub-date [hidden] { display: none; }
.hub-date-button[aria-expanded="true"] .hub-icon { transform: none; }
dialog.hub-date-panel { max-height: 92dvh; }
.hub-date-body {
  display: flex; flex-direction: column; gap: var(--hub-md); flex: 1; min-height: 0;
  overflow-y: auto; overscroll-behavior: contain; padding: var(--hub-md) var(--hub-md) var(--hub-sm);
}
.hub-date-calendars { display: flex; gap: var(--hub-xl); justify-content: center; }
.hub-date-month { flex: 1; min-width: 0; max-width: 360px; }
.hub-date-head { display: flex; align-items: center; justify-content: space-between; gap: var(--hub-xs); margin-bottom: var(--hub-sm); }
.hub-date-nav {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: 40px; height: 40px; padding: 0; background: none; border: 0; border-radius: 50%;
  color: var(--hub-text); cursor: pointer; transition: background-color var(--hub-time-hover) ease-out;
}
.hub-date-nav .hub-icon { width: 20px; height: 20px; }
.hub-date-title {
  padding: 8px 12px; background: none; border: 0; border-radius: var(--hub-radius-small);
  color: var(--hub-text); font: 600 16px/20px var(--hub-font-heading); cursor: pointer;
  transition: background-color var(--hub-time-hover) ease-out;
}
.hub-date-title:disabled { cursor: default; }
.hub-date-nav:active, .hub-date-title:not(:disabled):active { background: var(--hub-surface-muted); }
.hub-date-grid { width: 100%; border-collapse: collapse; table-layout: fixed; }
.hub-date-grid th { padding: 0 0 var(--hub-sm); color: var(--hub-text-tertiary); font: 600 12px/16px var(--hub-font-body); text-align: center; }
.hub-date-grid td { padding: 2px 0; text-align: center; }
.hub-date-day {
  position: relative; width: 100%; max-width: 44px; height: 44px; padding: 0;
  background: none; border: 0; border-radius: 50%; color: var(--hub-text);
  font: 500 15px/20px var(--hub-font-body); font-variant-numeric: tabular-nums; cursor: pointer;
  transition: background-color var(--hub-time-hover) ease-out;
}
.hub-date-day.hub-date-outside { color: var(--hub-text-tertiary); }
.hub-date-day[aria-current="date"] { box-shadow: inset 0 0 0 1.5px var(--hub-orange); }
.hub-date-day[aria-pressed="true"] { background: var(--hub-switch-on); color: var(--hub-button-primary-text); font-weight: 700; box-shadow: none; }
.hub-date-day:disabled { opacity: 0.4; cursor: not-allowed; }
.hub-date-day:focus-visible, .hub-date-nav:focus-visible, .hub-date-title:focus-visible, .hub-date-cell:focus-visible,
.hub-date-time:focus-visible, .hub-date-preset:focus-visible { outline: 2px solid var(--hub-focus); outline-offset: 2px; }
/* Faixa do intervalo: o fundo da célula liga o início ao fim; nas pontas, só a metade de dentro. */
.hub-date-band { --hub-date-band: color-mix(in srgb, var(--hub-orange) 16%, transparent); background: var(--hub-date-band); }
.hub-date-band-preview { --hub-date-band: color-mix(in srgb, var(--hub-orange) 9%, transparent); }
.hub-date-band.hub-date-band-start { background: linear-gradient(to right, transparent 50%, var(--hub-date-band) 50%); }
.hub-date-band.hub-date-band-end { background: linear-gradient(to left, transparent 50%, var(--hub-date-band) 50%); }
.hub-date-band-end .hub-date-day:not([aria-pressed="true"]) { box-shadow: inset 0 0 0 1.5px var(--hub-orange); }
.hub-date-band:first-child:not(.hub-date-band-start) { border-radius: 22px 0 0 22px; }
.hub-date-band:last-child:not(.hub-date-band-end) { border-radius: 0 22px 22px 0; }
/* Grade de meses e de anos */
.hub-date-picks { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--hub-sm); padding-top: var(--hub-xs); }
.hub-date-cell {
  height: 52px; padding: 0; background: var(--hub-surface-muted); border: 0; border-radius: var(--hub-radius-small);
  color: var(--hub-text); font: 500 15px/20px var(--hub-font-body); cursor: pointer;
  transition: background-color var(--hub-time-hover) ease-out;
}
.hub-date-cell[aria-current="true"] { background: var(--hub-switch-on); color: var(--hub-button-primary-text); font-weight: 700; }
/* Atalhos do intervalo: chips no celular, lista à esquerda no desktop */
.hub-date-presets { display: flex; gap: var(--hub-sm); overflow-x: auto; scrollbar-width: none; flex: none; }
.hub-date-preset {
  flex: none; padding: 8px 14px; background: var(--hub-surface); border: 1px solid var(--hub-border); border-radius: var(--hub-radius-tag);
  color: var(--hub-text); font: 500 14px/20px var(--hub-font-body); white-space: nowrap; cursor: pointer;
  transition: background-color var(--hub-time-hover) ease-out;
}
.hub-date-preset:active { background: var(--hub-surface-muted); }
/* Hora: duas colunas que rolam, horas e minutos */
.hub-date-clock { display: flex; gap: var(--hub-sm); justify-content: center; flex: none; }
.hub-date-column {
  position: relative; display: flex; flex-direction: column; gap: 2px; width: 76px; height: 176px; padding: 4px;
  overflow-y: auto; overscroll-behavior: contain; scrollbar-width: thin;
  background: var(--hub-surface-muted); border-radius: var(--hub-radius-small);
}
dialog[data-mode="time"] .hub-date-column { height: 240px; }
.hub-date-time {
  flex: none; height: 40px; padding: 0; background: none; border: 0; border-radius: var(--hub-radius-small);
  color: var(--hub-text); font: 500 16px/20px var(--hub-font-body); font-variant-numeric: tabular-nums; cursor: pointer;
}
.hub-date-time[aria-selected="true"] { background: var(--hub-switch-on); color: var(--hub-button-primary-text); font-weight: 700; }
.hub-date-footer { flex-wrap: wrap; align-items: center; }
.hub-date-summary { flex: 1 0 100%; color: var(--hub-text-secondary); font-size: 14px; line-height: 20px; }

/* Grupo de campos. disabled desliga todos de uma vez, como quando só administrador altera. */
.hub-fieldset { display: flex; flex-direction: column; gap: var(--hub-space-stack); min-width: 0; margin: 0; padding: 0; border: 0; }
.hub-fieldset:disabled { opacity: 0.6; }
.hub-legend { margin-bottom: var(--hub-sm); padding: 0; font-size: 12px; line-height: 16px; font-weight: 500; }
/* Dois campos lado a lado, como início e fim de um horário */
.hub-field-row { display: grid; grid-template-columns: 1fr 1fr; gap: var(--hub-space-stack); }
.hub-field-row .hub-field input { min-width: 0; width: 100%; }

/* Chips: caixas de seleção com cara de botão, como os dias da semana. O checkbox fica invisível
   por cima, para o toque e o teclado irem para ele. */
.hub-chips { display: flex; flex-wrap: wrap; gap: var(--hub-sm); }
.hub-chip {
  position: relative; display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--hub-touch); min-height: var(--hub-touch); padding: 0 var(--hub-md);
  background: var(--hub-surface); color: var(--hub-text);
  border: 1px solid var(--hub-border); border-radius: var(--hub-radius-tag);
  font: 500 14px/20px var(--hub-font-body); cursor: pointer;
  transition: background-color var(--hub-time-hover) ease-out, border-color var(--hub-time-hover) ease-out;
}
.hub-chip input { position: absolute; inset: 0; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.hub-chip:has(input:checked) { background: var(--hub-switch-on); border-color: var(--hub-switch-on); color: var(--hub-button-primary-text); }
.hub-chip:has(input:focus-visible) { outline: 2px solid var(--hub-focus); outline-offset: 2px; }
.hub-chip:has(input:disabled) { cursor: not-allowed; }
.hub-field input:focus, .hub-field textarea:focus, .hub-field select:focus {
  outline: none; border-color: var(--hub-orange);
  box-shadow: 0 0 0 1px var(--hub-orange), 0 0 0 4px var(--hub-focus-glow);
}

/* Tabela. No celular, cada linha vira um cartão: o cabeçalho some da tela (continua para leitor de
   tela) e cada célula com data-label mostra o nome da coluna ao lado do valor. */
.hub-table { width: 100%; border-collapse: collapse; font-size: 14px; line-height: 20px; }
.hub-table, .hub-table tbody, .hub-table tr, .hub-table td { display: block; }
.hub-table thead {
  position: absolute; width: 1px; height: 1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}
.hub-table tbody { display: flex; flex-direction: column; gap: var(--hub-sm); }
.hub-table tr {
  background: var(--hub-surface); border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius-card); box-shadow: var(--hub-shadow); overflow: hidden;
}
.hub-table td { padding: 12px var(--hub-md); text-align: left; border-bottom: 1px solid var(--hub-border-subtle); }
.hub-table td:last-child { border-bottom: 0; }
/* Numeração (ui.RowNumber). No celular, o número fica no canto de cima do cartão, sem ocupar uma
   linha, e o primeiro campo ganha espaço para não encostar nele. */
.hub-table tr { position: relative; }
.hub-table td.hub-table-number {
  position: absolute; top: 14px; right: var(--hub-md); padding: 0; border: 0;
  color: var(--hub-text-tertiary); font-size: 12px; line-height: 16px; font-weight: 600; font-variant-numeric: tabular-nums;
}
.hub-table td.hub-table-number:not(:empty)::before { content: "#"; }
.hub-table tr:has(> td.hub-table-number) > td:nth-child(2) { padding-right: 64px; }
.hub-table td[data-label] { display: flex; align-items: center; justify-content: space-between; gap: var(--hub-md); }
.hub-table td[data-label]::before { content: attr(data-label); color: var(--hub-text-secondary); font-size: 12px; line-height: 16px; }
.hub-table input[type="checkbox"] { flex: none; width: 24px; height: 24px; margin: 0; accent-color: var(--hub-orange); cursor: pointer; }
/* Célula de caixa de seleção: o rótulo ocupa a linha toda, então tocar em qualquer ponto marca. */
.hub-table-check { padding: 0 !important; }
.hub-table-check label {
  display: flex; align-items: center; justify-content: space-between; gap: var(--hub-md);
  min-height: var(--hub-touch); padding: 10px var(--hub-md); cursor: pointer;
  transition: background-color var(--hub-time-hover) ease-out;
}
.hub-table-check label:active { background: var(--hub-surface-muted); }

/* Busca no topo de uma lista: o campo ocupa a largura e os filtros ficam embaixo */
.hub-search { display: flex; flex-direction: column; gap: var(--hub-sm); }

/* Barra de busca com filtros em select: no celular, o texto na primeira linha e os selects dividem
   a segunda; do tablet para cima, tudo numa linha só. */
.hub-search-bar { display: grid; grid-template-columns: 1fr 1fr; gap: var(--hub-sm); align-items: start; }
.hub-search-fields { display: contents; }
.hub-search-bar .hub-search-fields > .hub-field:first-child { grid-column: 1 / -1; }
/* "Limpar campos": no celular, botão pequeno embaixo dos campos, com o texto. */
.hub-search-bar > .hub-search-clear { grid-column: 1 / -1; justify-self: start; }
.hub-search-clear .hub-icon { width: 16px; height: 16px; }
.hub-only-mobile { display: block; }

/* Cabeçalho que ordena (ui.SortHeader): o texto e a seta são o link. */
.hub-sort {
  display: inline-flex; align-items: center; gap: var(--hub-xs);
  color: inherit; text-decoration: none; white-space: nowrap; border-radius: var(--hub-radius-small);
  transition: color var(--hub-time-hover) ease-out;
}
.hub-sort .hub-icon { width: 14px; height: 14px; color: var(--hub-text-tertiary); }
th[aria-sort="ascending"] .hub-sort, th[aria-sort="descending"] .hub-sort { color: var(--hub-text); }
th[aria-sort="ascending"] .hub-sort .hub-icon, th[aria-sort="descending"] .hub-sort .hub-icon { color: var(--hub-orange); }
.hub-sort:focus-visible { outline: 2px solid var(--hub-focus); outline-offset: 2px; }

/* Paginação (ui.Pagination): o intervalo e, ao lado, os botões das páginas. */
.hub-pagination {
  display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between;
  gap: var(--hub-sm) var(--hub-md); margin-top: var(--hub-md);
}
.hub-pagination ul { display: flex; flex-wrap: wrap; gap: var(--hub-xs); margin: 0; padding: 0; list-style: none; }
.hub-pagination-page {
  display: inline-flex; align-items: center; justify-content: center;
  min-width: var(--hub-touch); height: var(--hub-touch); padding: 0 var(--hub-sm);
  border: 1px solid var(--hub-border); border-radius: var(--hub-radius-small);
  background: var(--hub-surface); color: var(--hub-text); font-size: 14px; text-decoration: none;
  transition: background-color var(--hub-time-hover) ease-out, border-color var(--hub-time-hover) ease-out;
}
a.hub-pagination-page:active { background: var(--hub-surface-muted); }
a.hub-pagination-page:focus-visible { outline: 2px solid var(--hub-focus); outline-offset: 2px; }
.hub-pagination-page[aria-current="page"] {
  background: var(--hub-orange); border-color: var(--hub-orange); color: var(--hub-button-primary-text); font-weight: 600;
}
.hub-pagination-page[aria-disabled="true"] { color: var(--hub-disabled); cursor: default; }
.hub-pagination-gap { display: inline-flex; align-items: center; height: var(--hub-touch); padding: 0 var(--hub-xs); color: var(--hub-text-secondary); }

/* Acima do grid: a contagem e, no celular, o select de ordenar. */
.hub-results-head { display: flex; flex-wrap: wrap; align-items: center; justify-content: space-between; gap: var(--hub-sm); margin-bottom: var(--hub-sm); }
.hub-results-head .hub-results-count { margin: 0; }
/* "Limpar campos": botão discreto embaixo da barra de busca, só o ícone e o texto. */
.hub-search-clear:focus-visible { outline: 2px solid var(--hub-focus); outline-offset: 2px; }
.hub-results-head .hub-field { flex: 0 1 220px; }
/* Valor de uma célula com mais de uma linha (etiqueta + resposta, data + autor). No celular fica à
   direita, ao lado do nome da coluna. */
.hub-cell { display: flex; flex-direction: column; align-items: flex-end; gap: var(--hub-xs); min-width: 0; text-align: right; }
.hub-cell .hub-dispatch-reply { max-width: 28ch; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.hub-table .hub-table-actions { display: flex; justify-content: flex-end; }
/* Ações da linha: "Abrir conversa" à vista e o menu "Mais ações" com as outras (seção 7 do app). */
.hub-row-actions { display: flex; align-items: center; justify-content: flex-end; gap: var(--hub-xs); }
.hub-table-more { display: inline-flex; align-items: center; justify-content: center; width: var(--hub-touch); padding: 0; }
.hub-table-more .hub-icon { width: 20px; height: 20px; }
.hub-executed-by { font-size: 12px; line-height: 16px; }
.hub-cell-date { flex-direction: row; gap: 6px; }
/* No card do celular, valor de várias linhas alinha o nome da coluna em cima. */
.hub-table td[data-label]:has(> .hub-cell, > .hub-property) { align-items: flex-start; }
.hub-nowrap { white-space: nowrap; }

/* Linha de tabela não lida pelo Hub: o ponto vem antes do nome (hub-unread-dot) e a resposta fica
   em negrito, como no card. */
.hub-row-unread .hub-dispatch-reply { font-weight: 700; }

/* Campo de busca (ui.SearchField): a lupa fica dentro do campo, no fim, e é o botão que envia.
   O "x" de limpar do navegador sai, para não disputar espaço com a lupa. */
.hub-search-field { position: relative; display: flex; }
.hub-search-field input { flex: 1; min-width: 0; padding-right: calc(var(--hub-touch) + var(--hub-xs)); }
.hub-search-field input::-webkit-search-cancel-button,
.hub-search-field input::-webkit-search-decoration { -webkit-appearance: none; appearance: none; }
.hub-search-button {
  position: absolute; top: 0; right: 0; bottom: 0;
  display: flex; align-items: center; justify-content: center; width: var(--hub-touch); padding: 0;
  background: none; border: 0; border-radius: 0 var(--hub-radius-small) var(--hub-radius-small) 0;
  color: var(--hub-text-secondary); cursor: pointer;
  transition: color var(--hub-time-hover) ease-out, background-color var(--hub-time-hover) ease-out;
}
.hub-search-button:active { color: var(--hub-text); background: var(--hub-surface-muted); }
.hub-search-button:focus-visible { outline: 2px solid var(--hub-focus); outline-offset: -2px; }

/* Lista de cartões que cresce com "Carregar mais". Enquanto a busca carrega, a lista atual fica
   apagada (o HTMX põe htmx-request no indicador). */
.hub-results { display: flex; flex-direction: column; gap: var(--hub-space-list); transition: opacity var(--hub-time-hover) ease-out; }
.hub-results.htmx-request { opacity: 0.55; }
.hub-list { display: flex; flex-direction: column; gap: var(--hub-space-list); }
/* Controle de acesso: as duas partes (usuários e administradores) e a lista compacta de baixo. */
.hub-access-section { display: flex; flex-direction: column; gap: var(--hub-sm); margin-top: var(--hub-xl); }
.hub-access-section h2 { margin: 0; }
.hub-access-section > p { margin: 0; }
.hub-list-item {
  display: flex; flex-direction: column; gap: var(--hub-xs); padding: var(--hub-space-item);
  background: var(--hub-surface); border: 1px solid var(--hub-border);
  border-radius: var(--hub-radius-card); box-shadow: var(--hub-shadow);
  font-size: 14px; line-height: 20px; overflow-wrap: anywhere;
}
.hub-list-head { display: flex; align-items: flex-start; justify-content: space-between; gap: var(--hub-sm); }

/* Texto principal de um card ou item de lista (título do app, título do imóvel...): sempre na cor
   primária, em 20px negrito. O tamanho não é enfeite: o laranja do tema claro dá 3,41:1, que só
   passa no contraste como texto grande (a partir de 19px em negrito). */
.hub-card-title { color: var(--hub-heading-accent); font: 700 20px/28px var(--hub-font-heading); }
/* Envios da Atualização de imóveis: resposta e quem conversou, lado a lado no tablet. */
.hub-dispatch-facts { display: grid; gap: var(--hub-sm); margin: var(--hub-xs) 0 0; padding-top: var(--hub-space-divider); border-top: 1px solid var(--hub-border-subtle); }
.hub-dispatch-facts dt { color: var(--hub-text-secondary); font-size: 12px; line-height: 16px; }
.hub-dispatch-facts dd { margin: 0; }
.hub-dispatch-reply { font-weight: 500; white-space: pre-line; }
/* As duas situações do envio (mensagem e execução), lado a lado. Sem espaço ao lado do nome (no
   celular), descem para a linha de baixo, em vez de espremer o nome. */
.hub-list-head:has(> .hub-dispatch-tags) { flex-wrap: wrap; }
.hub-dispatch-tags { display: flex; flex-wrap: wrap; gap: var(--hub-sm); }
/* Envio não lido pelo Hub: ponto na cor primária antes do nome e a resposta em negrito. */
.hub-unread-dot {
  display: inline-block; width: 10px; height: 10px; margin-right: var(--hub-sm); vertical-align: middle;
  border-radius: 50%; background: var(--hub-heading-accent);
}
.hub-list-item-unread .hub-dispatch-reply { font-weight: 700; }

/* Proprietários de um imóvel na busca do "Enviar pergunta": nome à esquerda, botão à direita. */
.hub-owner-list { display: flex; flex-direction: column; gap: var(--hub-sm); margin-top: var(--hub-xs); padding-top: var(--hub-sm); border-top: 1px solid var(--hub-border-subtle); }
.hub-owner-list > li { display: flex; flex-direction: column; }
.hub-owner-list > li + li { padding-top: var(--hub-sm); border-top: 1px solid var(--hub-border-subtle); }
.hub-owner-name { display: flex; flex-direction: column; min-width: 0; }
.hub-owner-name small { font-size: 12px; line-height: 16px; }

/* Botão com ícone e texto */
.hub-button-with-icon { display: inline-flex; align-items: center; justify-content: center; gap: var(--hub-sm); }

/* Chat da Atualização de imóveis, como o WhatsApp Web: o cabeçalho com o proprietário fica preso
   embaixo da barra do Hub e o rodapé com o campo, ao pé da tela; só as mensagens correm entre os
   dois. Quem rola é a página, não uma caixa: assim a barra de endereço do celular some, o toque
   no topo do iPhone funciona e o follow.js segue a página. A tela ocupa a altura toda, para o
   rodapé ficar no pé mesmo numa conversa curta. */
/* A conversa ocupa a largura inteira, mesmo em monitores grandes; os balões têm o limite deles. */
.hub-page:has(> .hub-chat-layout) { max-width: none; padding-top: 0; padding-bottom: 0; }
.hub-chat-layout { margin: 0 calc(var(--hub-gutter) * -1); }
.hub-chat-screen {
  display: flex; flex-direction: column; min-width: 0;
  min-height: calc(100dvh - var(--hub-header-height) - var(--hub-safe-top));
  /* Clima do WhatsApp: o fundo bege com a nossa textura (desenhos do sprite, bem apagados). */
  background: var(--hub-chat-pattern) repeat, var(--hub-chat-background);
}
/* Cabeçalho, igual no desktop e no celular, sempre numa linha: à esquerda o nome com o + colado;
   à direita, juntos, as situações e o "…", que nunca encolhem. Sem espaço, quem cede é o nome,
   cortado com reticências, com o + logo depois delas: o + nunca some. z-index 3: acima do
   rodapé (2), para os painéis daqui abrirem por cima dele no celular; abaixo da barra do Hub (40). */
.hub-chat-header {
  position: sticky; top: calc(var(--hub-header-height) + var(--hub-safe-top)); z-index: 3;
  display: flex; flex-wrap: nowrap; align-items: center; justify-content: space-between; gap: var(--hub-sm);
  padding: var(--hub-xs) var(--hub-gutter); background: var(--hub-surface); border-bottom: 1px solid var(--hub-border);
}
.hub-chat-title { display: flex; align-items: center; gap: 2px; flex: 1 1 auto; min-width: var(--hub-touch-small); }
.hub-chat-title h1 {
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-size: 16px; line-height: 24px; font-weight: 600;
}
.hub-chat-owner, .hub-chat-actions > .hub-icon-button { color: var(--hub-icon); }
/* Botão de pessoas antes do nome (abre a lista de conversas), só no celular. O ícone, e não o
   círculo de toque, encosta na margem, como a seta da barra. */
.hub-chat-list-open { flex: none; color: var(--hub-icon); margin-left: calc((var(--hub-touch) - var(--hub-icon-size)) / -2); }

/* Lista de conversas, como os contatos do WhatsApp Web. No desktop, é a coluna da esquerda (bloco
   de 768px). No celular fica escondida e abre como um aside, mas da esquerda: painel por cima de
   tudo, inclusive da barra do Hub (z-index 46, com o fundo escurecido em 45, abaixo dos menus, 49),
   deixando à direita uma faixa da conversa, escurecida, que fecha a lista ao ser tocada. A página
   de trás trava (base.css). Só a lista de pessoas rola; a busca e os filtros ficam parados. */
.hub-chat-list { display: none; flex-direction: column; min-height: 0; background: var(--hub-surface); }
@media (max-width: 767.98px) {
  .hub-chat-layout[data-list-open]::before {
    content: ""; position: fixed; inset: 0; z-index: 45; background: var(--hub-overlay);
    animation: hub-fade-in var(--hub-time-enter) var(--hub-ease-enter);
  }
  .hub-chat-layout[data-list-open] .hub-chat-list {
    display: flex; position: fixed; z-index: 46; inset: 0 auto 0 0;
    width: min(var(--hub-aside-width), calc(100vw - 56px));
    padding: var(--hub-safe-top) 0 var(--hub-safe-bottom) var(--hub-safe-left);
    box-shadow: var(--hub-shadow-floating);
    animation: hub-slide-in-left var(--hub-time-aside) var(--hub-ease-enter);
  }
  .hub-chat-layout[data-list-open] .hub-chat-list.hub-leaving {
    animation: hub-slide-out-left var(--hub-time-aside-leave) var(--hub-ease-leave) forwards;
  }
  .hub-chat-layout[data-list-open]:has(> .hub-chat-list.hub-leaving)::before {
    animation: hub-fade-out var(--hub-time-leave) var(--hub-ease-leave) forwards;
  }
}
.hub-chat-list-top {
  flex: none; display: flex; flex-direction: column; gap: var(--hub-sm);
  padding: var(--hub-sm) var(--hub-gutter) var(--hub-md); border-bottom: 1px solid var(--hub-border);
}
.hub-chat-list-head { display: flex; align-items: center; justify-content: space-between; gap: var(--hub-sm); min-height: var(--hub-touch); }
.hub-chat-list-title { font-size: 16px; line-height: 24px; font-weight: 600; }
.hub-chat-list-close { color: var(--hub-icon); margin-right: calc((var(--hub-touch) - var(--hub-icon-size)) / -2); }
/* A lupa da busca local é só um sinal: a busca roda enquanto se digita, sem botão. */
.hub-search-icon {
  position: absolute; top: 0; right: 0; bottom: 0; width: var(--hub-touch);
  display: flex; align-items: center; justify-content: center; color: var(--hub-text-secondary); pointer-events: none;
}
.hub-chat-list .hub-chips { gap: var(--hub-xs); }
.hub-chat-list .hub-chip { min-height: 32px; padding: 0 12px; }
.hub-chat-list-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; }
/* Cada pessoa é um card arredondado, com as mesmas margens laterais da busca e dos chips: o fundo
   do selecionado e do hover nunca encosta nas laterais. */
.hub-chat-contacts { display: flex; flex-direction: column; gap: var(--hub-sm); padding: var(--hub-sm) var(--hub-gutter); }
.hub-chat-list-empty { padding: var(--hub-lg) var(--hub-gutter); text-align: center; }
.hub-chat-contact {
  display: flex; flex-direction: column; gap: 4px; padding: 12px;
  border: 1px solid var(--hub-border); border-radius: var(--hub-radius-card); color: var(--hub-text); text-decoration: none;
  transition: background-color var(--hub-time-hover) ease-out, border-color var(--hub-time-hover) ease-out;
}
/* A conversa aberta fica um tom acima do hover, para a etiqueta cinza continuar visível. */
/* A conversa aberta é marcada pelo fundo: a borda some na cor dele, sem mudar o tamanho do card. */
.hub-chat-contact[aria-current="page"] { background: var(--hub-surface-hover); border-color: var(--hub-surface-hover); }
.hub-chat-contact:focus-visible { outline: 2px solid var(--hub-focus); outline-offset: -2px; }
.hub-chat-contact-top { display: flex; align-items: baseline; justify-content: space-between; gap: var(--hub-sm); min-width: 0; }
.hub-chat-contact-name { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; font-size: 15px; line-height: 22px; font-weight: 600; }
.hub-chat-contact-time { flex: none; color: var(--hub-text-secondary); font-size: 12px; line-height: 16px; }
.hub-chat-contact-property {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  color: var(--hub-text-secondary); font-size: 14px; line-height: 20px;
}
.hub-chat-contact-bottom { display: flex; align-items: center; gap: var(--hub-sm); min-height: 20px; }
.hub-chat-contact-bottom .hub-unread-dot { margin: 0 0 0 auto; }
[data-chat-item][data-unread] .hub-chat-contact-name { font-weight: 700; }
.hub-chat-status { display: flex; flex: none; flex-wrap: nowrap; align-items: center; gap: var(--hub-sm); margin-left: auto; }
.hub-chat-line { display: flex; align-items: center; gap: var(--hub-xs); font-size: 14px; line-height: 20px; overflow-wrap: anywhere; }
.hub-chat-line .hub-icon { flex: none; width: 16px; height: 16px; color: var(--hub-icon); }
/* Painel do + (dados do proprietário) e janela "Dados do proprietário". */
/* Imóvel em todo o app: "(Cód. N) Tipo X quartos" e, embaixo, o endereço. */
.hub-property { display: flex; flex-direction: column; gap: 2px; min-width: 0; overflow-wrap: anywhere; }
.hub-property-name { font-weight: 600; }
.hub-property-address { color: var(--hub-text-secondary); font-size: 14px; line-height: 20px; }
/* Janela "Dados do proprietário": o bloco do proprietário fica fixo em cima e só a lista de imóveis
   rola. Nesta janela o corpo do dialog não rola nem tem espaço próprio; os dois blocos cuidam disso. */
dialog:has(.hub-owner-details) .hub-dialog-body { overflow: hidden; padding: 0; min-height: 0; }
dialog:has(.hub-owner-details) .hub-dialog-body > div { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.hub-owner-details { display: flex; flex-direction: column; flex: 1; min-height: 0; }
/* O bloco do proprietário e cada imóvel: o conteúdo ocupa a largura e o link do CRM fica no canto
   de cima, à direita. */
.hub-owner-top {
  flex: none; display: flex; align-items: flex-start; gap: var(--hub-sm); background: var(--hub-surface);
  padding: var(--hub-space-bar) var(--hub-space-inside); border-bottom: 1px solid var(--hub-border);
}
.hub-owner-info { display: flex; flex-direction: column; align-items: flex-start; gap: var(--hub-xs); flex: 1; min-width: 0; }
.hub-crm-link {
  flex: none; color: var(--hub-icon);
  /* o ícone, e não o círculo de toque, encosta na borda direita e na linha do título */
  margin: -3px calc((var(--hub-touch-small) - var(--hub-icon-size-small)) / -2) 0 0;
}
.hub-owner-scroll { flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain; padding: var(--hub-space-bar) var(--hub-space-inside); }
.hub-owner-title { font: 700 18px/24px var(--hub-font-heading); overflow-wrap: anywhere; }
/* O sublinhado do telefone existe sempre, transparente, e aparece suave no hover: sublinhado não
   anima, a cor dele sim. */
.hub-owner-phone { color: var(--hub-text); text-decoration: none; }
.hub-owner-phone span {
  text-decoration: underline; text-decoration-color: transparent; text-underline-offset: 2px;
  transition: text-decoration-color var(--hub-time-hover) ease-out;
}
.hub-owner-count { font-size: 14px; line-height: 20px; font-weight: 600; padding-bottom: var(--hub-sm); }
/* Os dias para desatualizar e a situação, numa linha só, logo abaixo do imóvel, dentro do card. */
.hub-owner-property-tags { display: flex; flex-wrap: wrap; gap: var(--hub-xs); margin-top: 2px; }
/* Marcações sobre o fundo (carregando, dia, envio): pílula clara, como as mensagens de sistema
   do WhatsApp, para o texto continuar legível sobre a textura. */
.hub-chat-loading {
  align-self: center; margin: var(--hub-md) 0; padding: 4px 12px; border-radius: var(--hub-radius-tag);
  background: var(--hub-surface); box-shadow: var(--hub-bubble-shadow); font-size: 14px; line-height: 20px;
}
.hub-chat { flex: 1; display: flex; flex-direction: column; gap: var(--hub-sm); padding: var(--hub-md) var(--hub-gutter); }
/* Separador de dia ("Hoje", "Ontem", a data), como a pílula do WhatsApp. */
.hub-chat-day { align-self: center; margin: var(--hub-xs) 0; }
.hub-chat-day span {
  display: block; padding: 4px 12px; border-radius: var(--hub-radius-tag);
  background: var(--hub-surface); box-shadow: var(--hub-bubble-shadow);
  color: var(--hub-text-secondary); font-size: 12px; line-height: 16px; font-weight: 500;
}
.hub-chat-mark {
  display: flex; flex-direction: column; align-items: center; gap: 2px; align-self: center; max-width: min(90%, 560px);
  margin: var(--hub-xs) 0; padding: var(--hub-sm) var(--hub-md); text-align: center; font-size: 14px; line-height: 20px;
  background: var(--hub-surface); border-radius: var(--hub-radius-card); box-shadow: var(--hub-bubble-shadow);
  scroll-margin-top: calc(var(--hub-header-height) + var(--hub-safe-top) + 96px); /* abaixo do cabeçalho preso */
}
.hub-chat-mark small { font-size: 12px; line-height: 16px; }
.hub-bubble {
  display: flex; flex-direction: column; gap: var(--hub-xs); max-width: min(85%, 560px); padding: var(--hub-bubble-padding);
  border-radius: var(--hub-radius-card); box-shadow: var(--hub-bubble-shadow);
  color: var(--hub-text); font-size: 14px; line-height: 20px; overflow-wrap: anywhere;
}
/* Recebida branca e enviada em amarelo suave, como o WhatsApp (que usa verde). */
.hub-bubble-in { align-self: flex-start; background: var(--hub-bubble-in); border-bottom-left-radius: var(--hub-xs); }
.hub-bubble-out { align-self: flex-end; background: var(--hub-bubble-out); border-bottom-right-radius: var(--hub-xs); }
.hub-bubble-author { color: var(--hub-bubble-meta); font-size: 12px; line-height: 16px; font-weight: 500; }
.hub-bubble-body { white-space: pre-line; }
.hub-bubble-choices { display: flex; flex-wrap: wrap; gap: var(--hub-xs); }
.hub-bubble-choices li {
  padding: 4px 10px; border: 1px solid var(--hub-border); border-radius: var(--hub-radius-tag);
  background: var(--hub-surface); font-size: 12px; line-height: 16px; font-weight: 500;
}
.hub-bubble-meta { align-self: flex-end; display: flex; gap: var(--hub-sm); color: var(--hub-bubble-meta); font-size: 12px; line-height: 16px; }
.hub-bubble-failed { color: var(--hub-failure-text); }
/* Tiques da mensagem enviada, ao lado da hora: cinza (enviada, entregue), azul (lida) ou o alerta
   vermelho de não entregue. */
.hub-tick { display: inline-flex; align-items: center; gap: 4px; color: var(--hub-bubble-meta); }
.hub-tick .hub-icon { width: 16px; height: 16px; color: inherit; } /* a cor é a do tique, não a de ícone */
.hub-tick-read { color: var(--hub-tick-read); }
.hub-tick-failed { color: var(--hub-failure-text); }
/* Pergunta na fila (A3b): o relógio no lugar do tique, até ela sair. */
.hub-tick-queued { color: var(--hub-text-secondary); }
.hub-chat-composer {
  position: sticky; bottom: 0; z-index: 2;
  padding: var(--hub-sm) var(--hub-gutter) calc(var(--hub-sm) + var(--hub-safe-bottom));
  background: var(--hub-surface); border-top: 1px solid var(--hub-border);
}
.hub-chat-form { display: flex; align-items: flex-end; gap: var(--hub-sm); }
.hub-chat-field { flex: 1; min-width: 0; }
/* O campo começa com uma linha e cresce até cinco (autogrow.js); passou disso, rola. */
.hub-chat-field textarea { resize: none; max-height: calc(5 * 20px + 24px + 2px); overflow-y: auto; }
.hub-chat-send { color: var(--hub-icon); }
/* Ícones das pontas do rodapé e do "…" do cabeçalho na linha das bordas (do nome e dos balões):
   o botão de 44px puxa para fora a sobra em volta do ícone, como a seta de voltar da barra. */
.hub-chat-composer .hub-chat-quick > .hub-icon-button { margin-left: calc((var(--hub-touch) - var(--hub-icon-size)) / -2); }
.hub-chat-composer .hub-chat-send,
.hub-chat-actions > .hub-icon-button { margin-right: calc((var(--hub-touch) - var(--hub-icon-size)) / -2); }
/* Mensagens prontas: título e começo do texto em cada item. O painel cabe a lista longa. */
.hub-menu-item.hub-quick-reply { flex-direction: column; align-items: flex-start; gap: 2px; }
.hub-quick-reply small { color: var(--hub-text-secondary); font-size: 12px; line-height: 16px; font-weight: 400; }
.hub-chat-quick .hub-menu-panel { max-height: 60dvh; overflow-y: auto; }
/* Perguntas da Atualização de imóveis: o texto, as respostas e, no formulário, só os campos do
   tipo escolhido. O :has() mostra e esconde sem JS; os campos escondidos vão junto no envio e o
   servidor os ignora. */
.hub-question-text { white-space: pre-line; }
.hub-question-choices { display: flex; flex-direction: column; gap: var(--hub-xs); margin-top: var(--hub-xs); padding-top: var(--hub-sm); border-top: 1px solid var(--hub-border-subtle); }
.hub-question-choices li { display: flex; flex-wrap: wrap; column-gap: var(--hub-sm); }
.hub-question-choice { font-weight: 500; }
.hub-question-kind { display: none; flex-direction: column; gap: var(--hub-md); }
.hub-question-form:has(input[name="kind"][value="yes_no"]:checked) .hub-question-kind[data-kind="yes_no"],
.hub-question-form:has(input[name="kind"][value="options"]:checked) .hub-question-kind[data-kind="options"],
.hub-question-form:has(input[name="kind"][value="free_text"]:checked) .hub-question-kind[data-kind="free_text"] { display: flex; }
.hub-answer-message, .hub-answer-reason { display: none; }
.hub-answer:has(option:is([value="finish"], [value="assignee"], [value="keep"], [value="archive"]):checked) .hub-answer-message,
.hub-option:has(option:is([value="finish"], [value="assignee"], [value="keep"], [value="archive"]):checked) .hub-answer-message,
.hub-answer:has(option[value="archive"]:checked) .hub-answer-reason,
.hub-option:has(option[value="archive"]:checked) .hub-answer-reason { display: block; }
/* Por que o envio ficou em "Requer ação" quando a ação automática no CRM falhou. */
/* Item de menu desligado, com o motivo embaixo do nome (ex.: Nova conversa com ação pendente). */
.hub-menu-item:disabled { color: var(--hub-text-tertiary); cursor: not-allowed; }
.hub-menu-item:disabled .hub-icon { color: var(--hub-text-tertiary); }
.hub-menu-item-hint > span { display: flex; flex-direction: column; }
.hub-menu-item-hint small { color: var(--hub-text-tertiary); font-size: 12px; font-weight: 400; line-height: 16px; }
.hub-execution-note {
  display: flex; align-items: flex-start; gap: var(--hub-xs);
  color: var(--hub-warning-text); font-size: 14px; line-height: 20px; overflow-wrap: anywhere;
}
.hub-execution-note .hub-icon { flex: none; width: 16px; height: 16px; margin-top: 2px; color: inherit; }
.hub-option-list { display: flex; flex-direction: column; gap: var(--hub-md); }
.hub-option {
  display: flex; flex-direction: column; gap: var(--hub-md); padding: var(--hub-md);
  border: 1px solid var(--hub-border); border-radius: var(--hub-radius-card);
}
.hub-option-head { display: flex; align-items: flex-end; gap: var(--hub-sm); }
.hub-option-head .hub-field { flex: 1; min-width: 0; }
.hub-icon-button:disabled { opacity: 0.4; cursor: not-allowed; }
.hub-list-more { display: flex; flex-direction: column; align-items: stretch; gap: var(--hub-sm); padding-top: var(--hub-sm); text-align: center; }

/* Cartão vazio, no lugar do conteúdo enquanto ele carrega. Sem animação: o design system não
   usa animação em loop. */
.hub-skeleton { min-height: 120px; background: var(--hub-surface-muted); border-color: var(--hub-border-subtle); box-shadow: none; }

/* Switch: o checkbox fica invisível por cima do desenho, para o toque e o teclado irem para ele */
.hub-switch { position: relative; display: inline-flex; flex: none; width: 44px; height: 24px; }
.hub-switch input { position: absolute; inset: 0; z-index: 1; width: 100%; height: 100%; margin: 0; opacity: 0; cursor: pointer; }
.hub-switch-track {
  position: relative; width: 100%; height: 100%; border-radius: var(--hub-radius-tag);
  background: var(--hub-switch-off); transition: background-color var(--hub-time-hover) ease-out;
}
.hub-switch-track::after {
  content: ""; position: absolute; top: 2px; left: 2px; width: 20px; height: 20px; border-radius: 50%;
  background: var(--hub-switch-knob); box-shadow: var(--hub-shadow);
  transition: transform var(--hub-time-enter) var(--hub-ease-enter);
}
.hub-switch input:checked + .hub-switch-track { background: var(--hub-switch-on); }
.hub-switch input:checked + .hub-switch-track::after { transform: translateX(20px); }
.hub-switch input:focus-visible + .hub-switch-track { outline: 2px solid var(--hub-focus); outline-offset: 2px; }
.hub-switch input:disabled { cursor: not-allowed; }
.hub-switch input:disabled + .hub-switch-track { opacity: 0.5; }

/* Lista de preferências (Minha conta): a linha inteira é o rótulo, então tocar em qualquer
   ponto dela liga ou desliga */
.hub-settings li + li { border-top: 1px solid var(--hub-border-subtle); }
.hub-setting { display: flex; align-items: center; gap: var(--hub-md); min-height: var(--hub-touch); padding: var(--hub-sm) 0; cursor: pointer; }
.hub-setting-icon { display: flex; flex: none; }
.hub-setting-text { display: flex; flex: 1; flex-direction: column; gap: 2px; font-size: 14px; line-height: 20px; }
.hub-setting-text small { color: var(--hub-text-secondary); font-size: 12px; line-height: 16px; }

/* Etiquetas */
.hub-tag {
  display: inline-block; padding: var(--hub-tag-padding); border-radius: var(--hub-radius-tag);
  background: var(--hub-surface-muted); color: var(--hub-text);
  font-size: 12px; line-height: 16px; font-weight: 500; white-space: nowrap;
}
.hub-tag-failure { background: var(--hub-failure-bg); color: var(--hub-failure-text); }
.hub-tag-success { background: var(--hub-success-bg); color: var(--hub-success-text); }
.hub-tag-warning { background: var(--hub-warning-bg); color: var(--hub-warning-text); }
.hub-tag-info { background: var(--hub-info-bg); color: var(--hub-info-text); }
/* Etiqueta que é botão, como o "+1 pendente" do chat: abre algo. No hover, ganha um contorno na
   cor do texto dela. */
.hub-tag-button {
  border: 0; cursor: pointer; font-family: inherit;
  transition: box-shadow var(--hub-time-hover) ease-out;
}
/* Várias etiquetas lado a lado, como no topo de um cartão */
.hub-tags { display: flex; flex-wrap: wrap; justify-content: flex-end; gap: var(--hub-xs); }

/* Estado vazio */
.hub-empty { display: flex; flex-direction: column; align-items: flex-start; gap: var(--hub-sm); padding: var(--hub-lg) 0; }

/* Tela de login. dvh acompanha a barra de endereço do celular, que aparece e some. */
.hub-public {
  min-height: 100vh; min-height: 100dvh; display: flex; align-items: center; justify-content: center;
  padding: calc(var(--hub-md) + var(--hub-safe-top)) max(var(--hub-md), var(--hub-safe-right))
    calc(var(--hub-md) + var(--hub-safe-bottom)) max(var(--hub-md), var(--hub-safe-left));
  background: var(--hub-public-background);
}
.hub-public > .hub-card { width: 100%; max-width: var(--hub-public-width); }
.hub-public .hub-logo { height: var(--hub-logo-height-public); margin-bottom: var(--hub-lg); }

/* Widgets: toast e alerta. No celular, o toast ocupa a largura toda, embaixo, acima da barra do
   iPhone; no tablet e no desktop, fica no canto. */
hub-toasts {
  position: fixed; inset: auto var(--hub-md) calc(var(--hub-md) + var(--hub-safe-bottom)) var(--hub-md); z-index: 100;
  width: auto; margin: 0; padding: 0; background: none; border: 0; overflow: visible;
  display: flex; flex-direction: column; gap: var(--hub-sm);
}
hub-toast {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--hub-md);
  padding: var(--hub-md) var(--hub-lg); background: var(--hub-surface); color: var(--hub-text);
  border: 1px solid var(--hub-border); border-left-width: 4px; border-radius: var(--hub-radius-small);
  box-shadow: var(--hub-shadow-floating); font-size: 14px; line-height: 20px;
}
hub-toast[kind="success"] { border-left-color: var(--hub-success); }
hub-toast[kind="warning"] { border-left-color: var(--hub-warning); }
hub-toast[kind="failure"] { border-left-color: var(--hub-failure); }
hub-toast[kind="info"] { border-left-color: var(--hub-info); }

hub-alerts { display: flex; flex-direction: column; gap: var(--hub-sm); }
hub-alerts:empty { display: none; }
hub-alert {
  display: flex; align-items: flex-start; justify-content: space-between; gap: var(--hub-md);
  padding: 12px var(--hub-md); border-left: 4px solid; border-radius: var(--hub-radius-small);
  font-size: 14px; line-height: 20px;
}
hub-alert[kind="success"] { background: var(--hub-success-bg); color: var(--hub-success-text); border-color: var(--hub-success); }
hub-alert[kind="warning"] { background: var(--hub-warning-bg); color: var(--hub-warning-text); border-color: var(--hub-warning); }
hub-alert[kind="failure"] { background: var(--hub-failure-bg); color: var(--hub-failure-text); border-color: var(--hub-failure); }
hub-alert[kind="info"] { background: var(--hub-info-bg); color: var(--hub-info-text); border-color: var(--hub-info); }

/* Botão X: a área de toque tem 44px, mas a margem negativa faz ele ocupar só o tamanho do ícone. */
.hub-close {
  display: inline-flex; align-items: center; justify-content: center; flex: none;
  width: var(--hub-touch); height: var(--hub-touch); margin: calc((var(--hub-touch) - var(--hub-icon-size)) / -2);
  padding: 0; background: none; border: 0; border-radius: 50%; color: inherit; cursor: pointer;
  transition: background-color var(--hub-time-hover) ease-out;
}
.hub-close:active { background: var(--hub-surface-muted); }

/* Widget: dialog (ui.Dialog e o modal de confirmação). No celular, sobe do rodapé como um painel
   da largura da tela, perto do polegar; no tablet e no desktop, fica centralizado. */
dialog.hub-dialog {
  inset: auto 0 0 0; width: 100%; max-width: 100%; max-height: calc(100dvh - var(--hub-xl));
  margin: 0; padding: 0; overflow: hidden;
  background: var(--hub-surface); color: var(--hub-text);
  border: 1px solid var(--hub-border); border-width: 1px 0 0;
  border-radius: var(--hub-radius-sheet) var(--hub-radius-sheet) 0 0;
  box-shadow: var(--hub-shadow-floating);
}
dialog.hub-dialog[open] { display: flex; flex-direction: column; }
dialog.hub-dialog::backdrop { background: var(--hub-overlay); }
.hub-dialog-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--hub-md);
  padding: var(--hub-space-bar) var(--hub-space-inside); border-bottom: 1px solid var(--hub-border);
}
.hub-dialog-body {
  display: flex; flex-direction: column; gap: var(--hub-space-stack);
  flex: 1; overflow-y: auto; overscroll-behavior: contain; padding: var(--hub-space-inside);
}
.hub-dialog-body:last-child { padding-bottom: calc(var(--hub-space-inside) + var(--hub-safe-bottom)); }
/* Rodapé do dialog: como o do aside, os botões dividem a largura por igual em qualquer tela. */
.hub-dialog-footer {
  display: flex; gap: var(--hub-sm);
  padding: var(--hub-space-bar) var(--hub-space-inside) calc(var(--hub-space-bar) + var(--hub-safe-bottom));
  border-top: 1px solid var(--hub-border);
}
.hub-dialog-footer > .hub-button { flex: 1 1 0; min-width: 0; }

/* Popup de alerta (problem.js): o aviso enfático de um formulário. O ícone ao lado do título, sem
   fundo, com a cor nele: amarelo para validação e regra; vermelho para falha técnica; azul para
   informação. */
.hub-problem-title { display: flex; align-items: center; gap: 12px; min-width: 0; }
.hub-problem-title h4 { margin: 0; }
.hub-problem-icon { display: flex; flex: none; }
.hub-problem-icon .hub-icon { width: 24px; height: 24px; color: inherit; }
.hub-problem-warning { color: var(--hub-warning-icon); }
.hub-problem-failure { color: var(--hub-failure-icon); }
.hub-problem-info { color: var(--hub-info-icon); }
.hub-problem-body { gap: var(--hub-sm); }
.hub-problem-body p { margin: 0; }
.hub-problem-body ul { margin: 0; padding-left: 20px; list-style: disc; display: flex; flex-direction: column; gap: 4px; }

/* Ícones */
.hub-icon { display: block; flex: none; width: var(--hub-icon-size); height: var(--hub-icon-size); color: var(--hub-icon); }
/* Exceções: dentro de botão laranja, o ícone acompanha o texto do botão; em toast e alerta, a cor
   da mensagem (verde, vermelho...), porque laranja sobre esses fundos briga com o aviso. */
.hub-button-primary .hub-icon, .hub-button-danger .hub-icon, hub-toast .hub-icon, hub-alert .hub-icon { color: inherit; }

/* Avatar e menu do usuário */
.hub-avatar {
  position: relative; display: flex; align-items: center; justify-content: center; overflow: hidden;
  width: var(--hub-avatar-size); height: var(--hub-avatar-size); border-radius: 50%;
  background: var(--hub-orange); color: var(--hub-button-primary-text);
  font: 700 16px/1 var(--hub-font-heading);
}
.hub-avatar img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: cover; }
/* Avatar de uma pessoa numa lista: menor e cinza; o laranja é só de quem está logado. */
.hub-avatar-person {
  flex: none; width: 36px; height: 36px;
  background: var(--hub-surface-muted); color: var(--hub-text-secondary); font-size: 14px;
}
/* A pessoa numa célula: avatar e, ao lado, o nome e o e-mail. */
.hub-person { display: flex; align-items: center; gap: var(--hub-sm); }
.hub-person > div { display: flex; flex-direction: column; min-width: 0; overflow-wrap: anywhere; }
/* O nome e, logo depois da última palavra, o botão "Ver no CRM" (em linha, acompanha o nome que
   quebra); a margem negativa não deixa o botão aumentar a linha. */
.hub-person-crm { color: var(--hub-icon); vertical-align: middle; margin: -5px 0 -5px 2px; }
/* O padding leva a área de toque a 44px; a margem negativa mantém o tamanho no cabeçalho. */
.hub-avatar-button {
  display: block; padding: calc((var(--hub-touch) - var(--hub-avatar-size)) / 2);
  margin: calc((var(--hub-touch) - var(--hub-avatar-size)) / -2);
  background: none; border: 0; border-radius: 50%; cursor: pointer;
}
.hub-avatar-button:focus-visible { outline: 2px solid var(--hub-orange); outline-offset: 0; }

/* Menu do usuário. No celular, sobe do rodapé sobre um fundo escurecido (o ::before do hub-menu,
   que fecha o menu ao ser tocado); no tablet e no desktop, abre embaixo do avatar. */
hub-menu { position: relative; display: block; flex: none; }
hub-menu:has(> .hub-menu-panel:not([hidden]))::before {
  content: ""; position: fixed; inset: 0; z-index: 49; background: var(--hub-overlay);
}
.hub-menu-panel {
  position: fixed; inset: auto 0 0 0; z-index: 50;
  padding: var(--hub-sm) var(--hub-sm) calc(var(--hub-sm) + var(--hub-safe-bottom));
  background: var(--hub-surface); border: 1px solid var(--hub-border); border-width: 1px 0 0;
  border-radius: var(--hub-radius-sheet) var(--hub-radius-sheet) 0 0; box-shadow: var(--hub-shadow-floating);
  overscroll-behavior: contain;
}
.hub-menu-user {
  display: flex; flex-direction: column; gap: 2px; padding: var(--hub-sm); margin-bottom: var(--hub-xs);
  border-bottom: 1px solid var(--hub-border-subtle); font-size: 14px; line-height: 20px; overflow-wrap: anywhere;
}
.hub-menu-user small { color: var(--hub-text-secondary); font-size: 12px; line-height: 16px; }
/* Título do menu do app: só no celular, onde o menu vira painel e perde o botão de vista */
.hub-menu-title {
  padding: var(--hub-sm); margin-bottom: var(--hub-xs); border-bottom: 1px solid var(--hub-border-subtle);
  font: 600 14px/20px var(--hub-font-heading); color: var(--hub-text-secondary);
}
.hub-menu-item {
  display: flex; align-items: center; gap: var(--hub-md); width: 100%; min-height: 48px; padding: var(--hub-menu-item-padding);
  background: none; border: 0; border-radius: var(--hub-radius-small);
  color: var(--hub-text); font: 500 16px/24px var(--hub-font-body); text-align: left; cursor: pointer;
  transition: background-color var(--hub-time-hover) ease-out;
}
.hub-menu-item:active, .hub-menu-item:focus-visible { background: var(--hub-surface-muted); outline: none; }

/* Tooltip (tooltip.js): data-tooltip="Texto" num botão ou link, com aria-label igual. Um balão só,
   na camada de cima (popover), posicionado pelo JS: sempre em cima do elemento e, só sem espaço,
   embaixo (data-below). Entra com fade e um deslize leve vindo do lado em que abriu. A seta é um
   quadradinho girado, na cor do balão, apontando para o centro do elemento (--hub-tooltip-arrow,
   que o JS ajusta quando o balão desliza perto de uma borda). */
.hub-tooltip {
  position: fixed; inset: auto; margin: 0; z-index: 60; pointer-events: none; overflow: visible;
  max-width: min(320px, calc(100vw - 16px)); white-space: nowrap;
  padding: var(--hub-tooltip-padding); border: 0; border-radius: var(--hub-radius-small);
  background: var(--hub-tooltip-bg); color: var(--hub-tooltip-text); box-shadow: var(--hub-shadow-floating);
  font: 500 12px/16px var(--hub-font-body);
  opacity: 0; transform: translateY(4px);
  transition: opacity var(--hub-time-enter) var(--hub-ease-enter), transform var(--hub-time-enter) var(--hub-ease-enter);
}
.hub-tooltip[data-below] { transform: translateY(-4px); }
.hub-tooltip > span { display: block; overflow: hidden; text-overflow: ellipsis; }
.hub-tooltip::after {
  content: ""; position: absolute; bottom: -4px; left: var(--hub-tooltip-arrow, 50%); width: 10px; height: 10px;
  background: inherit; border-radius: 2px; transform: translateX(-50%) rotate(45deg);
}
.hub-tooltip[data-below]::after { top: -4px; bottom: auto; }
.hub-tooltip[data-shown] { opacity: 1; transform: none; }

/* Conteúdo carregado num aside ou dialog: o HTMX o põe num invólucro só com id ("...-body"), que
   empilha os filhos com o mesmo espaço do próprio corpo (texto, lista, botões). Invólucros com
   classe têm regra própria. */
:is(.hub-aside-body, .hub-dialog-body) > div:not([class]) { display: flex; flex-direction: column; gap: var(--hub-space-stack); }

/* Janela ou aside com lista de cards (ul.hub-list.hub-card-list): o corpo que rola fica com o
   cinza da página e os cards brancos, como as listas das telas. Um bloco fixo em cima (como o do
   proprietário) fica branco. Formulários e "Minha conta" seguem com o corpo branco. */
.hub-dialog-body:has(.hub-card-list), .hub-aside-body:has(.hub-card-list) { background: var(--hub-background); }

/* Widget: aside. Ocupa a tela toda no celular e respeita os recortes da tela. */
hub-aside { display: contents; }
hub-aside:not(:defined) { display: none; }
dialog.hub-aside {
  position: fixed; inset: 0 0 0 auto; width: min(var(--hub-aside-width), 100vw); max-width: none;
  height: 100dvh; max-height: none; margin: 0; padding: var(--hub-safe-top) 0 0; border: 0;
  background: var(--hub-surface); box-shadow: var(--hub-shadow-floating); color: var(--hub-text);
}
dialog.hub-aside[open] { display: flex; flex-direction: column; }
dialog.hub-aside::backdrop { background: var(--hub-overlay); }
/* Segunda camada (dialog.js): o fundo de baixo já escureceu a página. */
dialog[data-stacked]::backdrop { background: transparent; }
.hub-aside-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--hub-md);
  padding: var(--hub-space-bar) max(var(--hub-space-inside), var(--hub-safe-right)) var(--hub-space-bar) var(--hub-space-inside);
  border-bottom: 1px solid var(--hub-border);
}
/* Os filhos do corpo ficam empilhados com 16px entre eles, como num formulário: um texto não cola
   no campo de baixo. */
.hub-aside-body {
  display: flex; flex-direction: column; gap: var(--hub-space-stack);
  flex: 1; overflow-y: auto; overscroll-behavior: contain;
  padding: var(--hub-space-inside) max(var(--hub-space-inside), var(--hub-safe-right)) calc(var(--hub-space-inside) + var(--hub-safe-bottom)) var(--hub-space-inside);
}
/* Com rodapé, o recorte de baixo da tela fica com o rodapé, não com o corpo. */
.hub-aside-body:not(:last-child) { padding-bottom: var(--hub-space-inside); }
/* Rodapé fixo, com os botões: enxuto, para sobrar altura ao corpo com o teclado aberto. Os botões
   dividem a largura por igual, em qualquer tela (1 ocupa tudo, 2 ficam com metade cada...), com
   espaço entre eles. */
.hub-aside-footer {
  display: flex; gap: var(--hub-sm);
  padding: var(--hub-sm) max(var(--hub-space-inside), var(--hub-safe-right)) calc(var(--hub-sm) + var(--hub-safe-bottom)) var(--hub-space-inside);
  border-top: 1px solid var(--hub-border); background: var(--hub-surface);
}
.hub-aside-footer > .hub-button { flex: 1 1 0; min-width: 0; }

/* Tablet e desktop */
/* Desktop: a lista de conversas é a coluna da esquerda, presa embaixo da barra do Hub, com a altura
   da tela; a conversa ocupa o resto e continua rolando com a página. */
@media (min-width: 768px) {
  .hub-chat-layout { display: grid; grid-template-columns: clamp(300px, 28vw, 380px) minmax(0, 1fr); }
  .hub-chat-list {
    display: flex; position: sticky; align-self: start;
    top: calc(var(--hub-header-height) + var(--hub-safe-top));
    height: calc(100dvh - var(--hub-header-height) - var(--hub-safe-top));
    border-right: 1px solid var(--hub-border);
  }
  .hub-chat-list-open, .hub-chat-list-close { display: none; }
  .hub-chat-list-top { gap: var(--hub-space-field); padding: var(--hub-sm) var(--hub-space-bar) var(--hub-space-bar); }
  .hub-chat-contacts { padding: var(--hub-sm) var(--hub-space-bar); }
  .hub-chat-contact { padding: 14px; gap: 6px; }
}

@media (min-width: 768px) {
  /* Do tablet para cima, o logo completo também dentro dos apps. */
  .hub-header-app .hub-logo-icon.hub-logo-light, .hub-header-app .hub-logo-icon.hub-logo-dark { display: none; }
  .hub-header-app .hub-logo-light:not(.hub-logo-icon) { display: var(--hub-logo-light); }
  .hub-header-app .hub-logo-dark:not(.hub-logo-icon) { display: var(--hub-logo-dark); }

  /* Select: o painel fica colado ao campo (posição e altura vêm do select.js), sem escurecer a
     página, e as opções ficam no tamanho da tabela. */
  dialog.hub-select-panel[data-placed] {
    right: auto; max-width: none; border-width: 1px; border-radius: var(--hub-radius-card);
  }
  dialog.hub-select-panel::backdrop, dialog.hub-select-panel[data-stacked]::backdrop { background: transparent; }
  .hub-select-header { display: none; }
  .hub-select-search { margin: var(--hub-xs) var(--hub-xs) 0; padding: 0 var(--hub-sm); }
  .hub-select-search input { min-height: 40px; font-size: 14px; }
  .hub-select-list, dialog.hub-select-panel[data-multiple] .hub-select-list { padding: var(--hub-xs); }
  .hub-select-option { gap: var(--hub-sm); min-height: 40px; padding: 8px 12px; font-size: 14px; line-height: 20px; }
  .hub-select-group-title { padding: var(--hub-sm) 12px var(--hub-xs); font-size: 11px; }
  .hub-select-option.hub-select-active { background: var(--hub-surface-muted); }
  .hub-select-active .hub-select-hint { background: var(--hub-surface); }
  /* No desktop, os botões do rodapé ficam à direita, no tamanho do texto. */
  .hub-select-footer { justify-content: flex-end; padding: var(--hub-sm); }
  .hub-select-footer > .hub-button { flex: none; }
  /* Data: o painel tem a largura do conteúdo (um ou dois meses, a hora, os atalhos). */
  dialog.hub-date-panel[data-placed] { width: max-content; max-width: calc(100vw - 16px); }
  .hub-date-body { padding: var(--hub-md); }
  dialog.hub-date-panel:is([data-mode="range"], [data-mode="datetime"]) .hub-date-body { flex-direction: row; align-items: flex-start; }
  .hub-date-month { width: 296px; flex: none; }
  .hub-date-day { max-width: 40px; height: 40px; font-size: 14px; }
  .hub-date-band:first-child:not(.hub-date-band-start), .hub-date-band:last-child:not(.hub-date-band-end) { border-radius: 0; }
  .hub-date-presets {
    flex-direction: column; gap: 2px; width: 160px; padding-right: var(--hub-md); margin-right: var(--hub-xs);
    border-right: 1px solid var(--hub-border-subtle); overflow: visible;
  }
  .hub-date-preset { padding: 8px 12px; background: none; border: 0; border-radius: var(--hub-radius-small); text-align: left; }
  dialog[data-mode="datetime"] .hub-date-clock { align-self: stretch; padding-left: var(--hub-md); border-left: 1px solid var(--hub-border-subtle); }
  dialog[data-mode="datetime"] .hub-date-column { height: 300px; }
  .hub-date-summary { flex: 1 1 auto; margin-right: auto; }

  /* Do tablet para cima, tudo numa linha; o "Limpar campos" é o último item, quadrado, da altura
     dos campos, só com o ícone (o nome vai no tooltip e no aria-label). */
  .hub-search-bar { grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr) auto; }
  .hub-search-bar .hub-search-fields > .hub-field:first-child { grid-column: auto; }
  .hub-search-bar > .hub-search-clear { grid-column: auto; width: var(--hub-touch); height: var(--hub-touch); min-height: var(--hub-touch); padding: 0; justify-content: center; }
  .hub-search-bar > .hub-search-clear .hub-icon { width: 20px; height: 20px; }
  .hub-search-clear .hub-search-clear-label { display: none; }
  .hub-only-mobile { display: none; }
  .hub-cell { align-items: flex-start; text-align: left; }
  .hub-cell .hub-dispatch-reply { max-width: 18ch; }
  .hub-cell-date { flex-direction: column; gap: var(--hub-xs); }
  .hub-table .hub-table-actions { display: table-cell; width: 1%; text-align: right; }
  /* No grid, as colunas largas ganham um mínimo e o botão da linha fica só com o ícone (o nome
     vai no tooltip). */
  .hub-table-owner { min-width: 150px; }
  .hub-table-property { min-width: 200px; }
  .hub-table-open, .hub-table-more { width: 40px; height: 40px; min-height: 40px; padding: 0; justify-content: center; }
  .hub-table-open .hub-table-open-label { display: none; }
  .hub-pagination-page { min-width: 36px; height: 36px; }
  .hub-pagination-gap { height: 36px; }
  a.hub-pagination-page:hover { background: var(--hub-surface-hover); border-color: var(--hub-text-tertiary); }
  .hub-sort:hover { color: var(--hub-text); }

  .hub-table-scroll { overflow-x: auto; background: var(--hub-table-background); border: 1px solid var(--hub-border); border-radius: var(--hub-radius-card); }
  .hub-table { display: table; }
  .hub-table thead { position: static; width: auto; height: auto; overflow: visible; clip-path: none; display: table-header-group; }
  .hub-table tbody { display: table-row-group; }
  .hub-table tr { display: table-row; background: none; border: 0; border-radius: 0; box-shadow: none; }
  .hub-table th, .hub-table td { display: table-cell; padding: 12px var(--hub-md); text-align: left; border-bottom: 1px solid var(--hub-border-subtle); }
  .hub-table th { font-weight: 500; font-size: 12px; line-height: 16px; color: var(--hub-text-secondary); background: var(--hub-surface-muted); white-space: nowrap; }
  .hub-table td[data-label] { display: table-cell; }
  .hub-table td[data-label]::before { content: none; }
  .hub-table th.hub-table-number, .hub-table td.hub-table-number {
    position: static; width: 1%; padding: 12px 0 12px var(--hub-md); text-align: right; white-space: nowrap;
  }
  .hub-table td.hub-table-number { border-bottom: 1px solid var(--hub-border-subtle); font-weight: 500; font-size: 13px; line-height: 20px; }
  .hub-table tr:last-child td.hub-table-number { border-bottom: 0; }
  .hub-table td.hub-table-number:not(:empty)::before { content: none; }
  .hub-table tr:has(> td.hub-table-number) > td:nth-child(2) { padding-right: var(--hub-md); }
  .hub-table tr:last-child td { border-bottom: 0; }
  .hub-table td:last-child { border-bottom: 1px solid var(--hub-border-subtle); }
  .hub-table tr:last-child td:last-child { border-bottom: 0; }
  .hub-table input[type="checkbox"] { width: 20px; height: 20px; }
  .hub-table-check label { display: inline-flex; min-height: 0; padding: 12px var(--hub-md); }
  .hub-table-check label > span { display: none; }
  .hub-dispatch-facts { grid-template-columns: 1fr 1fr; column-gap: var(--hub-lg); }
  .hub-list-more { align-items: center; }

  hub-toasts {
    inset: auto var(--hub-gutter) calc(var(--hub-gutter) + var(--hub-safe-bottom)) auto;
    width: min(var(--hub-toast-width), calc(100vw - 2 * var(--hub-md)));
  }

  dialog.hub-dialog {
    inset: 0; width: min(var(--hub-dialog-width), calc(100vw - 2 * var(--hub-md)));
    max-height: calc(100dvh - 2 * var(--hub-md)); margin: auto;
    border-width: 1px; border-radius: var(--hub-radius-card);
  }
  .hub-dialog-body:last-child { padding-bottom: var(--hub-space-inside); }
  .hub-dialog-footer { padding: var(--hub-space-bar) var(--hub-space-inside); }
  /* No desktop, o "Entendi" do popup de alerta fica à direita, no tamanho do texto. */
  .hub-problem-footer { justify-content: flex-end; }
  .hub-problem-footer > .hub-button { flex: none; min-width: 120px; }

  hub-menu:has(> .hub-menu-panel:not([hidden]))::before { content: none; }
  .hub-menu-panel {
    position: absolute; inset: calc(100% + var(--hub-sm)) 0 auto auto;
    width: var(--hub-menu-width); padding: var(--hub-xs);
    border-width: 1px; border-radius: var(--hub-radius-card);
  }
  .hub-menu-item { gap: var(--hub-sm); min-height: var(--hub-touch); font-size: 14px; line-height: 20px; }
  .hub-menu-title { display: none; }
  /* No rodapé do chat, as mensagens prontas abrem para cima, sobre a conversa. */
  .hub-chat-quick .hub-menu-panel { inset: auto auto calc(100% + var(--hub-sm)) 0; width: min(400px, 80vw); max-height: 50vh; }
  /* Menu solto (data-float): fixo na tela, posição e altura vindas do anchor.js. */
  .hub-menu-panel[data-placed] { position: fixed; right: auto; overflow-y: auto; }
  /* Menus de ações de um envio: na largura do conteúdo, para os nomes não quebrarem. */
  .hub-menu-panel[data-placed], .hub-chat-actions .hub-menu-panel { width: max-content; min-width: var(--hub-menu-width); max-width: 320px; }
  .hub-menu-panel[data-placed][data-up] { transform-origin: bottom right; }
}

/* Efeitos de mouse, só em aparelhos com mouse */
@media (hover: hover) {
  .hub-button-primary:hover:not(:disabled) { background: var(--hub-orange-hover); }
  .hub-button-secondary:hover { background: var(--hub-surface-hover); }
  .hub-button-danger:hover:not(:disabled) { background: var(--hub-button-danger-hover); }
  a.hub-card:hover { box-shadow: var(--hub-shadow-hover); }
  .hub-menu-item:hover:not(:disabled) { background: var(--hub-surface-muted); }
  .hub-select-button:hover:not(:disabled):not([aria-expanded="true"]) { border-color: var(--hub-text-tertiary); }
  .hub-date-nav:hover, .hub-date-title:not(:disabled):hover, .hub-date-preset:hover { background: var(--hub-surface-muted); }
  .hub-date-day:hover:not(:disabled):not([aria-pressed="true"]) { background: var(--hub-surface-muted); }
  .hub-date-cell:hover:not([aria-current="true"]), .hub-date-time:hover:not([aria-selected="true"]) { background: var(--hub-surface-hover); }
  .hub-close:hover { background: var(--hub-surface-muted); }
  .hub-table-check label:hover { background: var(--hub-surface-muted); }
  .hub-chip:hover:not(:has(input:checked, input:disabled)) { background: var(--hub-surface-muted); }
  .hub-search-button:hover { color: var(--hub-text); }
  .hub-icon-button:hover { background: var(--hub-surface-muted); }
  .hub-owner-phone:hover span { text-decoration-color: currentColor; }
  .hub-tag-button:hover { box-shadow: inset 0 0 0 1px currentColor; }
  .hub-chat-contact:hover:not([aria-current="page"]) { background: var(--hub-surface-muted); }
}

/* Movimento: entrada suave de tudo que abre e saída suave de tudo que fecha.
   A entrada é só CSS. A saída usa a classe hub-leaving, posta pelo motion.js, que espera a animação
   terminar para esconder o elemento. Com prefers-reduced-motion (base.css), nada anima.
   As regras de saída ficam depois das de entrada, para valerem por cima delas. */
dialog.hub-dialog[open], dialog.hub-select-panel[open], .hub-menu-panel:not([hidden]) { animation: hub-sheet-in var(--hub-time-sheet) var(--hub-ease-enter); }
dialog.hub-aside[open] { animation: hub-slide-in var(--hub-time-aside) var(--hub-ease-enter); }
dialog[open]::backdrop, hub-menu:has(> .hub-menu-panel:not([hidden]))::before { animation: hub-fade-in var(--hub-time-enter) var(--hub-ease-enter); }
hub-toast { animation: hub-pop-in var(--hub-time-enter) var(--hub-ease-enter); }
hub-alert { animation: hub-drop-in var(--hub-time-enter) var(--hub-ease-enter); }

dialog.hub-dialog.hub-leaving, dialog.hub-select-panel.hub-leaving, .hub-menu-panel.hub-leaving { animation: hub-sheet-out var(--hub-time-sheet-leave) var(--hub-ease-leave) forwards; }
hub-toast.hub-leaving { animation: hub-pop-out var(--hub-time-leave) var(--hub-ease-leave) forwards; }
dialog.hub-aside.hub-leaving { animation: hub-slide-out var(--hub-time-aside-leave) var(--hub-ease-leave) forwards; }
dialog.hub-leaving::backdrop, hub-menu:has(> .hub-menu-panel.hub-leaving)::before { animation: hub-fade-out var(--hub-time-leave) var(--hub-ease-leave) forwards; }
hub-alert.hub-leaving { animation: hub-drop-out var(--hub-time-leave) var(--hub-ease-leave) forwards; }

@media (min-width: 768px) {
  dialog.hub-dialog[open] { animation: hub-pop-in var(--hub-time-enter) var(--hub-ease-enter); }
  .hub-menu-panel:not([hidden]) { transform-origin: top right; animation: hub-drop-in var(--hub-time-enter) var(--hub-ease-enter); }
  dialog.hub-dialog.hub-leaving { animation: hub-pop-out var(--hub-time-leave) var(--hub-ease-leave) forwards; }
  .hub-menu-panel.hub-leaving { animation: hub-drop-out var(--hub-time-leave) var(--hub-ease-leave) forwards; }
  /* O select desce do campo; aberto para cima, sobe dele. */
  dialog.hub-select-panel[data-placed][open] { animation: hub-drop-in var(--hub-time-enter) var(--hub-ease-enter); }
  dialog.hub-select-panel[data-placed][data-up][open] { animation: hub-pop-in var(--hub-time-enter) var(--hub-ease-enter); }
  dialog.hub-select-panel[data-placed].hub-leaving { animation: hub-drop-out var(--hub-time-leave) var(--hub-ease-leave) forwards; }
  dialog.hub-select-panel[data-placed][data-up].hub-leaving { animation: hub-pop-out var(--hub-time-leave) var(--hub-ease-leave) forwards; }
}

@keyframes hub-fade-in { from { opacity: 0; } }
@keyframes hub-fade-out { to { opacity: 0; } }
@keyframes hub-pop-in { from { opacity: 0; transform: translateY(var(--hub-motion-distance)) scale(0.98); } }
@keyframes hub-pop-out { to { opacity: 0; transform: translateY(var(--hub-motion-distance)) scale(0.98); } }
@keyframes hub-drop-in { from { opacity: 0; transform: translateY(calc(-1 * var(--hub-motion-distance))) scale(0.98); } }
@keyframes hub-drop-out { to { opacity: 0; transform: translateY(calc(-1 * var(--hub-motion-distance))) scale(0.98); } }
@keyframes hub-slide-in { from { transform: translateX(100%); } }
@keyframes hub-slide-out { to { transform: translateX(100%); } }
@keyframes hub-slide-in-left { from { transform: translateX(-100%); } }
@keyframes hub-slide-out-left { to { transform: translateX(-100%); } }
@keyframes hub-sheet-in { from { transform: translateY(100%); } }
@keyframes hub-sheet-out { to { transform: translateY(100%); } }

/* Troca de tema: as cores mudam suaves em vez de piscar. A classe fica no <html> só durante a
   troca (theme.js). */
@media (prefers-reduced-motion: no-preference) {
  .hub-theme-changing, .hub-theme-changing *, .hub-theme-changing *::before, .hub-theme-changing *::after {
    transition: background-color var(--hub-time-focus) ease, color var(--hub-time-focus) ease,
      border-color var(--hub-time-focus) ease, box-shadow var(--hub-time-focus) ease,
      transform var(--hub-time-enter) var(--hub-ease-enter) !important;
  }
}

