/* ══════════════════════════════════════════════════════════════════
   DESIGN SYSTEM — componentes `pt-*` + camada de reconciliação do
   Bootstrap 5 (mantido no projeto para grid/collapse/modal/dropdown;
   aqui só sua APARÊNCIA é redefinida para usar os tokens).

   Depende de frontend/static/css/design-tokens.css (carregado antes).
   Importado por base.html e por registration/base_onboarding.html.

   Convenção: toda regra abaixo lê variáveis de design-tokens.css —
   nunca um hex direto. Se precisar de uma cor nova, ela nasce como
   token, não aqui.
   ══════════════════════════════════════════════════════════════════ */

/* ── Reset & base ─────────────────────────────────────────────────── */
* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

html {
    scroll-behavior: smooth;
    /* Cor base sob o mesh — o que aparece se o gradiente falhar/for
       desligado (reduced-transparency, ou o navegador antes do CSS
       carregar). */
    background: var(--bg-primary);
}

@media (prefers-reduced-motion: reduce) {
    html {
        scroll-behavior: auto;
    }
}

/* ── Fundo ambiente — mesh sutil derivado de var(--accent) do tenant.
   É o que faz o vidro (materials.css, logo abaixo) finalmente
   aparecer: blur sobre cor lisa é invisível por definição — aqui há
   manchas de cor pra refratar. Cada tenant white-label ganha um fundo
   com a cara da marca dele automaticamente, sem configuração extra.

   position:fixed NO PSEUDO-ELEMENTO, não background-attachment:fixed
   no body — não repinta a cada scroll. z-index:-1 fica atrás de todo
   conteúdo real sem tocar filter/opacity/transform em body/.app-shell/
   .content-wrapper: é exatamente esse padrão (ancestral ganhando
   contexto de empilhamento) que já prendeu modal atrás do próprio
   backdrop neste repo — ver a nota de .content-wrapper mais abaixo.
   Por isso o body::before não precisa de position:relative no body:
   position:fixed usa o viewport como containing block sozinho, sem
   depender de um ancestral posicionado. */
body::before {
    content: '';
    position: fixed;
    inset: 0;
    z-index: -1;
    background:
        radial-gradient(ellipse 900px 700px at 12% -8%, color-mix(in srgb, var(--accent) 14%, transparent), transparent 60%),
        radial-gradient(ellipse 800px 800px at 92% 12%, color-mix(in srgb, var(--accent) 10%, transparent), transparent 55%),
        radial-gradient(ellipse 1000px 900px at 45% 115%, color-mix(in srgb, var(--accent) 8%, transparent), transparent 60%),
        var(--bg-primary);
    pointer-events: none;
}

/* Sem mesh quando o usuário pede menos transparência/mais contraste —
   o fundo cai pro sólido puro, sem manchas, e o vidro (materials.css)
   também vira opaco nesse mesmo cenário. */
@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    body::before {
        background: var(--bg-primary);
    }
}

body {
    font-family: var(--font-sans);
    font-size: var(--text-base);
    /* Transparente de propósito — o fundo visível é o body::before
       (o mesh). Mantém --bg-primary como fallback só em html, acima. */
    background-color: transparent;
    color: var(--text-primary);
    line-height: var(--leading-normal);
    transition: color var(--dur-slow);
}

h1, h2, h3, h4, h5, h6 {
    font-family: var(--font-title);      /* Gotham no portal; Snasm fora dele (login/onboarding/públicas têm --font-title próprio) */
    font-synthesis: none;                /* Snasm (fora do portal) é Light-only: sem negrito falso. Inofensivo pro Gotham, que já traz pesos reais */
    font-weight: var(--fw-semibold);
    letter-spacing: var(--tracking-snug);
    color: var(--text-primary);
}

/* ── Acessibilidade ───────────────────────────────────────────────── */
.pt-skip-link {
    position: absolute;
    top: -48px;
    left: var(--space-3);
    z-index: var(--z-toast);
    background: var(--accent);
    color: #fff;
    padding: var(--space-2) var(--space-4);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    font-weight: var(--fw-semibold);
    text-decoration: none;
    transition: top var(--dur-base) var(--ease);
}

.pt-skip-link:focus {
    top: var(--space-3);
    color: #fff;
}

.sr-only {
    position: absolute;
    width: 1px;
    height: 1px;
    padding: 0;
    margin: -1px;
    overflow: hidden;
    clip: rect(0, 0, 0, 0);
    white-space: nowrap;
    border: 0;
}

/* Indicador de foco único e visível em todo o produto — antes só
   existiam 2 ocorrências de :focus-visible, isoladas no CRM. */
:focus-visible {
    outline: 2px solid var(--accent);
    outline-offset: 2px;
    border-radius: var(--radius-sm);
}

a, button, [role="button"], input, select, textarea {
    font-family: inherit;
}

/* ══════════════════════════════════════════════════════════════════
   SHELL — sidebar, navbar, conteúdo principal
   ══════════════════════════════════════════════════════════════════ */
.app-shell {
    display: flex;
    min-height: 100vh;
}

/* Cor e material vêm de .material-nav (materials.css) — .sidebar cuida só
   de layout, e de --sidebar-text pro texto (materials.css não toca `color`,
   então não há conflito de cascata entre os dois arquivos). Antes disto era
   um gradiente escuro hardcoded IGUAL nos 3 temas; agora é uma superfície
   de material como qualquer outra e SEGUE o tema (decisão HIG). */
.sidebar {
    color: var(--sidebar-text);
    width: 248px;
    flex-shrink: 0;
    display: flex;
    flex-direction: column;
    transition: margin-left var(--dur-slow) var(--ease), width var(--dur-slow) var(--ease);
}

/* Desktop (>=1024px): colapsar vira rail de ícones, nunca some do layout */
@media (min-width: 1024px) {
    .sidebar {
        position: sticky;
        top: 0;
        height: 100vh;
    }

    .sidebar.collapsed {
        width: 76px;
    }

    .sidebar.collapsed .sidebar-logo .logo-img,
    .sidebar.collapsed .nav-link span,
    .sidebar.collapsed .nav-section-label,
    .sidebar.collapsed .chevron-icon,
    .sidebar.collapsed .sidebar-user-info {
        display: none;
    }

    .sidebar.collapsed .sidebar-logo .logo-img-mini {
        display: block;
    }

    .sidebar.collapsed .nav-link {
        justify-content: center;
        padding-left: 0;
        padding-right: 0;
    }

    .sidebar.collapsed .collapse-items {
        margin: 0 6px;
    }

    .sidebar-backdrop {
        display: none !important;
    }
}

/* Mobile/tablet (<1024px): a sidebar é um drawer — fora da tela por
   padrão, entra por cima do conteúdo com um backdrop. Resolve a
   inutilização em telas pequenas (sidebar fixa de 250px sem esse
   comportamento consumia a viewport inteira). */
@media (max-width: 1023px) {
    .sidebar {
        position: fixed;
        top: 0;
        left: 0;
        height: 100vh;
        z-index: var(--z-drawer);
        transform: translateX(-100%);
    }

    .sidebar.mobile-open {
        transform: translateX(0);
        box-shadow: var(--shadow-lg);
    }

    /* Em mobile o botão nunca "recolhe para rail" — ou está aberto
       (mobile-open) ou fechado (fora da tela). A classe .collapsed
       herdada do estado salvo em desktop é ignorada aqui. */
    .sidebar.collapsed {
        transform: translateX(-100%);
    }

    .sidebar.collapsed.mobile-open {
        transform: translateX(0);
    }
}

/* Scrim do drawer mobile — sem filhos (a sidebar é irmã dela no DOM, criada
   por pt-ui.js), então backdrop-filter aqui não cria o risco de contexto de
   empilhamento preso que existe em ancestral de modal. Mesmo tratamento de
   .material-scrim (materials.css), duplicado em vez de reutilizado porque
   este elemento é criado por JS com className fixo — trocar pra
   `.material-scrim` exigiria mexer em pt-ui.js pra um ganho cosmético. */
