html, body {
    font-family: var(--font-body);
}

h1:focus {
    outline: none;
}

a, .btn-link {
    color: #0071c1;
}

.btn-primary {
    color: #fff;
    background-color: #1b6ec2;
    border-color: #1861ac;
}

.btn:focus, .btn:active:focus, .btn-link.nav-link:focus, .form-control:focus, .form-check-input:focus {
  box-shadow: 0 0 0 0.1rem white, 0 0 0 0.25rem #258cfb;
}

.content {
    padding-top: 1.1rem;
}

.valid.modified:not([type=checkbox]) {
    outline: 1px solid #26b050;
}

.invalid {
    outline: 1px solid red;
}

.validation-message {
    color: red;
}

#blazor-error-ui {
    color-scheme: light only;
    background: lightyellow;
    bottom: 0;
    box-shadow: 0 -1px 2px rgba(0, 0, 0, 0.2);
    box-sizing: border-box;
    display: none;
    left: 0;
    padding: 0.6rem 1.25rem 0.7rem 1.25rem;
    position: fixed;
    width: 100%;
    z-index: 1000;
}

    #blazor-error-ui .dismiss {
        cursor: pointer;
        position: absolute;
        right: 0.75rem;
        top: 0.5rem;
    }

.blazor-error-boundary {
    background: url(data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iNTYiIGhlaWdodD0iNDkiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiIG92ZXJmbG93PSJoaWRkZW4iPjxkZWZzPjxjbGlwUGF0aCBpZD0iY2xpcDAiPjxyZWN0IHg9IjIzNSIgeT0iNTEiIHdpZHRoPSI1NiIgaGVpZ2h0PSI0OSIvPjwvY2xpcFBhdGg+PC9kZWZzPjxnIGNsaXAtcGF0aD0idXJsKCNjbGlwMCkiIHRyYW5zZm9ybT0idHJhbnNsYXRlKC0yMzUgLTUxKSI+PHBhdGggZD0iTTI2My41MDYgNTFDMjY0LjcxNyA1MSAyNjUuODEzIDUxLjQ4MzcgMjY2LjYwNiA1Mi4yNjU4TDI2Ny4wNTIgNTIuNzk4NyAyNjcuNTM5IDUzLjYyODMgMjkwLjE4NSA5Mi4xODMxIDI5MC41NDUgOTIuNzk1IDI5MC42NTYgOTIuOTk2QzI5MC44NzcgOTMuNTEzIDI5MSA5NC4wODE1IDI5MSA5NC42NzgyIDI5MSA5Ny4wNjUxIDI4OS4wMzggOTkgMjg2LjYxNyA5OUwyNDAuMzgzIDk5QzIzNy45NjMgOTkgMjM2IDk3LjA2NTEgMjM2IDk0LjY3ODIgMjM2IDk0LjM3OTkgMjM2LjAzMSA5NC4wODg2IDIzNi4wODkgOTMuODA3MkwyMzYuMzM4IDkzLjAxNjIgMjM2Ljg1OCA5Mi4xMzE0IDI1OS40NzMgNTMuNjI5NCAyNTkuOTYxIDUyLjc5ODUgMjYwLjQwNyA1Mi4yNjU4QzI2MS4yIDUxLjQ4MzcgMjYyLjI5NiA1MSAyNjMuNTA2IDUxWk0yNjMuNTg2IDY2LjAxODNDMjYwLjczNyA2Ni4wMTgzIDI1OS4zMTMgNjcuMTI0NSAyNTkuMzEzIDY5LjMzNyAyNTkuMzEzIDY5LjYxMDIgMjU5LjMzMiA2OS44NjA4IDI1OS4zNzEgNzAuMDg4N0wyNjEuNzk1IDg0LjAxNjEgMjY1LjM4IDg0LjAxNjEgMjY3LjgyMSA2OS43NDc1QzI2Ny44NiA2OS43MzA5IDI2Ny44NzkgNjkuNTg3NyAyNjcuODc5IDY5LjMxNzkgMjY3Ljg3OSA2Ny4xMTgyIDI2Ni40NDggNjYuMDE4MyAyNjMuNTg2IDY2LjAxODNaTTI2My41NzYgODYuMDU0N0MyNjEuMDQ5IDg2LjA1NDcgMjU5Ljc4NiA4Ny4zMDA1IDI1OS43ODYgODkuNzkyMSAyNTkuNzg2IDkyLjI4MzcgMjYxLjA0OSA5My41Mjk1IDI2My41NzYgOTMuNTI5NSAyNjYuMTE2IDkzLjUyOTUgMjY3LjM4NyA5Mi4yODM3IDI2Ny4zODcgODkuNzkyMSAyNjcuMzg3IDg3LjMwMDUgMjY2LjExNiA4Ni4wNTQ3IDI2My41NzYgODYuMDU0N1oiIGZpbGw9IiNGRkU1MDAiIGZpbGwtcnVsZT0iZXZlbm9kZCIvPjwvZz48L3N2Zz4=) no-repeat 1rem/1.8rem, #b32121;
    padding: 1rem 1rem 1rem 3.7rem;
    color: white;
}

    .blazor-error-boundary::after {
        content: "An error has occurred."
    }

.loading-progress {
    position: absolute;
    display: block;
    width: 8rem;
    height: 8rem;
    inset: 20vh 0 auto 0;
    margin: 0 auto 0 auto;
}

    .loading-progress circle {
        fill: none;
        stroke: #e0e0e0;
        stroke-width: 0.6rem;
        transform-origin: 50% 50%;
        transform: rotate(-90deg);
    }

        .loading-progress circle:last-child {
            stroke: #1b6ec2;
            stroke-dasharray: calc(3.141 * var(--blazor-load-percentage, 0%) * 0.8), 500%;
            transition: stroke-dasharray 0.05s ease-in-out;
        }

.loading-progress-text {
    position: absolute;
    text-align: center;
    font-weight: bold;
    inset: calc(20vh + 3.25rem) 0 auto 0.2rem;
}

    .loading-progress-text:after {
        content: var(--blazor-load-percentage-text, "Loading");
    }

code {
    color: #c02d76;
}

.form-floating > .form-control-plaintext::placeholder, .form-floating > .form-control::placeholder {
    color: var(--bs-secondary-color);
    text-align: end;
}

.form-floating > .form-control-plaintext:focus::placeholder, .form-floating > .form-control:focus::placeholder {
    text-align: start;
}

/* ===================== Camarada — shell mobile-first ===================== */
:root {
    --brand: #3E4C5C;
    --brand-dark: #29333F;
    --brand-tint: #DFE4E8;
    --ink: #1C1B19;
    --ink-soft: #47443F;
    --muted: #78746C;
    --line: #E7E2D9;
    --bg: #FAF7F2;
    --card: #FFFFFF;
    --ok: #146C43;
    --ok-tint: #E3F1E9;
    --danger: #BE123C;
    --danger-tint: #FCE4E9;
    --wait: #B8790A;
    --wait-tint: #FBF0DD;

    --font-display: 'Space Grotesk', 'Inter', sans-serif;
    --font-body: 'Inter', sans-serif;
}

/* Temas alternativos — escolhidos em Parâmetros, aplicados via [data-theme] no <html> (ver ThemeService).
   Chumbo azulado é o padrão (bare :root, sem atributo) desde que virou a cor padrão de novos usuários. */
:root[data-theme="terracota"] {
    --brand: #A8402E;
    --brand-dark: #7D2E20;
    --brand-tint: #F5DFDA;
}

:root[data-theme="azul-marinho"] {
    --brand: #1E3A5F;
    --brand-dark: #132840;
    --brand-tint: #DCE4F0;
}

html, body {
    margin: 0;
    background: var(--bg);
    color: var(--ink);
    font-variant-numeric: tabular-nums;
}

.app-main h1,
.page-title-strong,
.section-header h1.title-strong,
.attention-count,
.itens-total .value {
    font-family: var(--font-display);
}

.app-shell {
    min-height: 100vh;
    display: flex;
    flex-direction: column;
}

.app-header {
    position: sticky;
    top: 0;
    z-index: 10;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .75rem;
    padding: .75rem 1rem;
    padding-top: max(.75rem, env(safe-area-inset-top));
    background: var(--brand);
    color: #fff;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .15);
}

/* Faixa de "modo demonstração" — colada sob o cabeçalho e grudada com ele na rolagem, senão o aviso
   sai de vista na primeira lista longa, que é justamente onde o visitante esquece onde está.
   Amarelo de aviso (--wait), não vermelho: nada está errado, só é temporário. */
.aviso-demo {
    position: sticky;
    /* Sem valor fixo: o cabeçalho já é sticky no topo, então esta faixa encosta logo abaixo dele. */
    top: 0;
    z-index: 9;
    display: flex;
    align-items: center;
    gap: .5rem;
    padding: .5rem 1rem;
    background: var(--wait-tint);
    color: var(--ink);
    border-bottom: 1px solid var(--line);
    font-size: .85rem;
    line-height: 1.35;
}

.aviso-demo .icon { width: 1.05rem; height: 1.05rem; flex: 0 0 auto; color: var(--wait); }
.aviso-demo span { flex: 1 1 auto; }

.aviso-demo-cta {
    flex: 0 0 auto;
    background: none;
    border: 0;
    padding: .35rem .5rem;
    font-family: inherit;
    font-size: .85rem;
    font-weight: 700;
    color: var(--brand);
    text-decoration: underline;
    text-underline-offset: 3px;
    cursor: pointer;
}

.app-header .brand {
    font-family: var(--font-display);
    font-weight: 700;
    font-size: 1.15rem;
    letter-spacing: .2px;
    color: inherit;
    text-decoration: none;
}

.header-right {
    display: flex;
    align-items: center;
    gap: .75rem;
}

.app-header .icon-btn { color: #fff; }
.app-header .icon-btn:active { background: rgba(255, 255, 255, .28); }

.app-main {
    flex: 1;
    width: 100%;
    max-width: 720px;
    margin: 0 auto;
    padding: 1.1rem 1rem 2rem;
    box-sizing: border-box;
}

/* Connection status pill */
.conn-status {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    font-size: .82rem;
    background: rgba(255, 255, 255, .15);
    border-radius: 999px;
    padding: .25rem .6rem;
}

.conn-status .dot {
    width: .55rem;
    height: .55rem;
    border-radius: 50%;
    background: #86efac;
    box-shadow: 0 0 0 2px rgba(255, 255, 255, .25);
}

.conn-status.is-offline .dot { background: #fca5a5; }

.conn-status .pending {
    background: var(--wait);
    color: #fff;
    border-radius: 999px;
    padding: 0 .45rem;
    font-weight: 600;
}

/* Link "N erros" no widget de status — antes caía no azul default de <a>, invisível como alerta
   sobre o header escuro. Mesma pílula do .pending, em tom de perigo. */
.conn-status .sync-errors {
    background: var(--danger);
    color: #fff;
    border-radius: 999px;
    padding: 0 .45rem;
    font-weight: 600;
    text-decoration: none;
}

/* Text helpers */
.muted { color: var(--muted); }
.hint { color: var(--muted); font-size: .85rem; }
.error { color: var(--danger); font-weight: 600; }

/* A crítica do formulário (<FeedbackErro>) recebe o foco quando aparece, para ser lida pelo leitor
   de tela e para dizer a quem enxerga onde a tela parou. Sem contorno o salto ficaria mudo: a página
   rolaria sozinha e nada indicaria por quê. Em :focus e não :focus-visible pelo mesmo motivo do
   .resultado-alert — foco movido por código, não pelo teclado. */
.error:focus { outline: 2px solid var(--danger); outline-offset: 3px; border-radius: .3rem; }

/* .login-card foi removido na S15: login e cadastro eram as duas únicas telas com cartão de largura
   fixa (360px) centralizado, e no navegador isso deixava o formulário espremido no meio da tela,
   já nascendo com barra de rolagem. As duas passaram ao padrão de página (<h1> + .dev-panel),
   herdando a largura de .app-main (720px, fluida abaixo disso) como todas as demais. */

.field { margin-bottom: 1rem; display: flex; flex-direction: column; }
.field label { font-size: .85rem; font-weight: 600; margin-bottom: .35rem; }

/* Campo de cabeçalho com ícone mnemônico à esquerda em vez de label acima (Orçamento: Cliente,
   Endereço, Equipamento, Válido até) — app de uso repetido em campo, o ícone é decorado uma vez só.
   O rótulo textual não some: vira aria-label no controle e title no ícone/wrapper pra leitor de
   tela e tooltip. */
.field-icon {
    display: flex;
    align-items: center;
    gap: .65rem;
    margin-bottom: .75rem;
}

.field-icon > .icon {
    flex-shrink: 0;
    width: 1.35rem;
    height: 1.35rem;
    color: var(--muted);
}

.field-icon > .input,
.field-icon > .picker-summary,
.field-icon > .btn-secondary {
    flex: 1;
    min-width: 0;
}

/* Duas .field-icon lado a lado (Orçamento: Data do orçamento + Válido até). Mesmo gotcha do
   .filtro-datas: input[type=date] nativo + ícone mnemônico apertam em telas de 360px — reduz
   padding/fonte do input e o tamanho do ícone pra caber os dois campos no painel. */
.field-icon-row { display: flex; gap: .5rem; margin-bottom: .75rem; }
.field-icon-row > .field-icon { flex: 1; min-width: 0; margin-bottom: 0; }
.field-icon-row > .field-icon > .icon { width: 1.1rem; height: 1.1rem; }
.field-icon-row .input { padding: .55rem .4rem; font-size: .85rem; }
.field-icon-row .input::-webkit-calendar-picker-indicator { padding: 0; margin-left: .2rem; }

.input {
    width: 100%;
    box-sizing: border-box;
    padding: .8rem .85rem;
    font-size: 1rem;
    border: 1px solid var(--line);
    border-radius: .6rem;
    background: #fff;
}

.input:focus { outline: 2px solid var(--brand); border-color: var(--brand); }

/* Textarea nativa não herda a fonte do documento (vira monoespaçada) e cresce nos dois eixos —
   estoura a largura do card. Só vertical, e na mesma fonte dos demais campos. */
textarea.input { font-family: inherit; resize: vertical; }

/* O seletor de arquivo tem largura intrínseca própria (botão + nome do arquivo, no shadow DOM) e
   ignora o container: o <InputFile> das fotos da OS estourava a viewport em ~11px, o bastante para
   ligar rolagem horizontal na tela inteira. Regra no seletor de tipo, não numa classe, porque vale
   para qualquer campo de arquivo que apareça depois. */
input[type="file"] { max-width: 100%; box-sizing: border-box; }

/* Select nativo do navegador é quadrado e destoa do resto dos campos — some com o widget padrão
   do SO e desenha uma seta consistente com os demais ícones do app. */
select.input {
    appearance: none;
    -webkit-appearance: none;
    -moz-appearance: none;
    padding-right: 2.6rem;
    background-color: #fff;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='24' height='24' viewBox='0 0 24 24' fill='none' stroke='%2378746c' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9 6 6 6-6'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right .75rem center;
    background-size: 1.15rem;
}

select.input::-ms-expand { display: none; }

select.input:disabled { opacity: .6; background-color: var(--bg); }

.btn-primary-lg {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: .85rem;
    font-size: 1.05rem;
    font-weight: 600;
    color: #fff;
    background: var(--brand);
    border: none;
    border-radius: .6rem;
    text-decoration: none;
    cursor: pointer;
}

.btn-primary-lg:disabled { opacity: .6; cursor: default; }
.btn-primary-lg:active:not(:disabled) { background: var(--brand-dark); }

/* Botão grande de ação negativa (recusar orçamento — link público, S5) — mesmo tamanho/alvo de
   toque do .btn-primary-lg, cor de perigo em vez da marca. */
.btn-danger-lg {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    padding: .85rem;
    font-size: 1.05rem;
    font-weight: 600;
    color: #fff;
    background: var(--danger);
    border: none;
    border-radius: .6rem;
    text-decoration: none;
    cursor: pointer;
    min-height: 44px;
    box-sizing: border-box;
}

.btn-danger-lg:disabled { opacity: .6; cursor: default; }
.btn-danger-lg:active:not(:disabled) { background: #b91c1c; }

.dev-hint {
    margin-top: 1rem;
    font-size: .8rem;
    color: var(--muted);
    text-align: center;
}

/* Dev panel on Home */
.dev-panel {
    margin-top: 1.5rem;
    background: #fff;
    border: 1px solid var(--line);
    border-radius: .8rem;
    padding: 1rem 1.1rem;
}

.dev-panel h2 { margin: 0 0 .5rem; font-size: 1.05rem; }

.fieldset-reset { border: 0; margin: 0; padding: 0; min-width: 0; }
.fieldset-reset:disabled { opacity: .65; }

.row-actions { display: flex; flex-wrap: wrap; gap: .5rem; margin: .5rem 0; }

.btn-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: .6rem .9rem;
    font-size: .95rem;
    color: var(--brand);
    background: #fff;
    border: 1px solid var(--brand);
    border-radius: .55rem;
    text-decoration: none;
    cursor: pointer;
}

.btn-secondary:active { background: var(--brand-tint); }

.btn-secondary,
.btn-primary-lg { min-height: 44px; box-sizing: border-box; }

.btn-block { width: 100%; }

/* Botão desabilitado que não é um <button>/<input> (ex.: <label> disfarçado de botão apontando pra
   um <input type=file> — esses não têm :disabled próprio), então o visual vem de uma classe. */
.is-disabled { opacity: .6; cursor: default; pointer-events: none; }

/* Input de arquivo nativo é feio e não dá pra restilizar direto (cada navegador desenha o próprio
   "Escolher arquivo"). Escondido do olho mas presente no layout — clip em vez de display:none — pra
   continuar focável/anunciado por teclado e leitor de tela; quem dispara o seletor é o <label for=...>
   estilizado como .btn-secondary ao lado, gesto padrão de HTML que não precisa de JS. */
.input-file-oculto {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* EntityPicker — preenche a altura do sheet-body; a lista rola no seu próprio contêiner pra o
   CTA "Cadastrar novo X" ficar sempre visível, sem depender de scroll até o fim. */
.entity-picker { display: flex; flex-direction: column; height: 100%; min-height: 0; }
.entity-picker .search-row { flex-shrink: 0; }
.entity-picker-list { flex: 1; overflow-y: auto; min-height: 0; }
.picker-create { flex-shrink: 0; margin-top: .75rem; }
.picker-create .icon { width: 1.1rem; height: 1.1rem; margin-right: .4rem; }

/* Empty-state de primeiro uso das listas — o texto ganha um CTA pro mesmo destino do botão
   primário da BottomBar (que fica longe do olho, fixo na base). Só no recorte "ativos":
   arquivados e busca sem resultado continuam texto puro. */
.empty-state {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .75rem;
    padding: 1rem 0;
    text-align: center;
}

/* Section header (title + inline toggle) */
.section-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: .5rem;
    flex-wrap: wrap;
    margin-bottom: .75rem;
}

.section-header h1 { margin: 0; font-size: 1.05rem; }

.section-header h1.title-strong {
    font-size: 1.25rem;
    font-weight: 800;
    color: var(--brand-dark);
    letter-spacing: -.01em;
}

.page-title-strong {
    margin: 0 0 .5rem;
    font-weight: 800;
    font-size: 1.2rem;
    line-height: 1;
    color: var(--brand-dark);
    letter-spacing: -.01em;
    text-align: center;
    text-transform: uppercase;
}

.dev-panel-tight { margin-top: 0; }

/* Painel com bastante informação por linha (ex.: Agenda) — menos respiro nas laterais que o padrão,
   pra sobrar espaço pro conteúdo do card. */
.dev-panel-compact-x { padding-left: .65rem; padding-right: .65rem; }

/* Cabeçalho de seção com um único item (sem h1/h2 ao lado) — encosta à direita em vez de colar à
   esquerda, que é o comportamento padrão de um único filho num flex space-between. */
.section-header-end { justify-content: flex-end; }

.toggle-inline {
    display: flex;
    align-items: center;
    gap: .45rem;
    font-size: .9rem;
    cursor: pointer;
}

.toggle-inline input[type="checkbox"] { width: 1.2rem; height: 1.2rem; }

/* Checkbox field (form) */
.field-checkbox {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin-bottom: 1rem;
}

.field-checkbox input[type="checkbox"] { width: 1.2rem; height: 1.2rem; }

/* Cliente — lista/cartão */
.cliente-list {
    list-style: none;
    margin: 0;
    padding: 0;
    display: flex;
    flex-direction: column;
    gap: .6rem;
}

.cliente-card {
    display: flex;
    flex-direction: column;
    gap: .6rem;
    padding: .85rem .9rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: #fff;
}

.cliente-card.is-inactive, .cliente-card.is-archived { opacity: .65; }

/* Destaque temporário ao voltar de outra tela posicionado neste card (ver scrollHelper.js) */
.cliente-card.is-focado { animation: cardFoco 2.2s ease-out; }
@keyframes cardFoco {
    0%, 55% { box-shadow: 0 0 0 3px var(--brand); }
    100% { box-shadow: 0 0 0 3px transparent; }
}
@media (prefers-reduced-motion: reduce) {
    .cliente-card.is-focado { animation: none; box-shadow: 0 0 0 3px var(--brand); }
}

.cliente-card-header {
    display: flex;
    justify-content: space-between;
    align-items: flex-start;
    gap: .5rem;
}

.cliente-info { display: flex; flex-direction: column; gap: .2rem; min-width: 0; flex: 1 1 auto; }
.cliente-info strong { font-size: 1rem; }

/* Coluna de horário à esquerda do card da Agenda — início sobre fim, empilhados (ver HorarioColuna) */
.agenda-card-horario {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    flex-shrink: 0;
    min-width: 2.8rem;
    line-height: 1.2;
}
.agenda-card-horario strong { font-size: .95rem; }
.agenda-card-horario-fim { font-size: .78rem; color: var(--muted); }

/* Nome do cliente no card da Agenda — maior que o padrão de .cliente-info strong (1rem): na lista
   do dia é por ele que se identifica a visita de relance, com o celular na mão e sol na tela.
   Classe própria em vez de mexer no global, que vale para Clientes/Orçamentos/OS/Recebimentos. */
.agenda-card-nome { font-size: 1.3rem; font-weight: 800; color: var(--ink); line-height: 1.2; }

/* Trunca com reticências em vez de quebrar linha — usado onde o título já é uma frase longa
   (hora + nome do cliente, Agenda) e o espaço lateral é disputado com badges. */
.truncate-line { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

.cliente-name-row { display: flex; align-items: center; gap: .4rem; flex-wrap: wrap; }

.cliente-info-line { display: flex; justify-content: space-between; align-items: baseline; gap: .5rem; }
.cliente-info-line-end { text-align: right; flex-shrink: 0; }

.cliente-card-actions { display: flex; gap: .25rem; flex-shrink: 0; align-self: flex-start; }

/* Puxa o botão de ações pra mais perto da borda direita do card (Agenda, Orçamentos, OS) — o alvo
   de toque de 2.75rem do icon-btn sobra bastante respiro em volta do ícone; a margem negativa
   compensa isso visualmente sem reduzir a área de toque. */
.cliente-card-actions.is-edge { margin-right: -.75rem; }

/* Badges de status alinhadas à direita do card, entre as infos e o botão de ações (Orçamentos) */
.cliente-card-badges { display: flex; flex-direction: column; align-items: flex-end; gap: .25rem; flex-shrink: 0; }

/* Variante do cliente-card-header (Orçamentos) que centraliza badges e botão de ações
   verticalmente entre si, em vez do padrão flex-start (que alinha tudo pelo topo). */
.cliente-card-header-vcenter { align-items: center; }
.cliente-card-header-vcenter .cliente-card-badges,
.cliente-card-header-vcenter .cliente-card-actions { align-self: center; }

/* Badge de situação — só "Inativo" é exibido; "Ativo" é o padrão implícito */
.badge {
    display: inline-block;
    white-space: nowrap;
    font-size: .75rem;
    font-weight: 600;
    padding: .2rem .6rem;
    border-radius: 999px;
}

.badge-inactive, .badge-archived { background: var(--line); color: var(--ink-soft); }
.badge-principal { background: var(--brand-tint); color: var(--brand-dark); }

/* Badge de status do Orçamento */
.badge-rascunho { background: var(--line); color: var(--ink-soft); }
.badge-enviado { background: var(--wait-tint); color: var(--wait); }
.badge-aprovado { background: var(--ok-tint); color: var(--ok); }
.badge-recusado { background: var(--danger-tint); color: var(--danger); }

/* Badge de validade vencida (S5) — some quando o orçamento é reaberto/editado, nunca é o único sinal (também no texto do aviso da página pública) */
.badge-vencido { background: var(--danger-tint); color: var(--danger); }

.badge-agendamento { background: var(--wait-tint); color: var(--wait); }
.badge-os { background: var(--ok-tint); color: var(--ok); }

/* OS ainda Aberta — trabalho pendente, então tom de espera (mesmo do badge-agendamento), não o
   verde do badge-os. Um "OS" único pros dois estados fazia o card parecer mais adiantado do que
   está: OS aberta é justo o que segura o Concluído do orçamento (ADR 0007). */
.badge-os-aberta { background: var(--wait-tint); color: var(--wait); }

/* Badge de orçamento ligado (Agenda) — só indica o vínculo; a navegação é botão no BottomSheet.
   Tom de marca pra distinguir do badge-agendamento (wait) e do badge-os (ok). */
.badge-orcamento { background: var(--brand-tint); color: var(--brand-dark); }

/* Concluído (ADR 0007) — desfecho de sucesso, derivado de agenda/OS. Preenchido (não tint) pra
   ler como "selado/encerrado", distinto do Aprovado (tint) que ainda está no fluxo ativo. */
.badge-concluido { background: var(--ok); color: #fff; }

/* Badge de item ainda não sincronizado com o servidor (S9 — criado/alterado offline, na fila) */
.badge-pendente { background: var(--wait-tint); color: var(--wait); }

/* "Visto" — o cliente abriu o link público. Sinal positivo (o link chegou e a proposta foi lida),
   então tom de ok em tint, no mesmo nível do badge-aprovado; nunca preenchido, que é do Concluído.
   O par "Não abriu" reusa o .badge-rascunho (cinza neutro) de propósito: não é erro nem alerta —
   vermelho ali gritaria em todo orçamento recém-enviado e ensinaria o prestador a ignorar o badge. */
.badge-visto { background: var(--ok-tint); color: var(--ok); }

/* Código de convite (/admin) — monoespaçado e espaçado porque ele é DITADO por WhatsApp e conferido
   caractere a caractere. Na fonte de texto do app, um código lido de relance é onde "0" vira "O";
   o alfabeto do gerador já evita os pares ambíguos, e a tipografia faz a outra metade do trabalho. */
.convite-codigo {
    font-family: ui-monospace, SFMono-Regular, "Cascadia Mono", Consolas, monospace;
    font-size: 1.05rem;
    letter-spacing: .08em;
}

/* Senha temporária recém-gerada (/admin, entrega 04). Bloco destacado e não mais uma linha da lista
   de ações: ela aparece UMA vez, e o que a distingue das outras linhas do sheet é justamente que
   perdê-la de vista custa outra rodada de reset. Reusa a tipografia do .convite-codigo — mesmo
   alfabeto, mesmo caminho (ditada/colada num WhatsApp). */
.senha-reset {
    display: flex;
    flex-direction: column;
    gap: .4rem;
    padding: .75rem;
    border: 1px solid var(--line);
    border-radius: .8rem;
    background: var(--bg);
}

/* Botão só-ícone das ações de card (Editar/Endereços/Equipamentos) */
.icon-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    height: 2.75rem;
    padding: 0;
    color: var(--brand);
    background: transparent;
    border: none;
    border-radius: .5rem;
    text-decoration: none;
    cursor: pointer;
}

.icon-btn .icon { width: 1.25rem; height: 1.25rem; }

.icon-btn:active { background: var(--brand-tint); }

/* Botão de busca por voz (protótipo) — pulsa em vermelho enquanto está ouvindo */
.icon-btn.is-listening { color: var(--danger); animation: pulse-listening 1.2s ease-in-out infinite; }

@keyframes pulse-listening {
    0%, 100% { opacity: 1; }
    50% { opacity: .45; }
}

/* Botão de atualizar lista (Orçamentos) — ícone gira enquanto a revalidação está em andamento */
.icon-btn.is-spinning .icon { animation: spin 0.9s linear infinite; }

@keyframes spin {
    from { transform: rotate(0deg); }
    to { transform: rotate(360deg); }
}

/* Campo de busca da lista */
.search-row { display: flex; align-items: center; gap: .5rem; margin-bottom: .75rem; }
.search-row .input { flex: 1; }

/* Abas Histórico/Hoje/Próximos (Agenda) — mesma barra em toda a largura, aba ativa marcada por
   sublinhado na cor da marca. Com 3 abas o texto lado a lado do ícone truncava em telas estreitas
   (360px) — ícone em cima + rótulo embaixo (mesma estratégia do BottomBar) cabe sem truncar; a
   contagem vira um badge sobreposto no canto do ícone em vez de ocupar espaço horizontal. */
.tabs {
    display: flex;
    border-bottom: 1px solid var(--line);
    margin-bottom: .85rem;
}

.tab {
    flex: 1;
    min-width: 0;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .2rem;
    padding: .5rem .25rem .55rem;
    font-size: .72rem;
    font-weight: 600;
    color: var(--muted);
    background: transparent;
    border: none;
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    cursor: pointer;
}

.tab.is-active { color: var(--brand-dark); border-bottom-color: var(--brand); }
.tab:active { background: var(--bg); }

.tab .icon { width: 1.35rem; height: 1.35rem; }

.tab-label { max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.tab-icon-wrap { position: relative; display: inline-flex; }

.tab-count {
    position: absolute;
    top: -.4rem;
    right: -.6rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.1rem;
    height: 1.1rem;
    padding: 0 .25rem;
    font-size: .62rem;
    font-weight: 700;
    border-radius: 999px;
    background: var(--line);
    color: var(--ink-soft);
    box-shadow: 0 0 0 2px #fff;
}

.tab.is-active .tab-count { background: var(--brand); color: #fff; }

/* Separador entre grupos de dia (Agenda, aba Próximos) — sem isso o título do dia seguinte fica
   colado no último card do dia anterior. O primeiro grupo não leva a régua/respiro de cima, já tem
   o filtro de data logo acima.

   PADRÃO DO APP: "1px dashed var(--line)" é a régua usada em toda separação entre blocos/itens
   empilhados dentro de um mesmo painel (aqui, em .resumo-bloco, no card de foto do Catálogo, na
   linha de status do Home). Um `border-top` sólido nesse contexto é inconsistência, não escolha de
   estilo — ao criar uma nova separação do tipo, reusar esta régua em vez de inventar uma variante. */
.dia-grupo { margin-top: 1.1rem; padding-top: .85rem; border-top: 1px dashed var(--line); }
.dia-grupo.is-first { margin-top: 0; padding-top: 0; border-top: none; }

/* Separador de pausa para almoço dentro de um mesmo dia (Agenda) — mesma régua pontilhada do
   .dia-grupo, mas centralizado e sem título de seção: marca o intervalo entre os agendamentos da
   manhã e os da tarde, não um novo dia. */
.agenda-almoco-sep {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: .4rem;
    list-style: none;
    padding-top: .85rem;
    padding-bottom: .85rem;
    border-top: 1px dashed var(--line);
    border-bottom: 1px dashed var(--line);
    color: var(--ink-soft);
    font-size: .85rem;
}
.agenda-almoco-sep .icon { width: 16px; height: 16px; }

/* Linha de endereço do card da Agenda — bairro em destaque + apelido + logradouro, tudo num span
   comum (não flex): um nó de texto solto dentro de um container flex vira seu próprio item e pode
   quebrar de forma imprevisível no meio da frase. */
.endereco-linha strong { color: var(--ink); font-weight: 700; }

/* Variação vertical do .cliente-card-actions (que por padrão empilha os icon-btn lado a lado nas
   páginas mais antigas) — usada quando o card tem uma 2ª ação fixa ("Ver no mapa", na Agenda) além
   do menu de ações, uma embaixo da outra em vez de lado a lado. */
.cliente-card-actions.is-stacked { flex-direction: column; }

/* Filtro de período colapsável (aba Próximos da Agenda) — fechado por padrão pra não ocupar espaço
   de tela; expande ao tocar no cabeçalho, igual um <details> mas com o mesmo visual dos demais
   controles do app. */
.filtro-colapsavel {
    margin-bottom: .75rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: var(--bg);
    overflow: hidden;
}

.filtro-toggle {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    min-height: 44px;
    box-sizing: border-box;
    padding: .6rem .85rem;
    font-size: .9rem;
    font-weight: 600;
    color: var(--ink-soft);
    background: transparent;
    border: none;
    text-align: left;
    cursor: pointer;
}

.filtro-toggle .icon { width: 1.1rem; height: 1.1rem; color: var(--muted); flex-shrink: 0; }
.filtro-toggle-label { flex: 1; }

.filtro-chevron { display: inline-flex; transition: transform .15s ease; flex-shrink: 0; }
.filtro-toggle.is-open .filtro-chevron { transform: rotate(180deg); }

.filtro-painel {
    display: flex;
    flex-direction: column;
    gap: .6rem;
    padding: 0 .85rem .85rem;
}

/* Campo rotulado dentro do painel de filtro (ex.: filtro por cliente nas listas globais de
   endereços/equipamentos) — label pequeno acima do controle, empilhado. */
.filtro-campo { display: flex; flex-direction: column; gap: .3rem; }
.filtro-campo-label { font-size: .85rem; color: var(--muted); }

.filtro-datas { display: flex; gap: .5rem; margin-bottom: 1rem; }
.filtro-datas .field { flex: 1; min-width: 0; margin-bottom: 0; }

/* input[type=date] nativo reserva espaço fixo pro ícone de calendário — em telas de 360px, com dois
   campos lado a lado, isso empurrava o campo pra fora do painel. Reduz padding/fonte e tira a folga
   que o Chrome deixa entre o texto da data e o ícone. */
.filtro-datas .input {
    width: 100%;
    min-width: 0;
    padding: .55rem .4rem;
    font-size: .85rem;
}

.filtro-datas .input::-webkit-calendar-picker-indicator {
    padding: 0;
    margin-left: .2rem;
}

/* Grupo de chips — filtro multi-seleção de estado, uma barra única (não pills soltos) com cada
   botão contido dentro dela; colunas de largura igual dividindo a barra toda (sem rolagem), só
   ícone (rótulo de texto vai no title/aria-label). Cinza neutro (mesmo tom do badge-rascunho) é o
   padrão não-selecionado; marcado vira cor da marca. Cada botão marca/desmarca sem afetar os demais. */
.chip-group {
    display: flex;
    overflow: hidden;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: var(--bg);
    margin-bottom: .75rem;
}

.chip {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 1 1 0;
    min-height: 2.75rem;
    padding: .5rem;
    color: var(--ink-soft);
    background: transparent;
    border: none;
    border-right: 1px solid var(--line);
    cursor: pointer;
}

.chip:last-child { border-right: none; }

.chip .icon { width: 1.3rem; height: 1.3rem; }

.chip.is-selected {
    color: #fff;
    background: var(--brand);
}

.chip:active { background: rgba(28, 27, 25, .08); }
.chip.is-selected:active { background: var(--brand-dark); }

/* ---- TabBar global (navegação entre módulos) ----
   Só nas rotas-destino (Home + 4 listas) — ver TabBar.razor/MainLayout. Empilhamento com a
   BottomBar da página: a TabBar fica em bottom: 0 e é a ÚNICA a receber o safe-area-inset (é ela
   que encosta na borda física); a BottomBar sobe pra cima dela via .has-tab-bar (abaixo). Os
   sheets (z 30/31) cobrem as duas. Altura fixa em --tab-bar-h para o deslocamento ser exato. */
:root { --tab-bar-h: 4.3rem; }

.tab-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 21; /* acima da .bottom-bar (20), abaixo dos sheets (30/31) */
    box-sizing: content-box; /* altura = --tab-bar-h; o padding do safe-area soma por fora */
    height: var(--tab-bar-h);
    display: flex;
    align-items: stretch;
    justify-content: space-around;
    max-width: 720px;
    margin: 0 auto;
    padding-bottom: env(safe-area-inset-bottom, 0px);
    background: #fff;
    border-top: 1px solid var(--line);
    box-shadow: 0 -2px 10px rgba(28, 27, 25, .06);
}

.tab-bar-item {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .15rem;
    flex: 1;
    min-width: 48px;
    padding: .3rem .2rem;
    color: var(--muted);
    text-decoration: none;
}

.tab-bar-item .icon { width: 1.5rem; height: 1.5rem; }

.tab-bar-label { font-size: .68rem; font-weight: 600; line-height: 1.1; text-align: center; }

/* Berço do ícone — existe em toda aba, com fundo só na ativa (abaixo), pra entrar e sair do
   destaque sem mexer no layout: a pílula cresce simétrica em volta do ícone de 1,5rem, então o
   ícone continua na mesma altura de antes e o cadeado da aba Pro, que é absoluto na célula, não
   se desalinha. */
.tab-bar-icone {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3.2rem;
    height: 1.9rem;
    border-radius: 1rem;
}

/* Aba ativa: cor de marca + pílula de fundo atrás do ícone, o mesmo --brand-tint que já marca
   selecionado no .attention-tile e no .view-toggle-btn. A cor sozinha era o único sinal visual de
   "você está aqui", e matiz é o primeiro a sumir no uso em campo (sol na tela) e para quem não
   distingue cores (WCAG 1.4.1) — forma resiste. O aria-current já servia o leitor de tela; isto é
   o equivalente para quem enxerga a barra.
   O fundo vai no berço do ícone, e não na célula inteira, porque a célula já usa fundo no :active
   pro retorno do toque — dois fundos no mesmo lugar e o toque some justamente na aba ativa. */
.tab-bar-item.is-active { color: var(--brand); }

.tab-bar-item.is-active .tab-bar-icone { background: var(--brand-tint); }

.tab-bar-item:active { background: var(--bg); border-radius: .6rem; }

/* Aba travada por assinatura (Recebimentos). Vira <button> — abre o sheet de valor em vez de navegar
   —, então precisa dos mesmos resets do button.module-tile: sem eles herdaria fonte, borda e fundo do
   agente e sairia visivelmente diferente das abas vizinhas. */
button.tab-bar-item {
    font: inherit;
    background: none;
    border: 0;
    cursor: pointer;
}

/* Menos peso visual que as abas navegáveis: o cadeado informa, a cor não precisa gritar. Mesmo
   tratamento do .module-tile.is-locked no painel Gerenciamento da Home. */
.tab-bar-item.is-locked { position: relative; color: var(--muted); opacity: .75; }

.tab-bar-lock {
    position: absolute;
    top: .3rem;
    right: 50%;
    margin-right: -1.15rem; /* encosta no canto superior direito do ícone, não da célula inteira */
    display: inline-flex;
    color: var(--muted);
}

.tab-bar-lock .icon { width: .8rem; height: .8rem; }

/* Reserva de rolagem pra TabBar — o MainLayout emite depois do @Body nas rotas-destino. */
.tab-bar-spacer { height: calc(var(--tab-bar-h) + env(safe-area-inset-bottom, 0px)); }

/* Página COM TabBar: a BottomBar da página empilha logo acima dela e devolve o safe-area
   (quem paga o inset é a TabBar). O spacer da BottomBar não muda — a reserva extra da TabBar
   vem do .tab-bar-spacer emitido pelo layout. */
.has-tab-bar .bottom-bar {
    bottom: calc(var(--tab-bar-h) + env(safe-area-inset-bottom, 0px));
    padding-bottom: .5rem;
    box-shadow: none;
}

/* Barra de ação fixa — ícones sempre ao alcance do polegar, um único padrão em todo o app */
.bottom-bar-spacer { height: 5.5rem; }

.bottom-bar {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 20;
    display: flex;
    align-items: stretch;
    justify-content: space-around;
    gap: .5rem;
    max-width: 720px;
    margin: 0 auto;
    padding: .5rem 1rem;
    padding-bottom: max(.5rem, env(safe-area-inset-bottom));
    background: #fff;
    border-top: 1px solid var(--line);
    box-shadow: 0 -2px 10px rgba(28, 27, 25, .06);
}

.bottom-bar-action {
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: .15rem;
    flex: 1;
    min-width: 48px;
    min-height: 48px;
    padding: .3rem;
    color: var(--ink);
    background: transparent;
    border: none;
    border-radius: .6rem;
    text-decoration: none;
    cursor: pointer;
}

/* Início emitido pela própria barra: largura de uma célula da TabBar (1/5 da barra), não a fração
   que sobraria de dividir a barra pelo número de ações da tela. As duas barras nunca aparecem
   juntas, então é ao TROCAR de tela que o botão mudaria de tamanho — e o dedo procura o Início pelo
   mesmo alvo em todo o app, que é a razão de ele já vir na mesma ponta esquerda das duas.
   A conta sai em % da própria barra, não em vw: percentual de flex-basis resolve contra o conteúdo
   do container (largura da barra menos as 2rem de padding lateral), então 1/5 da barra = 20% dele
   + .4rem. Usar vw erraria no desktop, onde a barra do rolamento entra no 100vw e não na barra. */
.bottom-bar-action.is-inicio { flex: 0 0 calc(20% + .4rem); }

.bottom-bar-action .icon { width: 1.6rem; height: 1.6rem; }

.bottom-bar-label {
    font-size: .68rem;
    font-weight: 600;
    line-height: 1.15;
    text-align: center;
}

.bottom-bar-action:active { background: var(--bg); }

.bottom-bar-action.is-primary {
    color: #fff;
    background: var(--brand);
    border-radius: .8rem;
}

.bottom-bar-action.is-primary:active { background: var(--brand-dark); }

.bottom-bar-action:disabled { opacity: .5; cursor: default; }
.bottom-bar-action:disabled:active { background: transparent; }
.bottom-bar-action.is-primary:disabled:active { background: var(--brand); }

/* Bottom sheet — painel deslizante pra ações que antes ocupavam espaço fixo (seleção, inclusão de item) */
.sheet-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(28, 27, 25, .45);
    z-index: 30;
}

/* Sheet aberto POR CIMA de outro (BottomSheet Aninhado="true"). Um degrau acima do par 30/31 para
   o backdrop de cima cobrir o painel de baixo — sem isto o sheet externo fica aceso e clicável ao
   lado do interno, porque painel(31) pinta depois de backdrop(30) mesmo vindo antes no DOM. */
.sheet-backdrop.is-aninhado { z-index: 32; }
.sheet-panel.is-aninhado { z-index: 33; }

.sheet-panel {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 31;
    max-width: 720px;
    margin: 0 auto;
    display: flex;
    flex-direction: column;
    max-height: 85vh;
    background: #fff;
    border-radius: .9rem .9rem 0 0;
    box-shadow: 0 -2px 16px rgba(28, 27, 25, .12);
    padding-bottom: env(safe-area-inset-bottom);
}

.sheet-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    padding: .9rem 1rem;
    border-bottom: 1px solid var(--line);
}

.sheet-title { margin: 0; font-size: 1.05rem; }

.sheet-body {
    overflow-y: auto;
    padding: 1rem;
    flex: 1;
}

.sheet-actions { display: flex; gap: .5rem; margin-top: .5rem; }
.sheet-actions .btn-secondary,
.sheet-actions .btn-primary-lg,
.sheet-actions .btn-danger-lg { flex: 1; }

/* Diálogo de confirmação (ConfirmDialog) — mesmo painel deslizante do BottomSheet, sem o
   cabeçalho com botão de fechar; sai só pelas duas ações explícitas. */
.confirm-panel { z-index: 31; }
.confirm-body { padding: 1.1rem 1rem; }
.confirm-title { margin: 0 0 .5rem; font-size: 1.05rem; }
.confirm-message { margin: 0 0 1rem; color: var(--muted); }

/* Frame de itens do orçamento — destaca o bloco de itens (cabeçalho + lista + total) do resto do
   formulário, que é só campos soltos. */
.itens-panel {
    margin: 0 0 1rem;
    padding: .9rem .95rem 1rem;
    background: var(--brand-tint);
    border: 1px solid #E3AFA0;
    border-radius: .8rem;
}

.itens-panel .section-header { margin-bottom: .6rem; }

.itens-panel h2 {
    margin: 0;
    font-size: 1.05rem;
    font-weight: 800;
    color: var(--brand-dark);
}

.itens-total {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    margin-top: .75rem;
    padding-top: .6rem;
    border-top: 1px dashed #E3AFA0;
    font-weight: 700;
    color: var(--brand-dark);
}

.itens-total .value { font-size: 1.2rem; font-weight: 800; }

/* Card de item do orçamento — layout dedicado (cliente-card genérico é compartilhado com listas de
   outras entidades, e ali título+ações dividem espaço com o subtítulo na mesma linha, quebrando
   feio). Aqui título+ações ficam na 1ª linha; quantidade/preço/subtotal ocupam a 2ª linha inteira. */
.item-card {
    display: flex;
    flex-direction: column;
    gap: .35rem;
    padding: .85rem .9rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: #fff;
}

.item-card-top {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: .5rem;
}

.item-card-title { font-size: 1rem; min-width: 0; }

.item-card-actions { display: flex; gap: .25rem; flex-shrink: 0; }

.item-card-detail {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: .5rem;
    font-size: .85rem;
    color: var(--muted);
}

.item-card-detail .value { font-weight: 700; color: var(--ink); }

/* ---- Parcelamento do recebimento (RecebimentoForm, modo "Parcelado") ----
   As linhas do plano reusam o .item-card do orçamento, mas cada uma carrega DOIS campos editáveis
   (valor e vencimento) em vez de texto: pelo ADR 0010 cada parcela é um recebimento independente, e
   negociação desigual ("500 agora, 250 e 250") é rotina. Lado a lado enquanto couber; empilhados no
   celular estreito, porque um <input type="date"> não encolhe abaixo da largura do seu próprio
   formato e espremê-lo corta o ano. */
.parcela-campos {
    display: flex;
    flex-wrap: wrap;
    gap: .5rem;
}

.parcela-campo {
    display: flex;
    flex-direction: column;
    gap: .25rem;
    flex: 1 1 9rem;
    min-width: 0;
}

.parcela-campo label {
    font-size: .78rem;
    color: var(--muted);
}

.parcela-campo .input { margin-bottom: 0; }

/* ---- Prévia do recebimento mensal (RecebimentoForm, modo "Todo mês") ----
   Não reusa o .item-card do parcelamento porque aqui não há campo editável: no mensal as linhas são
   iguais por definição (o que varia é a data), e ajustar um mês específico se faz depois, no card
   dele — cada linha nasce um recebimento comum. São até 12, e como cartões de dois campos elas
   empurrariam o Salvar para bem longe do polegar; em uma linha cada, a lista inteira cabe na tela. */
.plano-mensal {
    list-style: none;
    margin: 0 0 .5rem;
    padding: 0;
    border: 1px solid var(--line);
    border-radius: .7rem;
    overflow: hidden;
    background: var(--bg);
}

.plano-mensal li {
    display: flex;
    align-items: baseline;
    gap: .5rem;
    padding: .45rem .6rem;
    border-bottom: 1px solid var(--line);
    font-size: .9rem;
}

.plano-mensal li:last-child { border-bottom: none; }
.plano-mensal .plano-mes { color: var(--muted); min-width: 4.5rem; }
.plano-mensal .plano-data { flex: 1 1 auto; }
.plano-mensal .value { font-weight: 700; }

/* Passo a passo do número de parcelas. Alvo de toque de 44px nas duas pontas (a mão que segura o
   celular no cliente é uma só) e o valor no meio com largura fixa, para o texto não empurrar os
   botões de lugar ao passar de "9 parcelas" para "10 parcelas". */
.stepper {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    padding: .25rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: var(--bg);
    margin-bottom: .35rem;
}

.stepper-valor {
    flex: 1 1 auto;
    text-align: center;
    font-weight: 700;
    color: var(--ink);
}

/* Seletor de modo do recebimento: são 4 opções desde o mensal, e "Vou receber depois" não cabe num
   quarto de linha a 360px — viraria três linhas de texto, deixando o grupo MAIS alto do que a grade
   2x2 que a alternativa produz. A quebra continua acontecendo dentro do chip, sem espremer a fonte
   de todo chip-group do app. */
.chip-group-modo {
    display: grid;
    grid-template-columns: 1fr 1fr;
}

.chip-group-modo .chip {
    font-size: .88rem;
    line-height: 1.15;
    text-align: center;
    padding: .5rem .35rem;
}

/* Divisórias da grade. O .chip separa por border-right, que numa única fileira basta; em duas
   fileiras é o :last-child de lá que erra — ele zera a borda do 4º chip, não a do 2º, que é quem
   encosta na borda direita da barra. */
.chip-group-modo .chip:nth-child(2n) { border-right: none; }
.chip-group-modo .chip:nth-child(-n+2) { border-bottom: 1px solid var(--line); }

/* Fotos da ordem de serviço (S8) — grade de miniaturas com botão de remover sobreposto. */
.foto-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(84px, 1fr));
    gap: .5rem;
    margin-bottom: .75rem;
}