.sidebar-backdrop {
    display: none;
    position: fixed;
    inset: 0;
    background: rgba(9, 9, 11, .5);
    z-index: var(--z-backdrop);
    animation: pt-fade-in var(--dur-base) var(--ease);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .sidebar-backdrop {
        background: rgba(9, 9, 11, .32);
        backdrop-filter: blur(8px);
        -webkit-backdrop-filter: blur(8px);
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .sidebar-backdrop {
        background: rgba(9, 9, 11, .72) !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

.sidebar-backdrop.show {
    display: block;
}

.sidebar-logo {
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4) var(--space-3);
    border-bottom: 1px solid var(--sidebar-separator);
    min-height: 64px;
}

.sidebar-logo a {
    display: flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
    width: 100%;
}

.sidebar-logo .logo-img {
    width: 90%;
    max-width: 200px;
    height: auto;
    object-fit: contain;
    display: block;
}

.sidebar-logo .logo-img-mini {
    display: none;
    height: 36px;
    width: 36px;
    object-fit: contain;
    border-radius: var(--radius-md);
}

.sidebar-nav {
    flex: 1;
    overflow-y: auto;
    padding: var(--space-2) 0;
}

/* `.sidebar .nav-link`, não `.nav-link` sozinho + !important: o Bootstrap
   CDN carrega ANTES de design-system.css (mesma especificidade, 0,1,0), e a
   ordem de origem já bastaria — mas escopar pro ancestral sobe a
   especificidade (0,2,0) e deixa o vencedor explícito na regra, não
   implícito na ordem dos <link>. `.nav-link` só existe dentro da sidebar
   neste produto (nenhum outro template usa a classe), então o escopo não
   perde nada. */
.sidebar .nav-link {
    color: var(--sidebar-text);
    padding: 10px var(--space-4);
    margin: 2px var(--space-2);
    border-radius: var(--radius-md);
    transition: background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease);
    font-weight: var(--fw-medium);
    font-size: var(--text-sm);
    display: flex;
    align-items: center;
    gap: 10px;
    min-height: var(--tap-target-min);
}

.sidebar .nav-link i {
    width: 18px;
    text-align: center;
    font-size: var(--text-sm);
    flex-shrink: 0;
}

/* Fase 7 (passada de alinhamento, 08/09/2026) — o rótulo é o único filho
   flexível do link; sem isto, um label comprido ("Antecipação de
   Recebíveis", "Cartão de Crédito" + o badge "em breve") não quebra nem
   trunca: ele estoura a largura do item e o resto some cortado pela
   sidebar (era o "informações que não aparecem por completo" relatado).
   `min-width: 0` é o que permite um item de flex ENCOLHER abaixo do seu
   conteúdo — sem ele, `flex: 1` sozinho não impede o overflow. */
.sidebar .nav-link span {
    flex: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sidebar .nav-link small {
    flex-shrink: 0;
}

.sidebar .nav-link:hover {
    color: var(--sidebar-text-hover);
    background: var(--sidebar-hover-bg);
}

/* Estado ativo NÃO depende só de cor (WCAG + spec): a barra de 3px via
   inset box-shadow é o marcador de FORMA — continua legível pra quem não
   distingue a diferença de matiz entre --sidebar-active-bg e o resto. */
.sidebar .nav-link.active {
    color: var(--sidebar-text-active);
    background: var(--sidebar-active-bg);
    font-weight: var(--fw-semibold);
    box-shadow: inset 3px 0 0 var(--accent);
}

.nav-link[data-bs-toggle="collapse"] .chevron-icon {
    margin-left: auto;
    font-size: var(--text-2xs);
    transition: transform var(--dur-base) var(--ease);
}

.nav-link[data-bs-toggle="collapse"]:not(.collapsed) .chevron-icon {
    transform: rotate(180deg);
}

.collapse-items {
    background: var(--sidebar-hover-bg);
    margin: 0 var(--space-2);
    border-radius: var(--radius-md);
    padding: var(--space-1) 0;
}

.collapse-items .nav-link {
    margin: 2px 8px;
    padding-left: 32px;
    font-size: var(--text-sm);
}

/* --sidebar-label é cor DEDICADA por tema (ver design-tokens.css) — não
   var(--text-tertiary)/var(--text-secondary) direto: em light esses
   tokens reprovam WCAG nesse papel (documentado na Fase 1). Antes
   este rótulo era 30% branco fixo e reprovava até AA-large (2.10:1,
   "FALHA CONHECIDA" no check_contrast.py antigo). */
.nav-section-label {
    color: var(--sidebar-label);
    font-family: var(--font-accent);     /* rótulo/detalhe (Gotham via --font-accent) */
    font-size: var(--text-2xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
    padding: var(--space-4) var(--space-4) 6px;
    margin: 0 var(--space-2);
}

.nav-divider {
    height: 1px;
    background: var(--sidebar-separator);
    margin: var(--space-2) var(--space-4);
}

.sidebar-footer {
    padding: var(--space-3) var(--space-4);
    border-top: 1px solid var(--sidebar-separator);
}

.sidebar-user {
    display: flex;
    align-items: center;
    gap: 10px;
    font-size: var(--text-sm);
    color: var(--sidebar-label);
}

.sidebar-user-avatar {
    width: 32px;
    height: 32px;
    border-radius: var(--radius-md);
    background: color-mix(in srgb, var(--accent) 35%, transparent);
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: var(--fw-bold);
    font-size: var(--text-xs);
    color: #fff;
    flex-shrink: 0;
}

/* ── Conteúdo principal ───────────────────────────────────────────── */
.main-content {
    flex: 1;
    min-width: 0;
    display: flex;
    flex-direction: column;
    min-height: 100vh;
}

/* Cor e material vêm de .material-bar (materials.css), igual .sidebar —
   .top-navbar cuida só de layout. É `position: sticky` com conteúdo
   rolando por baixo: é essa relação que dá sentido ao material (scroll
   edge effect da Apple), não decoração. */
.top-navbar {
    height: 56px;
    padding: 0 var(--space-5);
    display: flex;
    align-items: center;
    justify-content: space-between;
    position: sticky;
    top: 0;
    z-index: var(--z-sticky);
    gap: var(--space-3);
}

.top-navbar-left {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    min-width: 0;
}

.top-navbar-title {
    font-size: var(--text-md);
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.top-navbar-right {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
}

.sidebar-toggle,
.theme-toggle {
    background: transparent;
    color: var(--text-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    width: var(--tap-target-min);
    height: var(--tap-target-min);
    display: flex;
    align-items: center;
    justify-content: center;
    cursor: pointer;
    transition: all var(--dur-fast) var(--ease);
    font-size: var(--text-sm);
}

.sidebar-toggle:hover {
    background: var(--accent-light);
    color: var(--accent);
    border-color: var(--accent);
}

.theme-toggle:hover {
    background: var(--amber-light);
    color: var(--amber);
    border-color: var(--amber);
}

.navbar-logout {
    background: transparent;
    border: 1px solid var(--border-color);
    color: var(--text-secondary);
    border-radius: var(--radius-md);
    padding: 8px var(--space-4);
    font-size: var(--text-sm);
    font-weight: var(--fw-medium);
    cursor: pointer;
    transition: all var(--dur-fast) var(--ease);
    display: flex;
    align-items: center;
    gap: 6px;
    min-height: var(--tap-target-min);
}

.navbar-logout:hover {
    background: var(--rose-light);
    color: var(--rose);
    border-color: var(--rose);
}

/* O padding real do conteúdo vem de .portal-content/.alert-container —
   zera o gutter que o Bootstrap aplica por padrão em .container-fluid
   para não somar as duas coisas (dobraria a margem lateral). */
.container-fluid {
    padding-left: 0 !important;
    padding-right: 0 !important;
}

.content-wrapper {
    width: 100%;
    flex: 1;
}

.portal-content {
    padding: var(--space-5) var(--space-6) var(--space-8);
    max-width: 100%;
}

.alert-container {
    padding: 0 var(--space-6);
}

@media (max-width: 640px) {
    .portal-content {
        padding: var(--space-4) var(--space-3) var(--space-6);
    }

    .alert-container {
        padding: 0 var(--space-3);
    }

    .top-navbar {
        padding: 0 var(--space-3);
    }
}

/* Entrada de conteúdo — uma única transição leve por navegação,
   sem depender de nth-child (o que antes limitava a 5 elementos e
   deslocava o layout em 12px a cada troca de página). */
@keyframes pt-fade-in {
    from {
        opacity: 0;
    }

    to {
        opacity: 1;
    }
}

@media (prefers-reduced-motion: no-preference) {
    .content-wrapper {
        /* `backwards`, NUNCA `both`/`forwards`: com fill pra frente o
           navegador mantém a animação de `opacity` "preenchendo" depois de
           terminar, e elemento com animação de opacity CRIA CONTEXTO DE
           EMPILHAMENTO — permanente, nesse caso. Isso aprisionava todo
           `.modal` declarado aqui dentro (que é onde os templates os
           declaram): o `z-index: 1055` do modal passava a valer só dentro
           do .content-wrapper, e o `.modal-backdrop` — que o Bootstrap cria
           como filho DIRETO de <body>, no contexto raiz — pintava por cima.
           O modal aparecia, mas nenhum clique chegava nele: nem "Cancelar"
           nem o "X" fechavam, então o backdrop ficava preso e a página
           inteira ficava escura e morta. Atingia TODA tela com modal
           (calendário, agenda e o modal de planos/checkout em
           configuracoes.html), não só o calendário.
           `backwards` mantém o estado inicial ANTES de começar (que é o que
           evita o flash) e não deixa fill depois — e como o estado final é
           `opacity: 1`, idêntico ao padrão, não se perde nada visualmente.
           Medido com elementFromPoint sobre o botão: com `both` devolvia
           `.modal-backdrop`; com `backwards`, devolve o próprio botão. */
        animation: pt-fade-in var(--dur-slow) var(--ease) backwards;
    }
}

/* ══════════════════════════════════════════════════════════════════
   CARDS
   ══════════════════════════════════════════════════════════════════ */
.card,
.pt-card {
    background-color: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    box-shadow: var(--card-shadow);
    color: var(--text-primary);
    transition: box-shadow var(--dur-base) var(--ease);
}

.pt-card:hover {
    box-shadow: var(--card-shadow-hover);
}

.pt-card-header {
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
}

.pt-card-header h3 {
    font-size: var(--text-base);
    font-weight: var(--fw-semibold);
    margin: 0;
}

.pt-card-body {
    padding: var(--space-5);
}

/* ══════════════════════════════════════════════════════════════════
   KPI / ESTATÍSTICA — canônico .pt-stat. Absorve .metric-card
   (home/dashboard), .periodo-kpi (financeiro/dre) e .stat-card
   (crm/tarefas/kanban): eram 4 implementações do mesmo objeto.
   ══════════════════════════════════════════════════════════════════ */
.pt-stat-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: var(--space-4);
    margin-bottom: var(--space-5);
}

/* P2: vira vidro — tile de KPI/estatística, tier regular. `background`/
   `border`/sombra própria saíram; fundo/blur/borda vêm do material.
   A sombra de :hover abaixo continua — é ESTADO, não fundo estático,
   não é o que a Apple avisa contra. */
.pt-stat,
.metric-card,
.periodo-kpi,
.stat-card,
.fin-stat-card,
.task-stat-card {
    border-radius: var(--radius-lg);
    padding: var(--space-5);
    background: var(--material-bg-solid);
    border: 1px solid var(--material-border);
    background-image: linear-gradient(to bottom, var(--material-highlight), transparent 40%);
    background-blend-mode: overlay;
    box-shadow: var(--material-shadow-over-plain);
    transition: box-shadow var(--dur-base) var(--ease), transform var(--dur-base) var(--ease);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .pt-stat,
    .metric-card,
    .periodo-kpi,
    .stat-card,
    .fin-stat-card,
    .task-stat-card {
        background-color: var(--material-bg);
        backdrop-filter: blur(var(--material-blur-regular)) saturate(var(--material-saturate));
        -webkit-backdrop-filter: blur(var(--material-blur-regular)) saturate(var(--material-saturate));
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .pt-stat,
    .metric-card,
    .periodo-kpi,
    .stat-card,
    .fin-stat-card,
    .task-stat-card {
        background-color: var(--material-bg-solid) !important;
        background-image: none !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

.pt-stat:hover,
.metric-card:hover,
.periodo-kpi:hover,
.stat-card:hover {
    box-shadow: var(--card-shadow-hover);
    transform: translateY(-1px);
}

.pt-stat-icon,
.metric-icon,
.fsc-icon,
.tsc-icon {
    width: 40px;
    height: 40px;
    border-radius: var(--radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-md);
    margin-bottom: var(--space-3);
    /* Deriva o fundo do ícone da própria --stat-accent (ex.: ciano num
       card de PIX) em vez de --accent-light fixo — sem isso, um card
       com --stat-accent customizado mostrava o glifo colorido sobre um
       círculo sempre indigo, uma combinação que não batia. */
    background: color-mix(in srgb, var(--stat-accent, var(--accent)) 12%, transparent);
    color: var(--stat-accent, var(--accent));
}

.pt-stat-label,
.metric-label,
.periodo-kpi .label,
.stat-card .stat-label,
.fsc-label,
.tsc-label {
    font-family: var(--font-accent);     /* rótulo de KPI/stat (Gotham via --font-accent) */
    font-size: var(--text-xs);
    color: var(--text-secondary);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
    margin-bottom: 6px;
}

.pt-stat-value,
.metric-value,
.periodo-kpi .value,
.stat-card .stat-value,
.fsc-value,
.tsc-value {
    font-size: var(--text-2xl);
    font-weight: var(--fw-bold);
    color: var(--text-primary);
    line-height: 1;
    font-family: var(--font-display);
}

.periodo-kpi .value.warn {
    color: var(--amber);
}

/* ══════════════════════════════════════════════════════════════════
   AÇÕES RÁPIDAS (grid de atalhos)
   ══════════════════════════════════════════════════════════════════ */
.pt-action-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(140px, 1fr));
    gap: var(--space-3);
    margin-bottom: var(--space-5);
}

.pt-action-tile {
    background-color: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    padding: var(--space-5) var(--space-3) var(--space-4);
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 10px;
    text-decoration: none;
    color: var(--text-primary);
    box-shadow: var(--card-shadow);
    transition: all var(--dur-base) var(--ease);
    cursor: pointer;
}

.pt-action-tile:hover {
    border-color: var(--accent);
    box-shadow: var(--card-shadow-hover);
    transform: translateY(-2px);
    color: var(--text-primary);
}

.pt-action-tile .tile-icon {
    width: 44px;
    height: 44px;
    border-radius: var(--radius-md);
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--text-lg);
    background: var(--accent-light);
    color: var(--accent);
    transition: transform var(--dur-base) var(--ease);
}

.pt-action-tile:hover .tile-icon {
    transform: scale(1.08);
}

.pt-action-tile span {
    font-size: var(--text-xs);
    font-weight: var(--fw-medium);
    text-align: center;
    line-height: 1.3;
    color: var(--text-secondary);
}

.pt-action-tile:hover span {
    color: var(--text-primary);
}

/* ══════════════════════════════════════════════════════════════════
   TABELAS — .pt-table-wrap resolve as ~22 tabelas sem contêiner de
   scroll encontradas na auditoria (financeiro, calendário, home,
   licitações, equipe, projetos).
   ══════════════════════════════════════════════════════════════════ */
.pt-table-wrap {
    background: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-lg);
    overflow: hidden;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

.pt-table {
    width: 100%;
    border-collapse: collapse;
    font-size: var(--text-base);
    font-variant-numeric: tabular-nums;
}

/* P2: vira vidro — cabeçalho de tabela sticky, tier thin (a mais fina
   da escala, e a que mais roda dentro de contêiner com scroll — ver
   risco de performance no plano). `background` opaco saiu; o fundo
   translúcido vem do material. Fallback sólido primeiro, promoção via
   @supports depois — igual ao resto do arquivo. */
.pt-table thead th {
    padding: var(--space-3) var(--space-4);
    font-size: var(--text-xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
    color: var(--text-secondary);
    border-bottom: 1px solid var(--material-border);
    background: var(--material-bg-solid);
    white-space: nowrap;
    position: sticky;
    top: 0;
    z-index: 1;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .pt-table thead th {
        background-color: var(--material-bg);
        backdrop-filter: blur(var(--material-blur-thin)) saturate(var(--material-saturate));
        -webkit-backdrop-filter: blur(var(--material-blur-thin)) saturate(var(--material-saturate));
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .pt-table thead th {
        background-color: var(--material-bg-solid) !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

.pt-table tbody td {
    padding: var(--space-3) var(--space-4);
    border-bottom: 1px solid var(--border-color);
    color: var(--text-primary);
    vertical-align: middle;
}

.pt-table tbody tr:last-child td {
    border-bottom: none;
}

.pt-table tbody tr {
    transition: background var(--dur-fast);
}

.pt-table tbody tr:hover td {
    background: var(--accent-light);
}

.pt-table td.num,
.pt-table th.num {
    text-align: right;
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

/* Coluna de ações — não deve encolher nem quebrar linha quando a
   tabela é rolada horizontalmente */
.pt-table td.actions,
.pt-table th.actions {
    white-space: nowrap;
    text-align: right;
}

/* ══════════════════════════════════════════════════════════════════
   BADGES / STATUS — canônico .pt-badge, com os nomes legados
   agrupados na mesma regra (uma única definição para todos).
   ══════════════════════════════════════════════════════════════════ */
.pt-badge,
.status-pill {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 4px 10px;
    border-radius: var(--radius-full);
    font-size: var(--text-xs);
    font-weight: var(--fw-semibold);
    white-space: nowrap;
}

.pt-badge::before,
.status-pill::before {
    content: '';
    width: 6px;
    height: 6px;
    border-radius: 50%;
    background: currentColor;
    flex-shrink: 0;
}

.pt-badge--success, .badge-success, .badge-active, .badge-concluido,
.conf-badge.alta, .dc-badge.credito {
    background: var(--emerald-light);
    color: var(--emerald);
}

.pt-badge--warning, .badge-warning, .badge-media, .badge-em-andamento, .badge-em-revisao,
.conf-badge.media {
    background: var(--amber-light);
    color: var(--amber);
}

.pt-badge--danger, .badge-danger, .badge-inactive, .badge-urgente, .badge-alta, .badge-arquivado,
.conf-badge.baixa, .dc-badge.despesa {
    background: var(--rose-light);
    color: var(--rose);
}

.pt-badge--info, .badge-nao-iniciado {
    background: var(--cyan-light);
    color: var(--cyan);
}

.pt-badge--neutral, .badge-muted, .badge-baixa {
    background: var(--bg-tertiary);
    color: var(--text-secondary);
}

.pt-badge--neutral::before {
    background: var(--text-tertiary);
}

/* Status pills — cores ORIGINAIS do produto (restauradas a pedido),
   independentes dos tokens --emerald/--amber/--rose acima. */
.status-ok { background: var(--emerald-light); color: #059669; }
.status-ok::before { background: #10b981; }

.status-pending { background: var(--amber-light); color: #d97706; }
.status-pending::before { background: #f59e0b; }

.status-fail { background: var(--rose-light); color: #e11d48; }
.status-fail::before { background: #f43f5e; }

.status-neutral { background: rgba(100, 116, 139, .1); color: #64748b; }
.status-neutral::before { background: #94a3b8; }

.status-warn { background: rgba(234, 179, 8, .1); color: #ca8a04; }
.status-warn::before { background: #eab308; }

/* Meios de pagamento — mesma família visual dos badges, sem o ponto */
.type-pix {
    background: var(--cyan-light);
    color: var(--cyan);
    font-size: var(--text-xs);
    padding: 3px 9px;
    border-radius: var(--radius-sm);
    font-weight: var(--fw-semibold);
}

.type-boleto {
    background: var(--accent-light);
    color: var(--accent);
    font-size: var(--text-xs);
    padding: 3px 9px;
    border-radius: var(--radius-sm);
    font-weight: var(--fw-semibold);
}

.type-credit_card,
.type-cartão {
    background: rgba(168, 85, 247, .1);
    color: #a855f7;
    font-size: var(--text-xs);
    padding: 3px 9px;
    border-radius: var(--radius-sm);
    font-weight: var(--fw-semibold);
}

/* ══════════════════════════════════════════════════════════════════
   SKELETON — estado de carregamento (auditoria: só 5 ocorrências
   soltas de "skeleton" no projeto inteiro; nenhuma padronizada)
   ══════════════════════════════════════════════════════════════════ */
.pt-skeleton {
    position: relative;
    overflow: hidden;
    background: var(--bg-tertiary);
    border-radius: var(--radius-md);
    color: transparent !important;
}

.pt-skeleton::after {
    content: '';
    position: absolute;
    inset: 0;
    transform: translateX(-100%);
    background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--text-primary) 6%, transparent), transparent);
}

@media (prefers-reduced-motion: no-preference) {
    .pt-skeleton::after {
        animation: pt-skeleton-sweep 1.4s ease-in-out infinite;
    }
}

@keyframes pt-skeleton-sweep {
    100% {
        transform: translateX(100%);
    }
}

.pt-skeleton-text {
    height: .85em;
    margin: .25em 0;
}

.pt-skeleton-row {
    height: 44px;
    margin-bottom: var(--space-2);
}

/* ══════════════════════════════════════════════════════════════════
   FORMULÁRIOS
   ══════════════════════════════════════════════════════════════════ */
.pt-form-group {
    margin-bottom: var(--space-4);
}

.pt-form-label {
    font-size: var(--text-sm);
    font-weight: var(--fw-medium);
    color: var(--text-primary);
    margin-bottom: 5px;
    display: block;
}

.pt-form-label .required {
    color: var(--rose);
}

.pt-form-control,
.form-control,
.form-select {
    width: 100%;
    padding: 10px var(--space-3);
    background-color: var(--bg-secondary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
    font-size: var(--text-md);
    color: var(--text-primary);
    transition: border-color var(--dur-fast), box-shadow var(--dur-fast);
    font-family: var(--font-sans);
    min-height: var(--tap-target-min);
}

.pt-form-control:focus,
.form-control:focus,
.form-select:focus {
    outline: none;
    border-color: var(--accent);
    box-shadow: 0 0 0 3px var(--accent-ring);
    background-color: var(--bg-secondary);
    color: var(--text-primary);
}

.pt-form-select,
.form-select {
    appearance: none;
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='12' viewBox='0 0 24 24' fill='none' stroke='%2371717A' stroke-width='2'%3E%3Cpolyline points='6 9 12 15 18 9'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right 12px center;
    padding-right: 34px;
}

.pt-form-hint {
    font-size: var(--text-xs);
    color: var(--text-secondary);
    margin-top: 4px;
}

/* Estado de erro — antes só 7 ocorrências de is-invalid em todo o
   projeto para 259 inputs. Padrão único: borda + texto abaixo do
   campo, ligado por aria-describedby (ver Fase 4). */
.pt-form-control.is-invalid,
.form-control.is-invalid,
.form-select.is-invalid {
    border-color: var(--rose);
}

.pt-form-control.is-invalid:focus,
.form-control.is-invalid:focus {
    box-shadow: 0 0 0 3px color-mix(in srgb, var(--rose) 18%, transparent);
}

.pt-form-error,
.invalid-feedback {
    display: block;
    font-size: var(--text-xs);
    color: var(--rose);
    margin-top: 4px;
    font-weight: var(--fw-medium);
}

/* ══════════════════════════════════════════════════════════════════
   BOTÕES
   ══════════════════════════════════════════════════════════════════ */
/* .btn-primary/.btn-danger seguram !important de propósito: são o
   destino de reconciliação de TODO botão emitido por Python/Bootstrap
   em ~50 templates, muitos com <style> local próprio carregado DEPOIS
   deste arquivo (via {% block extra_css %}) — sem !important, um CSS
   de página ganha por ordem de origem e o botão sai da paleta
   canônica. Não removido nesta fase: risco de regressão maior que o
   ganho de remover 2 !important que já têm justificativa documentada. */
.pt-btn,
.btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 7px;
    padding: 10px var(--space-5);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    font-weight: var(--fw-semibold);
    cursor: pointer;
    border: none;
    transition: background var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease), box-shadow var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease);
    text-decoration: none;
    font-family: var(--font-sans);
    min-height: var(--tap-target-min);
}

/* Resposta tátil HIG — "botão pressiona levemente" (spec §67). Não em
   .is-loading (pointer-events:none já torna irrelevante) nem quando o
   usuário pede menos movimento. */
@media (prefers-reduced-motion: no-preference) {
    .pt-btn:not(.is-loading):active,
    .btn:not(.is-loading):active {
        transform: scale(.97);
    }
}

.pt-btn-primary,
.btn-primary {
    background: var(--accent) !important;
    border-color: var(--accent) !important;
    color: #fff !important;
}

.pt-btn-primary:hover,
.btn-primary:hover {
    background: var(--accent-hover) !important;
    border-color: var(--accent-hover) !important;
    color: #fff !important;
    box-shadow: var(--shadow-sm);
}

/* P2: botão secundário vira vidro em cápsula — tier regular. `background`/
   `border` próprios saíram (o material supre); a cápsula (`radius-full`)
   é a forma de controle padrão da Apple. Estado de :hover é fill, não
   material (evita empilhar vidro sobre vidro no próprio hover). */
.pt-btn-outline {
    border-radius: var(--radius-full);
    color: var(--text-secondary);
    background: var(--material-bg-solid);
    border: 1px solid var(--material-border);
    background-image: linear-gradient(to bottom, var(--material-highlight), transparent 40%);
    background-blend-mode: overlay;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .pt-btn-outline {
        background-color: var(--material-bg);
        backdrop-filter: blur(var(--material-blur-regular)) saturate(var(--material-saturate));
        -webkit-backdrop-filter: blur(var(--material-blur-regular)) saturate(var(--material-saturate));
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .pt-btn-outline {
        background-color: var(--material-bg-solid) !important;
        background-image: none !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

.pt-btn-outline:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: var(--accent-light) !important;
    backdrop-filter: none !important;
    -webkit-backdrop-filter: none !important;
}

.pt-btn-danger,
.btn-danger {
    /* --rose-solid, não --rose: --rose varia por tema pra funcionar como
       TEXTO (ver design-tokens.css) e falha o contraste AA com texto
       branco em cima (2.77:1 a 3.76:1, precisa 4.5:1) — mesmo bug do
       botão primário, achado revisando o "azul claro ilegível". */
    background: var(--rose-solid) !important;
    border-color: var(--rose-solid) !important;
    color: #fff !important;
}

.pt-btn-danger:hover,
.btn-danger:hover {
    filter: brightness(.92);
    box-shadow: var(--shadow-sm);
}

.pt-btn-sm {
    padding: 7px 14px;
    font-size: var(--text-xs);
    min-height: 32px;
    border-radius: var(--radius-sm);
}

/* Botão terciário — cápsula sem material persistente (Apple "plain":
   nenhum chrome em repouso, só tinta ao passar o mouse/tocar). */
.pt-btn-ghost {
    background: none;
    border: none;
    color: var(--text-secondary);
    padding: 6px 10px;
    min-height: 32px;
    border-radius: var(--radius-full);
    cursor: pointer;
}

.pt-btn-ghost:hover {
    background: var(--accent-light);
    color: var(--accent);
}

/* Estado de carregamento — impede duplo POST em telas de cobrança e
   transferência (auditoria: 0 submits tinham esse estado). Ativado
   por pt-ui.js em qualquer <form> com data-pt-loading no submit. */
.pt-btn.is-loading,
.btn.is-loading {
    position: relative;
    color: transparent !important;
    pointer-events: none;
}

.pt-btn.is-loading::after,
.btn.is-loading::after {
    content: '';
    position: absolute;
    width: 16px;
    height: 16px;
    border: 2px solid currentColor;
    border-top-color: transparent;
    border-radius: 50%;
    color: #fff;
    animation: pt-spin .6s linear infinite;
}

@keyframes pt-spin {
    to {
        transform: rotate(360deg);
    }
}

/* ══════════════════════════════════════════════════════════════════
   ALERTAS
   ══════════════════════════════════════════════════════════════════ */
.pt-alert,
.alert {
    padding: var(--space-3) var(--space-4);
    border-radius: var(--radius-md);
    margin-bottom: var(--space-4);
    font-size: var(--text-sm);
    display: flex;
    align-items: flex-start;
    gap: 10px;
    background-color: var(--bg-secondary);
    border: 1px solid var(--border-color);
    color: var(--text-primary);
}

.pt-alert-success, .alert-success {
    background: var(--emerald-light);
    color: var(--emerald);
    border: 1px solid color-mix(in srgb, var(--emerald) 30%, transparent);
}

.pt-alert-error, .alert-danger {
    background: var(--rose-light);
    color: var(--rose);
    border: 1px solid color-mix(in srgb, var(--rose) 30%, transparent);
}

.pt-alert-info, .alert-info {
    background: var(--accent-light);
    color: var(--accent);
    border: 1px solid color-mix(in srgb, var(--accent) 30%, transparent);
}

.pt-alert-warning, .alert-warning {
    background: var(--amber-light);
    color: var(--amber);
    border: 1px solid color-mix(in srgb, var(--amber) 30%, transparent);
}

.pt-alert-actions {
    margin-left: auto;
    flex-shrink: 0;
}

/* ══════════════════════════════════════════════════════════════════
   CABEÇALHO DE PÁGINA / BREADCRUMB / PAGINAÇÃO / FILTROS / VAZIO
   ══════════════════════════════════════════════════════════════════ */
/* P2: vira vidro — "cabeçalho de página" na tabela do plano, tier
   thick (maior superfície de vidro da tela). Antes era só texto sem
   caixa; ganha padding pra virar superfície, e fundo/blur/borda vêm
   do material — nunca `background` customizado aqui (Apple: interfere
   no Liquid Glass e no scroll edge effect). Assenta sobre o fundo
   ambiente de `body::before` (design-system.css), que é o que dá algo
   pra refratar — `.portal-content`/`.content-wrapper` não têm fundo
   próprio de propósito. */
.pt-page-header {
    margin-bottom: var(--space-5);
    padding: var(--space-4) var(--space-5);
    border-radius: var(--radius-lg);
    background: var(--material-bg-solid);
    border: 1px solid var(--material-border);
    background-image: linear-gradient(to bottom, var(--material-highlight), transparent 40%);
    background-blend-mode: overlay;
    box-shadow: var(--shadow-md);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .pt-page-header {
        background-color: var(--material-bg);
        backdrop-filter: blur(var(--material-blur-thick)) saturate(var(--material-saturate));
        -webkit-backdrop-filter: blur(var(--material-blur-thick)) saturate(var(--material-saturate));
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .pt-page-header {
        background-color: var(--material-bg-solid) !important;
        background-image: none !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

.pt-page-header h1 {
    font-size: var(--text-xl);
    font-weight: var(--fw-bold);
    margin: 0 0 4px;
    letter-spacing: var(--tracking-tight);
}

.pt-page-header p {
    font-size: var(--text-sm);
    color: var(--text-secondary);
    margin: 0;
}

/* Título de página curto — usado quando o `<h1>` fica sozinho num wrapper
   ao lado de um botão de ação (ver .pt-page-header--action logo abaixo),
   em vez de ser filho direto de `.pt-page-header`. Sem esta regra, o `<h1>`
   caía no Reboot do Bootstrap (carregado ANTES deste arquivo em base.html
   e nunca resetado): margin-bottom:.5rem + font-size ~32px, descasando do
   botão ao lado (09/09/2026 — crm/cliente/detalhes.html, crm/proposta/detalhes.html). */
.pt-page-title {
    font-size: var(--text-xl);
    font-weight: var(--fw-bold);
    margin: 0;
    letter-spacing: var(--tracking-tight);
}

/* Variante de `.pt-page-header` com um botão de ação ao lado do título —
   nunca existiu, então 16 telas vinham improvisando com `style=` inline em
   3 formas incompatíveis (align-items:center / :flex-start / nenhum flex,
   este último deixando o botão quebrar pra linha de baixo). Agrupar
   breadcrumb+título num wrapper e o botão como irmão. */
.pt-page-header--action {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-4);
    flex-wrap: wrap;
}

/* Título de seção dentro de um `.pt-card-header` (ícone + texto) — mesma
   regra que home/dashboard.html já definia localmente, promovida aqui pra
   as outras telas que usam a classe (crm/cliente/detalhes.html,
   crm/proposta/detalhes.html) pararem de cair no `<h2>` cru do Bootstrap. */
.section-title {
    display: flex;
    align-items: center;
    gap: 8px;
    margin: 0;
    font-size: var(--text-lg);
    font-weight: var(--fw-bold);
}

.pt-breadcrumb {
    font-size: var(--text-sm);
    color: var(--text-secondary);
    margin-bottom: 6px;
}

.pt-breadcrumb a {
    color: var(--accent);
    text-decoration: none;
}

.pt-breadcrumb a:hover {
    text-decoration: underline;
}

.pt-pagination {
    display: flex;
    align-items: center;
    gap: 6px;
    justify-content: center;
    margin-top: var(--space-5);
}

.pt-pagination a,
.pt-pagination span {
    padding: 7px 14px;
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    font-weight: var(--fw-medium);
    border: 1px solid var(--border-color);
    text-decoration: none;
    color: var(--text-secondary);
    transition: all var(--dur-fast);
    min-height: 36px;
    display: inline-flex;
    align-items: center;
}

.pt-pagination a:hover {
    border-color: var(--accent);
    color: var(--accent);
    background: var(--accent-light);
}

.pt-pagination .current {
    background: var(--accent);
    color: #fff;
    border-color: var(--accent);
}

/* P2: vira vidro — barra de filtro, tier regular. `background`/`border`
   próprios saíram (mesmo motivo do cabeçalho de página, acima). */
.pt-filters {
    display: flex;
    gap: var(--space-3);
    align-items: flex-end;
    flex-wrap: wrap;
    margin-bottom: var(--space-5);
    padding: var(--space-4) var(--space-5);
    border-radius: var(--radius-lg);
    background: var(--material-bg-solid);
    border: 1px solid var(--material-border);
    background-image: linear-gradient(to bottom, var(--material-highlight), transparent 40%);
    background-blend-mode: overlay;
    box-shadow: var(--material-shadow-over-plain);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .pt-filters {
        background-color: var(--material-bg);
        backdrop-filter: blur(var(--material-blur-regular)) saturate(var(--material-saturate));
        -webkit-backdrop-filter: blur(var(--material-blur-regular)) saturate(var(--material-saturate));
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .pt-filters {
        background-color: var(--material-bg-solid) !important;
        background-image: none !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

.pt-filter-group {
    display: flex;
    flex-direction: column;
    gap: 4px;
}

.pt-filter-label {
    font-family: var(--font-accent);     /* rótulo/detalhe (Gotham via --font-accent) */
    font-size: var(--text-xs);
    font-weight: var(--fw-semibold);
    color: var(--text-secondary);
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
}

.pt-empty {
    text-align: center;
    padding: var(--space-7) var(--space-5);
    color: var(--text-secondary);
}

.pt-empty i {
    font-size: 2.5rem;
    display: block;
    margin-bottom: var(--space-3);
    opacity: .3;
}

.pt-empty p {
    font-size: var(--text-md);
    margin: 0 0 6px;
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
}

.pt-empty small {
    font-size: var(--text-sm);
}

/* ══════════════════════════════════════════════════════════════════
   LINHA DE LISTA — canônico .pt-list-row. Promovido do Início
   (era `.client-alert`, local ao template e aplicado a TODA linha —
   inclusive as não atrasadas, que saíam rosa por engano). Genérico o
   bastante pra qualquer lista de "item + rótulo + ação" (Minhas
   Tarefas, Minha Semana, e o que mais precisar do mesmo formato).
   `--warn` é a única variação de cor: destaque vermelho pra atrasado/
   urgente, texto padrão pro resto.
   ══════════════════════════════════════════════════════════════════ */
.pt-list-row {
    background: var(--bg-tertiary, var(--bg-secondary));
    border-radius: var(--radius-md);
    padding: var(--space-3) var(--space-4);
    margin-bottom: 10px;
    border: 1px solid var(--border-color);
    display: flex;
    align-items: center;
    gap: var(--space-3);
}

.pt-list-row:last-child {
    margin-bottom: 0;
}

.pt-list-row--warn {
    background: var(--rose-light);
    border-color: color-mix(in srgb, var(--rose) 30%, transparent);
}

/* ══════════════════════════════════════════════════════════════════
   LISTA/CARD REDUZÍVEL — dois eixos independentes no mesmo componente
   (Fase 6 do plano de reforma de UX, 08/09/2026 — "Minhas Tarefas" do
   Início empilhava até 30 itens sem corte nem scroll):

     1. TETO de N itens — [data-max-items="N"] esconde o resto da
        lista via :nth-child PURO, não classe aplicada por JS: o corte
        já vale no primeiro paint (SSR ou injeção JS, tanto faz — CSS
        não distingue), sem a lista longa "piscar" inteira antes de
        encolher. N é um conjunto FECHADO (5/6/8/10) — adicionar um
        valor novo é adicionar uma regra aqui, de propósito: um
        seletor por N documentado bate melhor que inventar `attr()`
        dentro de :nth-child (que os navegadores não suportam).
        `.is-expanded` (JS, botão "Mostrar mais") desliga a regra.

     2. RECOLHER o card inteiro — [data-collapsible] + clique no botão
        [data-collapsible-toggle] soma/tira `.is-collapsed`, que
        esconde [data-collapsible-body] inteiro (sobra só o cabeçalho).
        Independente do teto — um card pode estar recolhido E truncado
        ao mesmo tempo.

   Ver initCollapsibles() em pt-ui.js. Markup de referência em
   home/dashboard.html (Minhas Tarefas: os dois eixos juntos) e
   calendario/calendario_financeiro.html (só recolher no calendário,
   recolher+teto na lista de lançamentos, que é montada em JS).
   ══════════════════════════════════════════════════════════════════ */
/* Sem `display` no container de `[data-collapsible-list]` de propósito:
   ele pode ser um <div> (Início, CRM) OU um <tbody> (tabelas de Projetos)
   — forçar `display: block` quebraria o algoritmo de layout de tabela do
   <tbody> (colunas do cabeçalho desalinhariam do corpo). Cada elemento
   mantém o `display` que já tinha por padrão. */
[data-max-items="5"]:not(.is-expanded) [data-collapsible-list] > *:nth-child(n+6),
[data-max-items="6"]:not(.is-expanded) [data-collapsible-list] > *:nth-child(n+7),
[data-max-items="8"]:not(.is-expanded) [data-collapsible-list] > *:nth-child(n+9),
[data-max-items="10"]:not(.is-expanded) [data-collapsible-list] > *:nth-child(n+11) {
    display: none;
}

/* Sem `display` aqui de propósito — o JS (initMaxItemsCard, pt-ui.js)
   controla visibilidade via o atributo `hidden`, e um `display: block`
   author-level venceria a regra `[hidden]` do user-agent (mesma
   especificidade, mas cascade de origem author > UA). <button> já é
   inline-block o bastante pra `width: 100%` funcionar sem isso. */
[data-collapsible-more] {
    width: 100%;
    margin-top: var(--space-2);
    background: none;
    border: none;
    border-top: 1px dashed var(--border-color);
    padding-top: var(--space-3);
    color: var(--accent);
    font-size: var(--text-sm);
    font-weight: var(--fw-medium);
    cursor: pointer;
    text-align: center;
}

[data-collapsible-more]:hover {
    text-decoration: underline;
}

.pt-card-actions {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    flex-shrink: 0;
}

.pt-collapse-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    border-radius: var(--radius-sm);
    border: 1px solid var(--border-color);
    background: none;
    color: var(--text-secondary);
    cursor: pointer;
    transition: transform var(--dur-base) var(--ease), background var(--dur-fast);
}

.pt-collapse-toggle:hover {
    background: var(--bg-tertiary, var(--bg-secondary));
    color: var(--text-primary);
}

/* Aponta pra baixo quando ABERTO (convenção oposta à do chevron da
   sidebar, de propósito: aqui o ícone descreve "recolher" — a seta
   aponta pro sentido em que o conteúdo vai sumir). Gira ao recolher. */
[data-collapsible].is-collapsed .pt-collapse-toggle {
    transform: rotate(-90deg);
}

[data-collapsible].is-collapsed [data-collapsible-body] {
    display: none;
}

/* ══════════════════════════════════════════════════════════════════
   PROGRESSO / STATUS DE PROJETO — canônico .pt-progress-bar e
   .pt-status-dot. Promovido de `projetos/lista.html` (onde viviam como
   `.proj-progress-bar`/`.proj-status-dot`, local ao template) pra ser
   reaproveitado por qualquer card de projeto fora daquela página — ex.
   o resumo de "Meus Projetos" no Início.
   ══════════════════════════════════════════════════════════════════ */
.pt-progress-bar {
    height: 3px;
    border-radius: 2px;
    background: var(--border-color);
    overflow: hidden;
}

.pt-progress-fill {
    height: 100%;
    border-radius: 2px;
    background: var(--accent);
    transition: width .4s ease;
}

.pt-status-dot {
    width: 7px;
    height: 7px;
    border-radius: 50%;
    display: inline-block;
    flex-shrink: 0;
}

.pt-dot-nao-iniciado { background: var(--cyan); }
.pt-dot-em-andamento { background: var(--amber); }
.pt-dot-em-revisao   { background: #8b5cf6; }
.pt-dot-atrasado     { background: var(--rose); }
.pt-dot-concluido    { background: var(--emerald); }
.pt-dot-arquivado    { background: var(--border-color); }

/* ══════════════════════════════════════════════════════════════════
   TOAST — substitui os 25 alert()/confirm() nativos do browser
   encontrados na auditoria. Comportamento em pt-ui.js (ptToast()).
   ══════════════════════════════════════════════════════════════════ */
.pt-toast-region {
    position: fixed;
    bottom: var(--space-5);
    right: var(--space-5);
    z-index: var(--z-toast);
    display: flex;
    flex-direction: column;
    gap: var(--space-2);
    max-width: min(360px, calc(100vw - 2 * var(--space-4)));
}

@media (max-width: 640px) {
    .pt-toast-region {
        left: var(--space-3);
        right: var(--space-3);
        bottom: var(--space-3);
        max-width: none;
    }
}

/* P2: vira vidro — toast, tier regular. `background`/`border` (exceto
   a tarja lateral colorida, que fica por cima do material) saíram. */
.pt-toast {
    border-left: 3px solid var(--accent);
    border-radius: var(--radius-md);
    background: var(--material-bg-solid);
    border-top: 1px solid var(--material-border);
    border-right: 1px solid var(--material-border);
    border-bottom: 1px solid var(--material-border);
    background-image: linear-gradient(to bottom, var(--material-highlight), transparent 40%);
    background-blend-mode: overlay;
    box-shadow: var(--material-shadow-over-content);
    padding: var(--space-3) var(--space-4);
    display: flex;
    align-items: flex-start;
    gap: 10px;
    font-size: var(--text-sm);
    color: var(--text-primary);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .pt-toast {
        background-color: var(--material-bg);
        backdrop-filter: blur(var(--material-blur-regular)) saturate(var(--material-saturate));
        -webkit-backdrop-filter: blur(var(--material-blur-regular)) saturate(var(--material-saturate));
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .pt-toast {
        background-color: var(--material-bg-solid) !important;
        background-image: none !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

.pt-toast--success {
    border-left-color: var(--emerald);
}

.pt-toast--error {
    border-left-color: var(--rose);
}

.pt-toast--warning {
    border-left-color: var(--amber);
}

/* Sucesso e erro: fundo SÓLIDO, não material/vidro — única exceção
   deliberada à regra do produto. O material translúcido com só a tarja
   de 3px acima (ainda usado por warning/info) lia como "quase
   transparente, texto branco sumindo" — exatamente o relatado no
   Calendário Financeiro. Estado (deu certo / deu errado) precisa ser
   lido de relance, não examinado. `-solid` (não `--emerald`/`--rose`
   direto) porque essas variam por tema pra funcionar como TEXTO — como
   FUNDO com texto branco em cima elas ficam claras/saturadas demais,
   sobretudo no escuro (mesmo motivo de --rose-solid em botão de perigo,
   acima). Os dois passam >=4.5:1 contra branco nos 3 temas
   (scripts/check_contrast.py). */
.pt-toast.pt-toast--success,
.pt-toast.pt-toast--error {
    background: var(--emerald-solid);
    background-image: none;
    border: none;
    color: #fff;
}

.pt-toast.pt-toast--error {
    background: var(--rose-solid);
}

.pt-toast i {
    margin-top: 2px;
}

.pt-toast--warning i {
    color: var(--amber);
}

/* Ícone e botão de fechar herdam branco do fundo sólido acima — não
   var(--emerald)/var(--rose), que ficariam invisíveis sobre eles mesmos. */
.pt-toast--success i,
.pt-toast--success .pt-toast-close,
.pt-toast--error i,
.pt-toast--error .pt-toast-close {
    color: #fff;
}

.pt-toast--success .pt-toast-close:hover,
.pt-toast--error .pt-toast-close:hover {
    color: #fff;
    opacity: .75;
}

.pt-toast-close {
    margin-left: auto;
    background: none;
    border: none;
    color: var(--text-tertiary);
    cursor: pointer;
    padding: 2px;
    line-height: 1;
}

.pt-toast-close:hover {
    color: var(--text-primary);
}

@media (prefers-reduced-motion: no-preference) {
    .pt-toast {
        animation: pt-toast-in var(--dur-slow) var(--ease) both;
    }
}

@keyframes pt-toast-in {
    from {
        opacity: 0;
        transform: translateY(8px);
    }

    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* ── Confirm dialog — substitui confirm() nativo (ptConfirm()) ────
   Material duplicado direto na regra (não via classe .material-scrim/
   -sheet): backdrop e dialog são criados por pt-ui.js com className
   fixo em string — mexer nisso por um ganho cosmético entraria em JS
   à toa. Backdrop é pai direto do dialog (confirmado no JS:
   backdrop.appendChild(dialog) + document.body.appendChild(backdrop)),
   então backdrop-filter aqui NÃO é o padrão perigoso de ancestral de
   modal preso — o filho sempre pinta por cima do próprio pai. */
.pt-confirm-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(9, 9, 11, .5);
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .pt-confirm-backdrop {
        background: rgba(9, 9, 11, .32);
        backdrop-filter: blur(8px);
        -webkit-backdrop-filter: blur(8px);
    }
}

.pt-confirm-dialog {
    background: var(--material-bg-solid);
    border: 1px solid var(--material-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--material-shadow-over-content);
    background-image: linear-gradient(to bottom, var(--material-highlight), transparent 40%);
    background-blend-mode: overlay;
    max-width: 400px;
    width: 100%;
    padding: var(--space-5);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .pt-confirm-dialog {
        background-color: var(--material-bg);
        backdrop-filter: blur(var(--material-blur)) saturate(var(--material-saturate));
        -webkit-backdrop-filter: blur(var(--material-blur)) saturate(var(--material-saturate));
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .pt-confirm-backdrop {
        background: rgba(9, 9, 11, .72) !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }

    .pt-confirm-dialog {
        background-color: var(--material-bg-solid) !important;
        background-image: none !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

.pt-confirm-dialog h3 {
    font-size: var(--text-md);
    margin-bottom: var(--space-2);
}

.pt-confirm-dialog p {
    font-size: var(--text-sm);
    color: var(--text-secondary);
    margin-bottom: var(--space-4);
}

.pt-confirm-actions {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
}

/* ══════════════════════════════════════════════════════════════════
   MODAL DE FORMULÁRIO — pt-ui.js → PtUI.formDialog()

   Irmão do .pt-confirm-* acima: aquele faz uma pergunta de sim/não,
   este COLETA dados. Existe porque window.prompt() (a) não estiliza,
   (b) aceita um único campo de texto e (c) não mostra de onde a ação
   partiu — e o momento em que o usuário cadastra uma categoria é
   justamente no meio da revisão de uma fatura, olhando UMA linha.
   Daí o .pt-modal-context: o modal repete a linha de origem, que é o
   que torna óbvio o nome, a frente e o tipo a escolher.
   ══════════════════════════════════════════════════════════════════ */
/* Mesma nota do .pt-confirm-backdrop acima: backdrop é pai direto do
   modal (pt-ui.js: backdrop.appendChild(modal)), então backdrop-filter
   aqui é seguro — não é ancestral externo prendendo modal atrás do
   próprio backdrop. */
.pt-modal-backdrop {
    position: fixed;
    inset: 0;
    background: rgba(9, 9, 11, .55);
    z-index: var(--z-modal);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: var(--space-4);
    animation: pt-modal-fade var(--dur-base) var(--ease);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .pt-modal-backdrop {
        background: rgba(9, 9, 11, .34);
        backdrop-filter: blur(8px);
        -webkit-backdrop-filter: blur(8px);
    }
}

.pt-modal {
    background: var(--material-bg-solid);
    border: 1px solid var(--material-border);
    border-radius: var(--radius-lg);
    box-shadow: var(--material-shadow-over-content);
    background-image: linear-gradient(to bottom, var(--material-highlight), transparent 40%);
    background-blend-mode: overlay;
    width: 100%;
    max-width: 440px;
    max-height: calc(100vh - var(--space-6));
    display: flex;
    flex-direction: column;
    animation: pt-modal-rise var(--dur-base) var(--ease);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .pt-modal {
        background-color: var(--material-bg);
        backdrop-filter: blur(var(--material-blur)) saturate(var(--material-saturate));
        -webkit-backdrop-filter: blur(var(--material-blur)) saturate(var(--material-saturate));
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .pt-modal-backdrop {
        background: rgba(9, 9, 11, .72) !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }

    .pt-modal {
        background-color: var(--material-bg-solid) !important;
        background-image: none !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

@keyframes pt-modal-fade {
    from { opacity: 0; }
}

@keyframes pt-modal-rise {
    from { opacity: 0; transform: translateY(10px) scale(.985); }
}

@media (prefers-reduced-motion: reduce) {
    .pt-modal-backdrop,
    .pt-modal { animation: none; }
}

.pt-modal-header {
    display: flex;
    align-items: center;
    gap: var(--space-3);
    padding: var(--space-4) var(--space-5);
    border-bottom: 1px solid var(--border-color);
}

.pt-modal-header .pt-modal-icon {
    width: 30px;
    height: 30px;
    flex: none;
    border-radius: var(--radius-md);
    display: grid;
    place-items: center;
    background: var(--accent-light);
    color: var(--accent);
    font-size: var(--text-sm);
}

.pt-modal-header h3 {
    font-size: var(--text-md);
    margin: 0;
    flex: 1;
}

.pt-modal-close {
    background: none;
    border: none;
    color: var(--text-tertiary);
    cursor: pointer;
    padding: var(--space-1);
    line-height: 1;
    border-radius: var(--radius-sm);
}

.pt-modal-close:hover {
    color: var(--text-primary);
    background: var(--bg-tertiary);
}

.pt-modal-body {
    padding: var(--space-5);
    overflow-y: auto;
}

/* `:last-child` sem o `>` alcançaria também o último campo DENTRO da
   grade de duas colunas, deixando as duas células com margens
   diferentes — e a linha do grid herda a maior, virando um vão morto
   acima do rodapé. */
.pt-modal-body > *:last-child {
    margin-bottom: 0;
}

.pt-modal-footer {
    display: flex;
    justify-content: flex-end;
    gap: var(--space-2);
    padding: var(--space-3) var(--space-5) var(--space-4);
    border-top: 1px solid var(--border-color);
}

/* ── Bloco de contexto (a linha de onde a ação partiu) ─────────── */
.pt-modal-context {
    display: grid;
    grid-template-columns: 1fr auto;
    gap: 2px var(--space-3);
    align-items: baseline;
    padding: var(--space-3);
    margin-bottom: var(--space-5);
    background: var(--bg-tertiary);
    border: 1px solid var(--border-color);
    border-radius: var(--radius-md);
}

.pt-modal-context .ctx-eyebrow {
    grid-column: 1 / -1;
    font-family: var(--font-accent);     /* eyebrow/detalhe (Gotham via --font-accent) */
    font-size: var(--text-xs);
    font-weight: var(--fw-semibold);
    text-transform: uppercase;
    letter-spacing: var(--tracking-caps);
    color: var(--text-tertiary);
}

.pt-modal-context .ctx-titulo {
    font-size: var(--text-sm);
    font-weight: var(--fw-semibold);
    color: var(--text-primary);
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.pt-modal-context .ctx-valor {
    font-family: var(--font-display);
    font-size: var(--text-sm);
    font-weight: var(--fw-bold);
    font-variant-numeric: tabular-nums;
    white-space: nowrap;
}

.pt-modal-context .ctx-tag {
    grid-column: 1 / -1;
    justify-self: start;
    margin-top: var(--space-1);
    font-size: var(--text-xs);
    font-weight: var(--fw-semibold);
    padding: 2px 8px;
    border-radius: var(--radius-full);
    background: var(--bg-secondary);
    color: var(--text-secondary);
}

.pt-modal-context .ctx-tag--negativo { background: var(--rose-light); color: var(--rose); }
.pt-modal-context .ctx-tag--positivo { background: var(--emerald-light); color: var(--emerald); }

/* ── Campo de texto com a cor escolhida como marcador ──────────── */
.pt-field-com-cor { position: relative; }

.pt-field-com-cor .pt-form-control { padding-left: 34px; }

.pt-field-com-cor .pt-field-ponto {
    position: absolute;
    left: 12px;
    top: 50%;
    transform: translateY(-50%);
    width: 12px;
    height: 12px;
    border-radius: var(--radius-full);
    pointer-events: none;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
    transition: background var(--dur-fast) var(--ease);
}

/* ── Paleta de cores ──────────────────────────────────────────── */
.pt-swatches {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: var(--space-3);
}

.pt-swatch {
    width: 24px;
    height: 24px;
    padding: 0;
    border-radius: var(--radius-full);
    border: 2px solid transparent;
    background-clip: padding-box;
    cursor: pointer;
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, .12);
    transition: transform var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
}

.pt-swatch:hover { transform: scale(1.15); }

.pt-swatch[aria-checked="true"] {
    border-color: var(--text-primary);
    transform: scale(1.15);
}

@media (prefers-reduced-motion: reduce) {
    .pt-swatch:hover,
    .pt-swatch[aria-checked="true"] { transform: none; }
}

/* ── Duas colunas dentro do modal ─────────────────────────────── */
.pt-modal-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: var(--space-4);
}

.pt-modal-grid > .pt-form-group {
    margin-bottom: 0;
}

/* Empilhado, o `gap` do próprio grid já é a separação entre os dois
   campos — a margem do .pt-form-group somaria em cima dele. */
@media (max-width: 520px) {
    .pt-modal-grid { grid-template-columns: 1fr; }
}

/* ── Erro devolvido pelo servidor, mostrado SEM fechar o modal ── */
.pt-modal-erro {
    display: flex;
    align-items: flex-start;
    gap: var(--space-2);
    margin-bottom: var(--space-4);
    padding: var(--space-2) var(--space-3);
    border-radius: var(--radius-md);
    background: var(--rose-light);
    color: var(--rose);
    font-size: var(--text-sm);
    font-weight: var(--fw-medium);
}

/* ══════════════════════════════════════════════════════════════════
   CONFIGURAÇÕES — layout de duas colunas com navegação lateral
   (substitui a coluna única de 1126 linhas sem âncora nenhuma).
   ══════════════════════════════════════════════════════════════════ */
.pt-settings-layout {
    display: grid;
    grid-template-columns: 220px 1fr;
    gap: var(--space-6);
    align-items: start;
}

@media (max-width: 900px) {
    .pt-settings-layout {
        grid-template-columns: 1fr;
    }
}

.pt-settings-nav {
    position: sticky;
    top: calc(56px + var(--space-4));
    display: flex;
    flex-direction: column;
    gap: 2px;
}

@media (max-width: 900px) {
    .pt-settings-nav {
        position: static;
        flex-direction: row;
        overflow-x: auto;
        gap: var(--space-2);
        padding-bottom: var(--space-2);
    }
}

.pt-settings-nav a {
    padding: 9px var(--space-3);
    border-radius: var(--radius-md);
    font-size: var(--text-sm);
    font-weight: var(--fw-medium);
    color: var(--text-secondary);
    text-decoration: none;
    white-space: nowrap;
    transition: all var(--dur-fast);
}

.pt-settings-nav a:hover {
    background: var(--bg-tertiary);
    color: var(--text-primary);
}

.pt-settings-nav a.active {
    background: var(--accent-light);
    color: var(--accent);
    font-weight: var(--fw-semibold);
}

.pt-settings-section {
    scroll-margin-top: calc(56px + var(--space-4));
    margin-bottom: var(--space-6);
}

.pt-settings-section h2 {
    font-size: var(--text-lg);
    margin-bottom: var(--space-4);
    display: flex;
    align-items: center;
    gap: 10px;
}

/* ── Escala de fonte (Configurações › Texto e Dash) ─────────────── */
.pt-scale-options {
    display: flex;
    flex-wrap: wrap;
    align-items: stretch;
    gap: var(--space-2);
}

/* .pt-scale-option é combinada com .pt-btn no HTML (mesmo elemento) —
   cada botão empilha .pt-scale-preview (Aa, altura variável por preset:
   26/28/34/42px via data-font-scale) sobre o rótulo. .pt-btn traz
   justify-content:center; com align-items:stretch no container todo
   botão já tem a MESMA altura externa (a do maior preview), mas centrar
   um bloco preview+rótulo de altura DIFERENTE dentro dessa altura igual
   desalinha os rótulos entre si (o de "Pequeno" sobra mais espaço acima
   que o de "Muito grande"). justify-content:flex-end (mesma
   especificidade de .pt-btn, declarada depois — vence na cascata) ancora
   os 4 blocos pela base, o que alinha os rótulos na mesma linha. */
.pt-scale-option {
    flex-direction: column;
    justify-content: flex-end;
    gap: 4px;
    min-width: 92px;
    padding: var(--space-3);
}

/* A amostra NÃO herda a escala do <html> — o atributo local
   data-font-scale no próprio <span> redefine os tokens só ali dentro
   (mesmo motivo pelo qual os blocos de preset em design-tokens.css usam
   [data-font-scale="..."] sem :root: especificidade igual, funciona
   aninhado). É o que deixa o botão "Grande" mostrar um "Aa" já grande
   mesmo com a página inteira renderizada em "médio". */
.pt-scale-preview {
    font-family: var(--font-title);
    font-size: var(--text-2xl);
    line-height: var(--leading-tight);
}

/* ══════════════════════════════════════════════════════════════════
   RECONCILIAÇÃO BOOTSTRAP — mesma aparência nos três temas para os
   componentes que continuam vindo do bootstrap.bundle.min.js
   (dropdown, modal, collapse).
   ══════════════════════════════════════════════════════════════════ */
/* .dropdown-menu vira .material-popover — verificado que nenhum dropdown
   deste produto vive dentro de .sidebar/.top-navbar (só em linha de
   tabela). EXCEÇÃO conhecida (P10): projetos/detalhe.html tem
   `.ws-bar .dropdown-menu` — .ws-bar é a barra de workspace, papel de
   navbar mas não é literalmente `.top-navbar` — com override local pra
   `.material-on-material` (não duplicar vidro). Se outro dropdown virar
   descendente de superfície de vidro, mesma receita. */
.dropdown-menu {
    background-color: var(--material-bg-solid);
    border: 1px solid var(--material-border);
    border-radius: var(--radius-md);
    box-shadow: var(--material-shadow-over-content);
    background-image: linear-gradient(to bottom, var(--material-highlight), transparent 40%);
    background-blend-mode: overlay;
    padding: 6px;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .dropdown-menu {
        background-color: var(--material-bg);
        backdrop-filter: blur(var(--material-blur)) saturate(var(--material-saturate));
        -webkit-backdrop-filter: blur(var(--material-blur)) saturate(var(--material-saturate));
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .dropdown-menu {
        background-color: var(--material-bg-solid) !important;
        background-image: none !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

.dropdown-item {
    color: var(--text-primary);
    font-size: var(--text-sm);
    border-radius: var(--radius-sm);
    padding: 8px 10px;
}

.dropdown-item:hover,
.dropdown-item:focus {
    background-color: var(--accent-light);
    color: var(--accent);
}

/* .modal-content vira .material-sheet; .modal-backdrop (criado pelo JS do
   Bootstrap, direto filho de <body> — mesma topologia segura do .pt-modal
   acima) ganha o mesmo scrim. */
.modal-content {
    background-color: var(--material-bg-solid);
    color: var(--text-primary);
    border: 1px solid var(--material-border);
    border-radius: var(--radius-lg);
    background-image: linear-gradient(to bottom, var(--material-highlight), transparent 40%);
    background-blend-mode: overlay;
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .modal-content {
        background-color: var(--material-bg);
        backdrop-filter: blur(var(--material-blur)) saturate(var(--material-saturate));
        -webkit-backdrop-filter: blur(var(--material-blur)) saturate(var(--material-saturate));
    }
}

.modal-backdrop.show {
    opacity: 1;
    background-color: rgba(9, 9, 11, .55);
}

@supports (backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px)) {
    .modal-backdrop.show {
        background-color: rgba(9, 9, 11, .34);
        backdrop-filter: blur(8px);
        -webkit-backdrop-filter: blur(8px);
    }
}

@media (prefers-reduced-transparency: reduce), (prefers-contrast: more) {
    .modal-content {
        background-color: var(--material-bg-solid) !important;
        background-image: none !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }

    .modal-backdrop.show {
        background-color: rgba(9, 9, 11, .72) !important;
        backdrop-filter: none !important;
        -webkit-backdrop-filter: none !important;
    }
}

.modal-header,
.modal-footer {
    border-color: var(--material-border);
}

.btn-close {
    filter: var(--btn-close-filter, none);
}

[data-theme="dark"] .btn-close {
    filter: invert(1) grayscale(100%) brightness(200%);
}

.text-dark { color: var(--text-primary) !important; }
.text-muted { color: var(--text-secondary) !important; }
.bg-white { background-color: var(--bg-secondary) !important; }
.bg-light { background-color: var(--bg-primary) !important; }
.border-dark { border-color: var(--border-color) !important; }
.shadow-sm { box-shadow: var(--card-shadow) !important; }

/* ── Tipografia — utilitários e citações ───────────────────────────
   --font-title em h1–h6; --font-accent (hoje alias de --font-sans,
   ver design-tokens.css) marca rótulo/eyebrow/deck. Opt-in por classe
   + citações. */
.font-title  { font-family: var(--font-title); font-synthesis: none; }
.font-accent { font-family: var(--font-accent); }

blockquote,
.pt-quote,
.pt-highlight {
    font-family: var(--font-accent);
    font-size: var(--text-md);
    line-height: var(--leading-snug);
    color: var(--text-primary);
}

blockquote {
    margin: var(--space-4) 0;
    padding-left: var(--space-4);
    border-left: 3px solid var(--accent);
    font-style: italic;
}

.card-header {
    background-color: var(--bg-secondary) !important;
    border-bottom-color: var(--border-color) !important;
    color: var(--text-primary);
}

.card-body {
    color: var(--text-primary);
}

.table-responsive {
    border-radius: var(--radius-lg);
}

.table {
    color: var(--text-primary);
    font-variant-numeric: tabular-nums;
}

.table > :not(caption) > * > * {
    background-color: var(--bg-secondary);
    color: var(--text-primary);
    border-bottom-color: var(--border-color);
}

/* Badges com utilitário de cor do Bootstrap (badge bg-primary/bg-success/
   bg-danger/bg-warning/bg-info/bg-secondary) — sem isto, mostravam o azul/
   verde/vermelho saturado padrão do Bootstrap 5, destoando da paleta
   contida do resto do produto. */
/* -solid, não a variável "crua": .badge herda color:#fff do Bootstrap, e
   --emerald/--amber/--rose/--cyan "crus" variam por tema pra funcionar como
   TEXTO — reprovam contraste com texto branco em cima (ver design-tokens.css). */
.badge.bg-primary { background-color: var(--accent) !important; }
.badge.bg-success { background-color: var(--emerald-solid) !important; }
.badge.bg-danger { background-color: var(--rose-solid) !important; }
.badge.bg-warning { background-color: var(--amber-solid) !important; color: #fff !important; }
.badge.bg-info { background-color: var(--cyan-solid) !important; }
.badge.bg-secondary { background-color: var(--text-tertiary) !important; }

/* Alvo de toque mínimo (WCAG 2.5.5) em controles pequenos comuns */
.btn-sm, .pt-btn-sm, .pt-btn-ghost, .sidebar-toggle, .theme-toggle {
    min-width: 32px;
}