.foto-thumb {
    position: relative;
    aspect-ratio: 1;
    border-radius: .5rem;
    overflow: hidden;
    border: 1px solid var(--line);
    background: #fff;
}

.foto-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }

.foto-thumb-remove {
    position: absolute;
    top: .2rem;
    right: .2rem;
    background: rgba(0, 0, 0, .55);
    color: #fff;
}

/* Assinatura do cliente final (S8) — canvas de desenho e preview do PNG salvo. */
.assinatura-canvas {
    width: 100%;
    max-width: 100%;
    height: auto;
    aspect-ratio: 600 / 220;
    border: 1px dashed var(--line);
    border-radius: .5rem;
    background: #fff;
    touch-action: none;
    margin-bottom: .6rem;
}

.assinatura-preview {
    width: 100%;
    max-width: 320px;
    border: 1px solid var(--line);
    border-radius: .5rem;
    background: #fff;
    margin-bottom: .6rem;
}

/* Resumo de seleção (cliente/equipamento já escolhidos) */
.picker-summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    padding: .75rem .85rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: #fff;
}

/* Campo de escolha entre opções fixas (CampoSelect) — mesmo desenho do .picker-summary, porque é o
   mesmo gesto: um resumo tocável que abre a lista num BottomSheet. Aqui o elemento é um <button>
   (todo o campo abre o sheet), então precisa neutralizar o estilo nativo de botão e voltar ao
   alinhamento de campo de formulário. */
.campo-select {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    width: 100%;
    padding: .75rem .85rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: #fff;
    font: inherit;
    color: var(--ink);
    text-align: left;
    cursor: pointer;
}

.campo-select:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.campo-select:active { background: var(--surface-2, #f6f5f2); }
.campo-select:disabled { opacity: .6; cursor: default; }

.campo-select > .icon { flex: 0 0 auto; color: var(--muted); }

/* O valor escolhido quebra em vez de estourar o botão (rótulos como "Refrigeração e
   ar-condicionado" não cabem em 360px). */
.campo-select-valor { min-width: 0; }
.campo-select-placeholder { min-width: 0; color: var(--muted); }

/* Painel de atenção (Orçamentos, S5b) — contadores clicáveis que filtram a lista (Vencidos /
   Aguardando resposta / Aprovados aguardando execução). Foco é exclusivo com os chips de estado —
   ver Orcamentos.razor. Contador zerado fica visível e desabilitado (não some, pra não deslocar
   os demais entre um carregamento e outro). */
/* Duas colunas fixas: cada tile ganha metade da largura, espaço que a descrição completa (em vez de
   um rótulo abreviado) precisa para não virar sopa de letrinha. Número ímpar de tiles deixa o último
   sozinho na própria linha — aceitável, não vale complicar o grid por isso. */
.attention-panel {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    gap: .5rem;
    margin-bottom: .75rem;
}

/* Coluna: topo (ícone + número) e, embaixo, a descrição completa da pendência, ambos centralizados
   na largura do tile. */
.attention-tile {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .3rem;
    padding: .65rem .7rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: #fff;
    color: var(--ink);
    text-decoration: none;
    cursor: pointer;
}

/* Ícone + número centralizados como par na largura do tile — não grudados no canto esquerdo, que é
   o que sobrava de espaço quando a descrição virou frase completa embaixo. */
.attention-top { display: flex; align-items: center; justify-content: center; width: 100%; gap: .4rem; }

.attention-tile .icon { width: 1.1rem; height: 1.1rem; color: var(--muted); }

.attention-tile:disabled { opacity: .45; cursor: default; }

.attention-tile.is-active { border-color: var(--brand); background: var(--brand-tint); }

.attention-tile.is-active .icon { color: var(--brand); }

/* Tile de erros de sincronização (Home) — o modo de falha do offline-first pede tom de alerta,
   não o neutro dos outros contadores; só renderiza quando > 0. */
.attention-tile-danger { border-color: var(--danger); background: var(--danger-tint); }
.attention-tile-danger .icon,
.attention-tile-danger .attention-count { color: var(--danger); }

.attention-count { font-size: 1.3rem; font-weight: 700; line-height: 1; }

.attention-label { font-size: .78rem; color: var(--muted); text-align: center; line-height: 1.25; }

/* Painel "Gerenciamento" (Home) — irmão do .day-panel, e de propósito mais leve que ele: neutro em
   vez de tintado, borda de 1px em vez de 2. O "Agora" é o herói da tela (o dia em curso); este é a
   visão gerencial logo abaixo, e dois painéis com o mesmo peso brigariam pela atenção. */
.gestao-panel {
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: .9rem;
    padding: 1rem 1.1rem;
    margin-bottom: 1rem;
}

/* Tile branco sobre painel branco sumiria — dentro do painel eles se invertem para o tom da página
   e passam a ler como chips embutidos, o que também é o que dá a sensação de "agrupado". */
.gestao-panel .attention-tile { background: var(--bg); }

/* justify-content: center — .page-title-strong já centraliza o próprio texto, mas como item de
   flex ele encolhe pro tamanho do conteúdo (a caixa não estica sozinha), e text-align vira sem
   efeito numa caixa do tamanho do texto. */
.gestao-panel-header { display: flex; align-items: center; justify-content: center; margin-bottom: .7rem; }

/* Mesmo idioma de colapso do .filtro-toggle das listas (inclusive o .filtro-chevron, reusado no
   markup) — o app já ensinou esse gesto ao usuário, não custa um segundo vocabulário. Aqui sem a
   moldura própria: o .gestao-panel já é a caixa. */
.gestao-toggle {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    min-height: 44px;
    box-sizing: border-box;
    padding: 0;
    background: transparent;
    border: none;
    text-align: left;
    cursor: pointer;
    color: inherit;
}

/* O resumo empurra o chevron para a direita e é o que faz a linha fechada valer alguma coisa. */
.gestao-resumo { flex: 1; font-size: .85rem; color: var(--muted); }

/* Quando o resumo é a contagem do funil (não uma frase), ela vira badge — mesmo vermelho do
   .badge-vencido usado no restante do app, para não inventar um segundo vocabulário de alerta.
   margin-left: auto substitui o flex:1 do .gestao-resumo: empurra o badge (e o chevron, que vem
   logo depois) para a direita sem esticar a pílula até a largura do espaço livre. */
.gestao-resumo-badge { margin-left: auto; }

.gestao-toggle .filtro-chevron .icon { width: 1.1rem; height: 1.1rem; color: var(--muted); }

/* A rotação do chevron mora em `.filtro-toggle.is-open` — reusar só o elemento .filtro-chevron não
   traz o giro junto, e o cabeçalho ficava sem indicar aberto/fechado. */
.gestao-toggle.is-open .filtro-chevron { transform: rotate(180deg); }

/* PainelExpansivel (Parâmetros): cada seção é a própria caixa (.dev-panel), então o toggle interno
   fica sem moldura própria — mesma ideia do .gestao-toggle, mas com título em destaque no lugar do
   <h2> que a seção tinha antes de virar colapsável. */
.painel-expansivel { margin-top: 0; }
.painel-expansivel + .painel-expansivel { margin-top: .75rem; }

.painel-expansivel-toggle {
    display: flex;
    align-items: center;
    gap: .5rem;
    width: 100%;
    min-height: 44px;
    box-sizing: border-box;
    padding: 0;
    background: transparent;
    border: none;
    text-align: left;
    cursor: pointer;
    color: inherit;
}

.painel-expansivel-titulo { font-size: 1.05rem; font-weight: 700; color: var(--ink); flex-shrink: 0; }

/* Ocupa a sobra e trunca em vez de quebrar linha — o resumo é só um indício do que tem lá dentro,
   não precisa caber inteiro numa tela de 360px. */
.painel-expansivel-resumo {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    font-size: .85rem;
    color: var(--muted);
    text-align: right;
}

/* O chevron se empurra sozinho pra borda, em vez de depender do flex:1 do resumo: painel sem resumo
   (Minha conta) deixava a seta colada no título, fora da coluna onde ela está em todos os outros. */
.painel-expansivel-toggle .filtro-chevron { margin-left: auto; }
.painel-expansivel-toggle .filtro-chevron .icon { width: 1.1rem; height: 1.1rem; color: var(--muted); }
.painel-expansivel-toggle.is-open .filtro-chevron { transform: rotate(180deg); }

/* Sem flex/gap aqui de propósito: o conteúdo (EditForm, botões soltos, .marca-preview) já resolve o
   próprio espaçamento interno (.field margin-bottom etc.) — um gap por cima duplicaria a folga. */
.painel-expansivel-corpo { margin-top: .85rem; }

/* Subseção "Pendências a regularizar" dentro do painel Gerenciamento — separada do grid de módulos
   por uma régua, mesmo idioma do rodapé (.gestao-panel-link-rodape) que fecha o corpo dela. */
.gestao-subsecao {
    margin-top: .9rem;
    padding-top: .8rem;
    border-top: 1px solid var(--line);
}

.gestao-subsecao-title { font-size: .9rem; font-weight: 700; color: var(--ink); }

.gestao-corpo { display: block; }
.gestao-corpo[hidden] { display: none; }

.gestao-panel-link {
    display: inline-flex;
    align-items: center;
    gap: .2rem;
    flex-shrink: 0;
    font-size: .8rem;
    font-weight: 600;
    color: var(--brand-dark);
    text-decoration: none;
}

.gestao-panel-link .icon { width: .95rem; height: .95rem; }

/* Rodapé do corpo expandido: centralizado e destacado do último tile, para não ser lido como
   legenda do bloco de cima. */
.gestao-panel-link-rodape {
    display: flex;              /* o base é inline-flex: sem isto a régua de cima só cobriria o texto */
    justify-content: center;
    margin-top: .7rem;
    padding-top: .7rem;
    border-top: 1px solid var(--line);
}

/* Último filho encosta no padding do painel — sem isto sobra uma faixa morta no rodapé, que era
   parte do que fazia o conjunto parecer solto. */
.gestao-panel > *:last-child { margin-bottom: 0; }

/* Estado do painel quando não há tile nenhum: offline (não dá para afirmar nada) ou tudo em dia de
   verdade. Fica dentro do painel, não solto na página. */
.gestao-panel-estado {
    display: flex;
    align-items: center;
    gap: .45rem;
    margin: 0;
    font-size: .875rem;
    color: var(--ink-soft);
}

.gestao-panel-estado .icon { width: 1.1rem; height: 1.1rem; flex-shrink: 0; color: var(--muted); }
.gestao-panel-estado.is-ok .icon { color: var(--ok); }

/* Cabeçalho de grupo da tela /pendencias — separa os 4 tipos dentro da mesma lista. A contagem fica
   à direita para o olho bater o tamanho do grupo sem contar os cards. */
.pendencia-grupo {
    display: flex;
    align-items: center;
    gap: .45rem;
    margin: 1.1rem 0 .5rem;
    font-size: .8rem;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .3px;
    color: var(--muted);
}

.pendencia-grupo:first-of-type { margin-top: .35rem; }
.pendencia-grupo .icon { width: 1rem; height: 1rem; flex-shrink: 0; }
.pendencia-grupo-label { min-width: 0; }

.pendencia-grupo-count {
    margin-left: auto;
    flex-shrink: 0;
    padding: .05rem .45rem;
    border-radius: 999px;
    background: var(--bg);
    color: var(--ink-soft);
}

/* O card da worklist empilha info + ação (a lista de cadastro põe as ações num sheet; aqui a ação é
   uma só e resolver a pendência é o ponto da tela, então ela fica visível no card). Escopado ao
   .pendencia-item de propósito: solto em .cliente-card pegaria card de qualquer lista futura. */
.pendencia-item > .btn-secondary { margin-top: .6rem; }

/* Pilha vertical das ações do sheet de card (Orçamentos, S5b) */
.acoes-lista { display: flex; flex-direction: column; gap: .5rem; }

/* Separador entre as ações de negócio e o atalho "Cliente" (e, dentro dele, entre contato e cadastro) */
.acoes-sep { border: none; border-top: 1px solid var(--line); margin: .25rem 0; }

/* Mesma linha, folga maior — o corpo de um painel de Parâmetros é mais arejado que a lista densa de
   um BottomSheet, então os .25rem do .acoes-sep somem entre dois blocos com sua própria borda. */
.acoes-sep-solta { margin: 1.25rem 0; }

/* Bloco "ponto no mapa" (<PontoGps>) — mora aqui, e não numa página, porque é o mesmo bloco no sheet
   das duas listas de endereço, no formulário do endereço e no sheet da Agenda. Herda o gap do
   .acoes-lista quando está dentro de um sheet; o gap próprio é para o formulário, onde ele é um
   bloco solto entre campos. */
.ponto-gps { display: flex; flex-direction: column; gap: .5rem; }

.ponto-gps-titulo { font-size: .85rem; font-weight: 600; }

/* A linha de estado é o que a tela ganhou: ela responde "a rota cai na porta?" sem exigir um toque.
   Ícone alinhado ao topo (não centralizado) porque o texto quebra em duas linhas no celular. */
.ponto-gps-status {
    display: flex;
    align-items: flex-start;
    gap: .4rem;
    margin: 0;
    font-size: .85rem;
    line-height: 1.35;
}

.ponto-gps-status .icon { width: 1rem; height: 1rem; flex-shrink: 0; margin-top: .1rem; }
.ponto-gps-status.is-marcado { color: var(--ok); font-weight: 600; }
.ponto-gps-status.is-vazio { color: var(--muted); }

/* Item de lista clicável do EntityPicker — reset de botão sobre .cliente-card */
.picker-item {
    width: 100%;
    text-align: left;
    font: inherit;
    color: inherit;
    background: #fff;
    cursor: pointer;
}

.picker-item:active { background: var(--brand-tint); }

/* Ícone dentro de botões de texto (.btn-secondary usado como gatilho de sheet) */
.btn-secondary .icon { width: 1.1rem; height: 1.1rem; margin-right: .4rem; }

/* Página pública do orçamento (S5) — botões empilhados e grandes (uso em campo, sem o prestador
   por perto pra ajudar), Aprovar/Recusar com o mesmo peso visual. */
.publico-actions {
    display: flex;
    flex-direction: column;
    gap: .75rem;
    margin-top: 1.25rem;
}

/* Resultado da resposta do cliente final (aprovou/recusou) — neutro de propósito, não usa verde/
   vermelho de sucesso-erro porque recusar é uma resposta válida, não uma falha. */
.resultado-alert {
    margin-top: 1.25rem;
    padding: .85rem .9rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: var(--bg);
    color: var(--ink);
    font-weight: 600;
}

.resultado-alert:focus { outline: 2px solid var(--brand); outline-offset: 2px; }

/* Comentário que o cliente final deixou ao recusar (S5) — quadro próprio, não um parágrafo solto
   no topo da tela: é fala de terceiro e precisa se ler como citação, não como aviso do sistema.
   Filete vermelho à esquerda amarra com o badge "Recusado" sem gritar como erro (recusar é uma
   resposta válida). Serve tanto no formulário quanto dentro do BottomSheet da lista. */
.motivo-recusa {
    margin: 0 0 1rem;
    padding: .7rem .85rem;
    background: #fff;
    border: 1px solid var(--line);
    border-left: 3px solid var(--danger);
    border-radius: .7rem;
}

.motivo-recusa-label {
    display: block;
    margin-bottom: .2rem;
    font-size: .8rem;
    font-weight: 700;
    color: var(--muted);
}

.motivo-recusa-texto { margin: 0; color: var(--ink); }

/* Variante dentro do card da lista — o card já é branco, então o quadro se destaca pelo fundo
   cinza; texto cortado em 2 linhas para um comentário longo não esticar o card (a lista é
   varredura; o motivo inteiro fica na tela do orçamento). */
.motivo-recusa-inline {
    margin: 0;
    padding: .5rem .65rem;
    background: var(--bg);
    font-size: .85rem;
}

.motivo-recusa-inline .motivo-recusa-texto {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 2;
    line-clamp: 2;
    overflow: hidden;
}

/* Na tela do orçamento o texto é integral, mas cortado em 4 linhas até o "Ver tudo" — um
   comentário no teto de 500 caracteres empurraria o formulário todo para fora da tela. */
.motivo-recusa-clamp {
    display: -webkit-box;
    -webkit-box-orient: vertical;
    -webkit-line-clamp: 4;
    line-clamp: 4;
    overflow: hidden;
}

.motivo-recusa-toggle {
    min-height: 44px;              /* alvo de toque — uso em campo, uma mão só */
    margin-top: .1rem;
    padding: 0;
    font-size: .85rem;
    font-weight: 600;
    color: var(--brand-dark);
    background: transparent;
    border: none;
    text-decoration: underline;
    cursor: pointer;
}
/* ============================================================
   Timeline arrastável da aba "Hoje" da Agenda (dragTimeline.js)
   ============================================================ */

/* Linha do filtro da Agenda: o filtro colapsável ocupa a largura e, na aba Hoje, o alternador
   Lista/Linha do tempo entra à direita na MESMA linha — antes ele era uma 2ª faixa de botões logo
   abaixo, empurrando o dia pra baixo. flex-start pra o alternador ficar alinhado ao cabeçalho do
   filtro mesmo quando o painel do filtro abre e cresce pra baixo. A margem inferior que era do
   .filtro-colapsavel passa pra cá. */
.agenda-filtro-row {
    display: flex;
    align-items: flex-start;
    gap: .4rem;
    margin-bottom: .75rem;
}

.agenda-filtro-row .filtro-colapsavel {
    flex: 1;
    min-width: 0;
    margin-bottom: 0;
}

/* Alternador de exibição (Lista / Linha do tempo) — pílula segmentada com a mesma altura e borda do
   cabeçalho do filtro fechado, pra os dois controles lerem como pares na linha. */
.agenda-view-toggle-inline {
    display: flex;
    align-items: stretch;
    flex-shrink: 0;
    height: 44px;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: var(--bg);
    overflow: hidden;
}

.view-toggle-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2.75rem;
    padding: 0;
    color: var(--muted);
    background: transparent;
    border: none;
    cursor: pointer;
}

.view-toggle-btn + .view-toggle-btn { border-left: 1px solid var(--line); }
.view-toggle-btn .icon { width: 1.2rem; height: 1.2rem; }
.view-toggle-btn.is-active { color: var(--brand-dark); background: var(--brand-tint); }
.view-toggle-btn:active { background: var(--bg); }

/* Rota do dia (aba Hoje): botão de página acima da lista — só respiro; o botão em si é o
   .btn-secondary.btn-block de sempre. */
.rota-dia { margin: 0 0 .75rem; }
.rota-dia .hint { margin: .25rem 0 0; }

/* Área da grade: gutter de 48px à esquerda para os rótulos de hora; blocos posicionados absolutos. */
.agenda-timeline {
    position: relative;
    margin-top: .25rem;
    padding-left: 48px;
    border: 1px solid var(--line);
    border-radius: .6rem;
    background: var(--card);
    overflow: hidden;
}

.agenda-timeline-hora {
    position: absolute;
    left: 0;
    right: 0;
    height: 0;
    border-top: 1px dashed var(--line);
    pointer-events: none;
}

.agenda-timeline-hora span {
    position: absolute;
    left: .3rem;
    top: -.55rem;
    font-size: .7rem;
    color: var(--muted);
    background: var(--card);
    padding: 0 .2rem;
}

.agenda-timeline-almoco {
    position: absolute;
    left: 48px;
    right: .4rem;
    background: var(--wait-tint);
    border-radius: .4rem;
    display: flex;
    align-items: flex-start;
    justify-content: flex-end;
    pointer-events: none;
}

.agenda-timeline-almoco span {
    font-size: .68rem;
    color: var(--wait);
    padding: .1rem .35rem;
    font-weight: 600;
}

.agenda-timeline-bloco {
    position: absolute;
    left: 48px;
    right: .4rem;
    display: flex;
    align-items: stretch;
    overflow: hidden;
    background: var(--brand-tint);
    border: 1px solid var(--brand);
    border-left: 4px solid var(--brand);
    border-radius: .5rem;
    box-shadow: 0 1px 2px rgba(0, 0, 0, .08);
    transition: top .15s ease, height .15s ease;
    touch-action: pan-y;
}

.agenda-timeline-bloco.is-overflow {
    border-color: var(--wait);
    border-left-color: var(--wait);
}

.agenda-timeline-bloco.arrastando {
    transition: none;
    z-index: 5;
    box-shadow: 0 6px 16px rgba(0, 0, 0, .22);
    opacity: .96;
}

.agenda-timeline-bloco.is-focado {
    box-shadow: 0 0 0 3px var(--brand);
}

.agenda-timeline-bloco-grip {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 30px;
    flex-shrink: 0;
    color: var(--brand-dark);
    background: rgba(0, 0, 0, .04);
    border-right: 1px solid var(--brand);
    cursor: grab;
    /* Bloqueia o scroll da página durante o arrasto por toque (celular). */
    touch-action: none;
}

.agenda-timeline-bloco-grip:active {
    cursor: grabbing;
}

.agenda-timeline-bloco-body {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    justify-content: center;
    gap: .05rem;
    padding: .25rem .45rem;
    line-height: 1.2;
}

.agenda-timeline-bloco-body strong { font-size: .82rem; }
.agenda-timeline-bloco-body span { font-size: .78rem; color: var(--ink-soft); }

.agenda-timeline-bloco .icon-btn {
    align-self: center;
    flex-shrink: 0;
}

/* ==========================================================================
   Fase 3 — Recebimento (S10), Histórico (S11) e Resumo (S12)
   ========================================================================== */

/* Totais em dinheiro logo abaixo do painel de atenção de /recebimentos. Os tiles respondem
   "quantas"; esta linha responde "quanto", que é a pergunta que o prestador faz de verdade. */
.totais-linha {
    display: flex;
    flex-wrap: wrap;
    gap: .25rem 1rem;
    margin: 0 0 .75rem;
    font-size: .85rem;
    color: var(--ink-soft);
}

.totais-linha strong { color: var(--ink); }
.totais-linha .totais-alerta,
.totais-linha .totais-alerta strong { color: var(--danger); }

/* Variante não-clicável do attention-tile (Histórico): mesmo bloco visual, sem afordância de
   botão — ali os números são leitura, não filtro. */
.attention-tile.is-static { cursor: default; }

/* Dinheiro não cabe no tamanho do contador de unidades — encolhe a fonte sem mudar o bloco. */
.attention-count-money { font-size: .95rem; }

/* ---- Histórico (S11) ---- */

/* Seletor de cliente do Histórico — é o escopo da tela, não um filtro, então fica fora do
   filtro-colapsável e sempre visível. */
.escopo-barra { display: flex; flex-direction: column; gap: .5rem; margin-bottom: .75rem; }

/* "Limpar filtro" dentro do filtro-painel: link discreto, alinhado à esquerda. */
.filtro-limpar { align-self: flex-start; background: none; border: none; padding: 0; font-size: .8rem; cursor: pointer; }

.timeline { list-style: none; margin: 0; padding: 0; }
.timeline-ano { list-style: none; }

/* O item inteiro é o link (sem coluna de seta) — ver o comentário em Historico.razor.
   flex-direction: row explícito: .cliente-card é column, e sem isto o marcador ia parar em cima
   do título em vez de ao lado. */
.timeline-item {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: .6rem;
    text-decoration: none;
    color: inherit;
}

/* min-width:0 deixa o truncate-line do título funcionar dentro do flex; flex:1 faz a coluna de
   texto ocupar o resto da linha. .cliente-info é um <span> aqui, então precisa de display. */
.timeline-item .cliente-info { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: .15rem; }

/* Marcador de tipo à esquerda do card, o "trilho" da linha do tempo. Círculo de contorno em vez
   de preenchido: o card já tem peso visual, o ícone só precisa dizer de que módulo veio. */
.timeline-marcador {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    height: 2rem;
    flex-shrink: 0;
    border: 1px solid var(--line);
    border-radius: 999px;
    color: var(--brand);
}

.timeline-marcador .icon { width: 1.1rem; height: 1.1rem; }

/* Data · valor e o badge na mesma linha, com quebra permitida — o badge deixa de roubar uma
   coluna fixa do título. */
.timeline-meta { display: flex; flex-wrap: wrap; align-items: center; gap: .35rem .5rem; }
.timeline-meta .muted { font-size: .85rem; }

/* ---- Resumo (S12) ---- */

.mes-nav { display: flex; align-items: center; justify-content: space-between; gap: .5rem; margin-bottom: .75rem; }
.mes-nav-titulo { margin: 0; font-size: 1rem; font-weight: 700; color: var(--brand-dark); }
.mes-nav .icon-btn:disabled { opacity: .35; }

/* Quebra linha porque o painel do mês passou de dois tiles para três (Recebido, Previsto,
   Serviços): a 390px, três colunas deixam ~7rem para "R$ 1.234,56" em 1.25rem e o valor quebra no
   meio. Com o flex-basis abaixo, cabem dois por linha no celular e os três numa tela larga. */
.resumo-grid { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: .75rem; }

.resumo-tile {
    flex: 1 1 8.5rem;
    min-width: 0;
    display: flex;
    flex-direction: column;
    gap: .15rem;
    padding: .7rem .6rem;
    border: 1px solid var(--line);
    border-radius: .7rem;
    background: #fff;
    color: var(--ink);
    text-decoration: none;
}

.resumo-tile-destaque { border-color: var(--brand); background: var(--brand-tint); }
.resumo-tile.is-alerta { border-color: var(--danger); }
.resumo-tile.is-alerta .resumo-valor { color: var(--danger); }

.resumo-label { font-size: .72rem; color: var(--muted); text-transform: uppercase; letter-spacing: .02em; }

/* Sobre o fundo tonal do tile de destaque, o --muted cai para ~3.9:1 — abaixo dos 4.5:1 que texto
   pequeno exige (e esta tela é lida no sol). Escurece para --ink-soft só nesse contexto. */
.resumo-tile-destaque .resumo-label,
.resumo-tile-destaque .resumo-sub { color: var(--ink-soft); }
.resumo-valor { font-size: 1.25rem; font-weight: 700; line-height: 1.1; }
.resumo-sub { font-size: .75rem; color: var(--muted); }

.resumo-bloco { margin-top: 1.1rem; padding-top: .85rem; border-top: 1px dashed var(--line); }
.resumo-bloco-titulo { margin: 0 0 .5rem; font-size: .95rem; font-weight: 700; }
.resumo-funil { margin: 0 0 .25rem; font-size: .9rem; }

/* Lista nome / barra / valor — serve tanto "Como entrou" (formas de pagamento) quanto o funil de
   orçamentos. Os nomes são genéricos de propósito: a estrutura é a mesma e um segundo vocabulário
   para o mesmo componente é como as duas divergem depois. */
.resumo-barras { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: .45rem; }
.resumo-barras li { display: flex; align-items: center; gap: .5rem; font-size: .85rem; }
.resumo-barra-nome { flex: 0 0 8.5rem; color: var(--ink-soft); }
.resumo-barra-trilho { flex: 1; min-width: 0; height: .45rem; border-radius: 999px; background: var(--line); overflow: hidden; }
.resumo-barra-preenchimento { display: block; height: 100%; border-radius: 999px; background: var(--brand); }
.resumo-barra-valor { flex-shrink: 0; font-variant-numeric: tabular-nums; font-weight: 600; }

/* Cores do funil: as mesmas três do resto do app para aprovado/recusado/esperando — a barra é
   aria-hidden e o texto ao lado carrega o significado, então a cor reforça, nunca informa sozinha. */
.resumo-barra-preenchimento.is-aprovado { background: var(--ok); }
.resumo-barra-preenchimento.is-recusado { background: var(--danger); }
.resumo-barra-preenchimento.is-espera { background: var(--wait); }

/* Telas estreitas: o rótulo come a barra — empilha nome/valor e deixa a barra ocupar a linha
   inteira embaixo. */
@media (max-width: 420px) {
    .resumo-barras li { flex-wrap: wrap; }
    .resumo-barra-nome { flex: 1 1 auto; }
    .resumo-barra-trilho { flex: 1 1 100%; order: 3; }
}

/* Aviso de lista filtrada por origem (/recebimentos?origem=…) — sem ele, os contadores e totais do
   painel de atenção pareceriam ser da carteira inteira. */
.filtro-origem {
    display: flex;
    flex-wrap: wrap;
    gap: .35rem;
    align-items: baseline;
    margin: 0 0 .5rem;
    padding: .45rem .6rem;
    border-radius: .5rem;
    background: var(--brand-tint);
    color: var(--brand-dark);
}

/* ==========================================================================
   Home — redesenho (painel do dia + ações rápidas + grade de módulos).
   Personalização (reordenar/favoritar/ocultar, persistida) fica para depois.
   ========================================================================== */

.greet { display: flex; align-items: baseline; justify-content: space-between; gap: .5rem; margin: 0 0 1rem; }
.greet-name { margin: 0; font-size: 1.3rem; }
.greet-date { flex-shrink: 0; font-size: .85rem; color: var(--muted); text-align: right; }

/* Painel "Hoje" — fundo claro (tinta da marca) com contorno forte, não um bloco escuro: precisa
   ficar em destaque sem pesar mais que o resto da tela logo abaixo do cabeçalho já escuro. */
.day-panel {
    background: var(--brand-tint);
    border: 2px solid var(--brand);
    border-radius: .9rem;
    padding: 1rem 1.1rem;
    margin-bottom: 1rem;
}

.day-panel-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: .5rem;
    margin-bottom: .55rem;
}

.day-panel-title {
    margin: 0;
    font-size: .95rem;
    font-weight: 700;
    color: var(--brand-dark);
}

.day-panel-link {
    display: inline-flex;
    align-items: center;
    gap: .2rem;
    flex-shrink: 0;
    font-size: .8rem;
    font-weight: 600;
    color: var(--brand-dark);
    text-decoration: none;
}

.day-panel-link .icon { width: .95rem; height: .95rem; }

.day-panel-empty { margin: 0; font-size: .85rem; color: var(--ink-soft); }

/* Rede caiu ao buscar a agenda de hoje — mesmo desenho do .gestao-panel-estado (ícone + texto mudo),
   mas com classe própria: os dois painéis não compartilham contexto visual (.day-panel é tintado). */
.day-panel-estado { display: flex; align-items: center; gap: .45rem; margin: 0; font-size: .85rem; color: var(--ink-soft); }
.day-panel-estado .icon { width: 1.1rem; height: 1.1rem; flex-shrink: 0; color: var(--muted); }

/* Procedência ("agenda salva às 08:12") logo abaixo do que ela qualifica: precisa de ar para não
   colar na última linha e ser lida como mais um compromisso. Irmão adjacente de propósito — quando
   a nota é a única coisa no painel (sem rede e sem snapshot do dia), não sobra margem pendurada. */
.day-status-row + .day-panel-estado,
.day-panel-empty + .day-panel-estado { margin-top: .55rem; }

/* Linha de status (atrasado/em andamento/próximo) — régua tracejada padrão do app entre elas (ver
   nota em .dia-grupo) em vez de gap grande, pra caber as três sem esticar o painel. */
.day-status-row { display: flex; align-items: flex-start; gap: .55rem; padding: .4rem 0; }
/* var(--line) some quase contra o fundo tintado do painel (.day-panel) — usa var(--brand) pra
   a régua tracejada continuar visível aqui, mesmo padrão dashed, cor mais forte só neste contexto. */
.day-status-row + .day-status-row { border-top: 1px dashed var(--brand); }

.day-status-row .icon { flex-shrink: 0; width: 1.15rem; height: 1.15rem; margin-top: .1rem; color: var(--brand-dark); }

.day-status-text { display: flex; flex-direction: column; gap: .1rem; min-width: 0; }
.day-status-text strong { font-size: .88rem; color: var(--ink); }

.day-status-detail {
    font-size: .8rem;
    color: var(--ink-soft);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.day-status-row.is-atrasado .icon { color: var(--danger); }
.day-status-row.is-atrasado strong { color: var(--danger); }

.day-status-row.is-andamento .icon { color: var(--ok); }

/* Grade de módulos — cartão (borda + sombra) com ícone em círculo + rótulo. Substitui o antigo
   .module-launcher/.module-card (lista "etiqueta" vertical). */
.module-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: .6rem; }

/* Hub de Cadastros: 4 peers exatos ficam melhor num 2x2 balanceado que num 3+1 torto. */
.module-grid-2 { grid-template-columns: repeat(2, 1fr); }

/* Cabeçalho do painel Gerenciamento (Home): os 4 módulos de apoio numa linha só, não 3+1 — aqui o
   grid é o topo do painel, não uma tela dedicada, e um segundo estande solto por baixo destoaria.
   Gap e padding do tile encolhem porque 4 colunas sobram menos largura que 2 ou 3. */
.module-grid-4 { grid-template-columns: repeat(4, 1fr); gap: .4rem; }
.module-grid-4 .module-tile { min-height: 84px; padding: .7rem .3rem .55rem; }
.module-grid-4 .module-tile-icon { width: 2.4rem; height: 2.4rem; }
.module-grid-4 .module-tile-icon .icon { width: 1.15rem; height: 1.15rem; }
.module-grid-4 .module-tile-label { font-size: .72rem; }

.cadastros-intro { margin: -.2rem 0 .9rem; }

.module-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .4rem;
    min-height: 96px;
    padding: .9rem .5rem .7rem;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: .8rem;
    color: var(--ink);
    text-decoration: none;
    text-align: center;
    box-shadow: 0 1px 2px rgba(28, 27, 25, .04);
}

.module-tile:active { background: var(--bg); }

.module-tile-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 3rem;
    height: 3rem;
    border-radius: 50%;
    background: var(--brand-tint);
    color: var(--brand-dark);
}

.module-tile-icon .icon { width: 1.35rem; height: 1.35rem; }

.module-tile-label { font-size: .8rem; font-weight: 700; line-height: 1.2; }

/* Contador no canto do cartão (não do círculo) — cor vem de .badge-vencido aplicada junto no
   markup, este seletor só posiciona/encolhe o overlay. */
.module-tile-badge {
    position: absolute;
    top: .5rem;
    right: .5rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    min-width: 1.2rem;
    height: 1.2rem;
    padding: 0 .3rem;
    font-size: .68rem;
    line-height: 1;
}

/* ---- Muro Pro (S14) ------------------------------------------------------------------------ */

/* Cadeado no canto do tile de módulo travado. Fica no mesmo canto do contador porque os dois nunca
   coexistem: tile travado não tem número para mostrar. O tile continua tocável — leva ao sheet de
   valor, não a um beco (estratégia §3.3). */
.module-tile-lock {
    position: absolute;
    top: .5rem;
    right: .5rem;
    display: inline-flex;
    color: var(--muted);
}

.module-tile-lock .icon { width: .95rem; height: .95rem; }

/* Tile travado: mesma silhueta, menos peso visual — o cadeado informa, a cor não precisa gritar. */
.module-tile.is-locked .module-tile-icon { background: var(--line); color: var(--muted); }
.module-tile.is-locked .module-tile-label { color: var(--ink-soft); }

/* Travado o tile vira <button> (abre o sheet em vez de navegar) — sem isto herdaria a fonte e o
   alinhamento padrão do agente, e ficaria visivelmente diferente dos vizinhos <a>. */
button.module-tile {
    width: 100%;
    font: inherit;
    text-align: center;
    cursor: pointer;
}

/* Bloco que substitui o conteúdo de uma tela/painel inteiramente Pro. */
.pro-muro {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: .5rem;
    padding: 1.5rem 1rem;
    text-align: center;
}

.pro-muro-icone { margin: 0; color: var(--muted); }
.pro-muro-icone .icon { width: 1.75rem; height: 1.75rem; }
.pro-muro-titulo { margin: 0; font-family: var(--font-display); font-weight: 700; }
.pro-muro-texto { margin: 0; max-width: 34ch; font-size: .875rem; color: var(--ink-soft); }
.pro-muro .btn-secondary { margin-top: .35rem; }

/* Corpo do sheet de valor. */
.pro-gate { display: flex; flex-direction: column; gap: .75rem; }

.pro-gate-titulo {
    display: flex;
    align-items: center;
    gap: .5rem;
    margin: 0;
    font-family: var(--font-display);
    font-weight: 700;
}

.pro-gate-titulo .icon { width: 1.15rem; height: 1.15rem; color: var(--muted); }
.pro-gate-texto { margin: 0; color: var(--ink-soft); }

/* Navegação entre documentos no topo do formulário — um bloco único, uma anatomia só.

   .vinculo-pai: abre um documento ligado (dado do registro; seta no FIM, para a direita).
   .vinculo-retorno: volta para a tela de onde o usuário veio (navegação; seta no INÍCIO, para a
   esquerda). Só existe com ?voltar= e empilha ACIMA das linhas de vínculo — o mesmo desenho nas
   duas evita o "chip desgarrado" que a pílula antiga criava; o sentido da seta é quem diferencia. */
.vinculo-pai {
    display: flex;
    align-items: center;
    gap: .6rem;
    width: 100%;
    box-sizing: border-box;
    min-height: 48px;
    margin-bottom: .75rem;
    padding: .6rem .8rem;
    text-align: left;
    font-family: inherit;
    color: var(--ink);
    background: var(--bg);
    border: 1px solid var(--line);
    border-radius: .7rem;
    cursor: pointer;
}

.vinculo-pai:active { background: var(--brand-tint); }
.vinculo-pai:focus { outline: 2px solid var(--brand); outline-offset: 2px; }
.vinculo-pai .icon { width: 1.15rem; height: 1.15rem; flex-shrink: 0; color: var(--muted); }

.vinculo-pai-texto {
    display: flex;
    flex-direction: column;
    gap: .1rem;
    flex: 1;
    min-width: 0;
}

.vinculo-pai-rotulo {
    font-size: .9rem;
    font-weight: 600;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.vinculo-pai-detalhe {
    font-size: .8rem;
    color: var(--muted);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* O bloco de navegação alinha por colunas: a seta da linha de retorno tem a coluna 1 só dela;
   ícone do módulo e descrição ficam nas colunas 2 e 3 em TODAS as linhas. Quem paga é a linha de
   vínculo irmã, indentada em um ícone + gap (1.15rem + .6rem) sobre o padding base de .8rem —
   e só quando a linha de retorno existe: sem ela não há coluna 1 e o seletor não pega. */
.vinculo-retorno ~ .vinculo-pai { padding-left: 2.55rem; }

/* ===================== Vitrine (home do visitante anônimo — BoasVindas.razor) =====================
   Mora no app.css como todo o resto (o projeto não usa CSS isolado por componente). É a única tela
   que uma pessoa de fora vê antes de decidir se o produto serve pra ela: por isso o hero sangra até
   as bordas, cancelando o padding do .app-main, e emenda no cabeçalho da marca sem costura visível. */
.bv-hero {
    margin: -1.1rem -1rem 1.25rem;
    padding: 2.25rem 1.25rem 1.75rem;
    background: linear-gradient(165deg, var(--brand) 0%, var(--brand-dark) 100%);
    color: #fff;
}

.bv-marca {
    margin: 0 0 .6rem;
    font-family: var(--font-display);
    font-weight: 700;
    font-size: .95rem;
    letter-spacing: .12em;
    text-transform: uppercase;
    opacity: .8;
}

/* Selo da fase de validação, no herói — pílula translúcida da mesma família do .header-entrar.
   Fica ACIMA do <h1> porque emoldura tudo o que vem depois: a promessa da seção "Ainda não lançamos"
   só se lê direito por quem já sabe que o produto ainda não foi lançado. */
.bv-selo {
    display: inline-flex;
    align-items: center;
    margin: 0 0 1rem;
    padding: .3rem .7rem;
    border: 1px solid rgba(255, 255, 255, .5);
    border-radius: 999px;
    font-size: .75rem;
    font-weight: 700;
    letter-spacing: .06em;
    text-transform: uppercase;
    color: #fff;
}

.bv-titulo {
    margin: 0 0 .75rem;
    font-size: 1.9rem;
    line-height: 1.15;
    font-weight: 700;
}

.bv-sub {
    margin: 0 0 1.5rem;
    font-size: 1.02rem;
    line-height: 1.5;
    color: rgba(255, 255, 255, .88);
}

.bv-sub strong { color: #fff; }

.bv-ctas { display: flex; flex-direction: column; align-items: center; gap: .9rem; }

/* CTA invertido: sobre o fundo da marca, o .btn-primary-lg (fundo = marca) sumiria. */
.bv-cta {
    background: #fff;
    color: var(--brand-dark);
    font-weight: 700;
    gap: .5rem;
}

.bv-cta:active:not(:disabled) { background: var(--brand-tint); }
.bv-cta .icon { width: 1.2rem; height: 1.2rem; }

.bv-cta-secundaria {
    color: #fff;
    font-size: .95rem;
    font-weight: 600;
    text-decoration: underline;
    text-underline-offset: 3px;
    /* Alvo de toque de 44px sem virar botão: o padding é o alvo, o texto continua sendo um link. */
    padding: .6rem .25rem;
}

/* "Ver uma demonstração" é <button> de verdade (dispara uma chamada, não navega), mas precisa ler
   como os outros links secundários do herói. Sem este reset o navegador entrega fundo cinza, borda e
   a fonte dele — o botão destoaria de tudo em volta justamente no primeiro contato com o produto. */
button.bv-cta-secundaria {
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    background: none;
    border: 0;
    font-family: inherit;
    cursor: pointer;
}

button.bv-cta-secundaria:disabled { opacity: .7; cursor: default; text-decoration: none; }
button.bv-cta-secundaria .icon { width: 1.05rem; height: 1.05rem; }

/* Crítica dentro do herói: o vermelho de .error nasceu para fundo claro e some sobre o fundo da
   marca. Aqui o contraste vem do fundo, não da cor do texto. */
.bv-hero .error {
    margin: .9rem 0 0;
    padding: .55rem .8rem;
    border-radius: 8px;
    background: rgba(0, 0, 0, .25);
    color: #fff;
    font-size: .9rem;
}

.bv-secao { margin-bottom: 1.75rem; }

.bv-secao-titulo {
    margin: 0 0 .9rem;
    font-family: var(--font-display);
    font-size: 1.15rem;
    font-weight: 700;
}

.bv-lista { list-style: none; margin: 0; padding: 0; }

.bv-item {
    display: flex;
    align-items: flex-start;
    gap: .8rem;
    padding: .85rem 1rem;
    margin-bottom: .6rem;
    background: var(--card);
    border: 1px solid var(--line);
    border-radius: .8rem;
}

.bv-item > .icon {
    flex-shrink: 0;
    width: 1.35rem;
    height: 1.35rem;
    margin-top: .1rem;
    color: var(--brand);
}

.bv-item strong { display: block; font-size: 1rem; margin-bottom: .15rem; }
.bv-item span { display: block; font-size: .9rem; line-height: 1.45; color: var(--ink-soft); }

/* A seção da fase de validação é a proposta, não mais uma lista de recursos — ganha moldura tintada
   para não se confundir com "O dia inteiro em um app só", logo acima, que usa os mesmos .bv-item. Os
   cards de dentro continuam em --card, então o tint aparece só nas beiradas: é o suficiente para
   agrupar sem virar um segundo herói competindo com o de cima. */
.bv-fase {
    padding: 1.15rem 1rem 1.25rem;
    background: var(--brand-tint);
    border: 1px solid var(--line);
    border-radius: .9rem;
}

.bv-fase-lead { margin: 0 0 1rem; font-size: .95rem; line-height: 1.55; color: var(--ink-soft); }
.bv-fase-lista .bv-item:last-child { margin-bottom: 0; }
.bv-fase-cta { margin-top: 1.15rem; }
.bv-fase-cta .icon { width: 1.15rem; height: 1.15rem; }

/* Segunda porta do CTA da fase ("Já tenho um convite"), mesma função do .bv-cta-secundaria do herói —
   mas sobre o tint claro, e não sobre o fundo da marca. Reusar aquele traria o color:#fff junto e o
   link sumiria dentro da moldura. Padding é alvo de toque, como lá. */
.bv-fase-cta-alt {
    display: block;
    margin-top: .7rem;
    padding: .5rem .25rem;
    text-align: center;
    font-size: .95rem;
    font-weight: 600;
    color: var(--brand-dark);
    text-decoration: underline;
    text-underline-offset: 3px;
}

.bv-instalar p { margin: 0; font-size: .95rem; line-height: 1.5; color: var(--ink-soft); }

.bv-rodape {
    margin-top: 2rem;
    padding-top: 1.25rem;
    border-top: 1px solid var(--line);
    text-align: center;
}

.bv-rodape p { margin: 0 0 .6rem; font-size: .85rem; color: var(--muted); }

.bv-rodape a {
    display: inline-block;
    padding: .6rem .25rem;
    font-size: .95rem;
    font-weight: 600;
    color: var(--brand);
}

/* "Entrar" no cabeçalho, para o visitante — pílula clara sobre o fundo da marca, mesmo alvo de
   toque dos ícones vizinhos. */
.header-entrar {
    display: inline-flex;
    align-items: center;
    min-height: 36px;
    padding: 0 .85rem;
    border: 1px solid rgba(255, 255, 255, .55);
    border-radius: 999px;
    font-size: .9rem;
    font-weight: 600;
    color: #fff;
    text-decoration: none;
}

.header-entrar:active { background: rgba(255, 255, 255, .22); }

/* Saídas do login: criar conta (principal) e a vitrine (secundária), empilhadas. */
.login-vitrine { margin: 1.1rem 0 0; text-align: center; display: flex; flex-direction: column; }
.login-vitrine a { display: block; padding: .5rem; font-size: .9rem; color: var(--brand); font-weight: 600; }
.login-vitrine-secundario { font-weight: 500 !important; color: var(--muted) !important; }

/* "Esqueci minha senha" (entrega 04): abre um sheet, então é <button> e não âncora — daí não herdar
   nada do .login-vitrine acima. Mesmo alvo de toque e mesmo peso visual dos links de lá, para a
   coluna sob o formulário continuar lendo como uma lista de saídas. */
.login-esqueci {
    display: block;
    width: 100%;
    margin: .75rem 0 0;
    padding: .5rem;
    background: none;
    border: none;
    font: inherit;
    font-size: .9rem;
    font-weight: 600;
    color: var(--brand);
    text-align: center;
    cursor: pointer;
}

@media (max-width: 380px) {
    .bv-titulo { font-size: 1.65rem; }
}

/* ============================================================
   Cadastro, política pública e marca própria (S15)
   ============================================================ */

/* Cidade + UF na mesma linha: são um dado só na cabeça de quem preenche, e no celular cada campo
   isolado é mais uma rolagem no formulário mais frágil do produto. */
.field-linha { display: flex; gap: .75rem; align-items: flex-start; }
.field-linha > .field { flex: 1; min-width: 0; }
.field-linha > .field-uf { flex: 0 0 5.5rem; }

.opcional { font-weight: 400; color: var(--muted); }

/* Aceite: o alvo de toque é o rótulo inteiro (dedo grande, tela ao sol — mesma premissa do resto
   do app), não só o quadradinho de 16px. */
.field-aceite { margin-top: 1.25rem; }

.aceite {
    display: flex;
    gap: .6rem;
    align-items: flex-start;
    font-size: .9rem;
    font-weight: 400 !important;
    line-height: 1.4;
    cursor: pointer;
}

.aceite input[type="checkbox"] {
    flex: 0 0 auto;
    width: 1.15rem;
    height: 1.15rem;
    margin-top: .1rem;
    accent-color: var(--brand);
}

.aceite a { color: var(--brand); font-weight: 600; }

/* Documento público (política de privacidade) — coluna de leitura, sem TabBar e sem cartão: é
   texto corrido lido antes de existir conta, inclusive fora do app. */
.doc-publico {
    max-width: 40rem;
    margin: 0 auto;
    padding: 1.5rem 1.15rem 3rem;
    line-height: 1.6;
}

.doc-publico h1 { font-size: 1.6rem; margin: 0 0 .35rem; }
.doc-publico h2 { font-size: 1.05rem; margin: 1.75rem 0 .5rem; }
.doc-publico p { margin: 0 0 .75rem; }
.doc-publico ul { margin: 0 0 .75rem; padding-left: 1.15rem; }
.doc-publico li { margin-bottom: .45rem; }
.doc-publico a { color: var(--brand); font-weight: 600; }
.doc-rodape { margin-top: 2.5rem; }

/* Marca própria (Parâmetros → Minha marca). */
.marca-preview {
    display: flex;
    align-items: center;
    gap: .85rem;
    padding: .75rem;
    border: 1px solid var(--line);
    border-radius: .65rem;
    background: var(--surface, #fff);
    margin-bottom: .75rem;
}

/* Fundo xadrez claro: logo PNG com transparência sobre branco parece "sem imagem" — e o prestador
   precisa ver o que o cliente final vai ver. */
.marca-logo {
    flex: 0 0 auto;
    max-height: 4.5rem;
    max-width: 9.5rem;
    object-fit: contain;
    background-image:
        linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%),
        linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%);
    background-size: 12px 12px;
    background-position: 0 0, 6px 6px;
}

/* Telefone em leitura na seção "Meus dados" — parece campo, mas não é editável aqui (é o login). */
.perfil-telefone { margin: 0; padding: .35rem 0; font-weight: 600; }

.marca-vazia { color: var(--muted); font-size: .9rem; }
/* flex:1 nos dois botões (mesmo idioma do .sheet-actions): sem isso cada botão fica do tamanho do
   próprio texto, e "Salvar nome"/"Remover logo" sobram estreitos e desalinhados da largura cheia do
   campo/input acima e do "Trocar a logo" abaixo. */
.marca-acoes { display: flex; flex-wrap: wrap; gap: .5rem; margin-bottom: 1rem; }
.marca-acoes .btn-secondary { flex: 1; }

/* Cabeçalho da página pública com a marca do prestador: logo à esquerda, identificação à direita,
   sempre abaixo do <h1> (que ocupa a linha inteira — ver o comentário no .razor). */
.publico-marca {
    display: flex;
    align-items: center;
    gap: .9rem;
    margin-bottom: 1.25rem;
}

.publico-marca-texto { min-width: 0; } /* deixa o texto quebrar em vez de estourar o flex */
.publico-marca-texto p { margin: 0; }
.publico-prestador { font-weight: 600; line-height: 1.25; }

/* Teto proporcional (maior que antes, a pedido — a marca do prestador é o que dá confiança pro
   cliente final antes de aprovar): o prestador sobe o arquivo que tem no celular, e um símbolo
   largo não pode empurrar a identificação para fora da tela a 390px. */
.publico-logo {
    flex: 0 0 auto;
    max-height: 4.5rem;
    max-width: 7.5rem;
    object-fit: contain;
    border-radius: .5rem;
    box-shadow: 0 1px 3px rgba(0, 0, 0, .15);
}

/* Assinatura do produto no rodapé do link público (grátis). Fora do cartão e discreta: é o motor de
   distribuição, não um selo de demonstração — não pode competir com "Aprovar orçamento". */
.publico-selo {
    max-width: 40rem;
    margin: 1.25rem auto 2rem;
    text-align: center;
    font-size: .8rem;
    color: var(--muted);
}

.publico-selo a { color: var(--brand); font-weight: 600; }
