/**
 * Main stylesheet for ISP Dashboard
 * System Name: ISP Dashboard
 * Author: Lélio Sato
 * Primary Color: #0067b5
 */

:root {
    --primary-color: #0067b5;
    --primary-dark: #005494;
    --primary-light: #3385c6;
    --secondary-color: #6c757d;
    --success-color: #28a745;
    --danger-color: #dc3545;
    --warning-color: #ffc107;
    --info-color: #17a2b8;
    --light-bg: #f8f9fa;
    --dark-text: #212529;
    --sidebar-width: 225px;
    --sidebar-collapsed-width: 63px;
    --topbar-height: 54px;
    --page-header-height: 120px; /* fallback; JS sets this per-page via measurePageHeader() */

    /* Tabelas — grade e fundos das células CONGELADAS (position: sticky).
       Sempre OPACOS: qualquer transparência deixa o conteúdo rolado vazar.
       Redefinidos em body.dark-mode, então cada regra usa só a variável.       */
    --tbl-grid: #dee2e6;
    --tbl-frozen-bg: #ffffff;              /* tbody */
    --tbl-frozen-head-bg: var(--light-bg); /* thead / tfoot */
    --tbl-frozen-hover-bg: #e7f0ff;        /* = rgba(13,110,253,.10) sobre branco */
    --tbl-frozen-shadow: 2px 0 5px rgba(0, 0, 0, 0.12); /* separador do congelamento */
}

* {
    margin: 0;
    padding: 0;
    box-sizing: border-box;
}

body {
    /* Typography */
    font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
    font-size: 12.6px;
    line-height: 1.5;
    color: var(--dark-text);
    background-color: #f0f2f5;

    /* Flexbox Layout */
    display: flex;
    flex-direction: column;
    height: 100dvh;
    overflow: hidden !important;  /* CRÍTICO: previne scroll no body */
    margin: 0 !important;
    padding: 0 !important;
    overscroll-behavior-y: contain; /* impede pull-to-refresh no Android */
}

html {
    overflow: hidden !important;  /* CRÍTICO: previne scroll no html */
}

@media (max-width: 768px) {
    html, body {
        width: 100% !important;
        max-width: 100% !important;
        overflow-x: hidden !important;
    }
}

/* Topbar - FIXED (solução IA) */
.topbar {
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    height: var(--topbar-height);
    background: white;
    color: #333;
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 0 20px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    z-index: 200; /* Acima do sticky (150) */
}

.topbar-left {
    display: flex;
    align-items: center;
    gap: 13.5px;
}

/* Hamburger no TOPBAR — sempre visível no mobile/tablet para abrir a gaveta.
   (O ☰ dentro da sidebar sai da tela quando a gaveta está fechada, então não serve
   para ABRIR — só aparece no desktop e dentro da gaveta aberta.) Escondido no desktop. */
.topbar-menu-btn {
    display: none;
    background: transparent;
    border: none;
    color: #333;
    font-size: 22px;
    line-height: 1;
    cursor: pointer;
    padding: 6px 10px;
    border-radius: 4px;
}
.topbar-menu-btn:hover { background: rgba(0,0,0,0.06); }
body.dark-mode .topbar-menu-btn { color: var(--dm-text, #e0e0e0); }
body.dark-mode .topbar-menu-btn:hover { background: rgba(255,255,255,0.08); }
@media (max-width: 1199px) {
    .topbar-menu-btn { display: inline-flex; align-items: center; }
}

.topbar-logo {
    height: 36px;
}

.topbar-title {
    font-size: 18px;
    font-weight: 600;
    color: #333;
}

.topbar-right {
    display: flex;
    align-items: center;
    gap: 13.5px;
}

.user-menu {
    position: relative;
}

.user-menu-button {
    background: none;
    border: none;
    padding: 7.2px 15px;
    cursor: pointer;
    display: flex;
    align-items: center;
    gap: 7.2px;
    border-radius: 5.4px;
    color: #333;
    transition: background-color 0.2s;
}

.user-menu-button:hover {
    background-color: var(--light-bg);
}

.user-menu-dropdown {
    position: absolute;
    top: 100%;
    right: 0;
    margin-top: 7.2px;
    background: white;
    border-radius: 7.2px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    min-width: 180px;
    display: none;
    z-index: 1001;
}

.user-menu-dropdown.show {
    display: block;
}

.user-menu-dropdown a {
    display: block;
    padding: 10.8px 20px;
    color: var(--dark-text);
    text-decoration: none;
    transition: background-color 0.2s;
}

.user-menu-dropdown a:hover {
    background-color: var(--light-bg);
}

.user-menu-dropdown a:first-child {
    border-radius: 7.2px 8px 0 0;
}

.user-menu-dropdown a:last-child {
    border-radius: 0 0 8px 8px;
    color: var(--danger-color);
}

/* Sidebar - Flexbox Layout (NÃO fixed!) */
.sidebar {
    width: var(--sidebar-width);
    background: #2c3e50;
    color: white;
    transition: width 0.3s;
    /* height e overflow definidos no layout Flexbox (linha ~991) */
}

.mobile-hamburger {
    position: absolute;
    top: 9px;
    left: 9px;
    background: var(--primary-color);
    color: white;
    border: none;
    border-radius: 3.6px;
    padding: 9px 15px;
    cursor: pointer;
    z-index: 1001;
    display: none !important;  /* F08-0005-2: Sempre escondido */
}

.sidebar-toggle {
    position: absolute;
    top: 9px;
    right: -15px;
    width: 27px;
    height: 27px;
    background: var(--primary-color);
    border: none;
    border-radius: 50%;
    color: white;
    cursor: pointer;
    display: flex;
    align-items: center;
    justify-content: center;
    box-shadow: 0 2px 4px rgba(0,0,0,0.2);
}

.sidebar-menu {
    list-style: none;
    padding: 18px 0;
}

.sidebar-menu-item {
    position: relative;
}

.sidebar-menu-link {
    display: flex;
    align-items: center;
    gap: 10.8px;
    padding: 10.8px 20px;
    color: #ecf0f1;
    text-decoration: none;
    transition: all 0.2s;
    cursor: pointer;
}

.sidebar-menu-link:hover {
    background-color: rgba(255,255,255,0.1);
    color: white;
}

.sidebar-menu-link.active {
    background-color: var(--primary-color);
    color: white;
}

.sidebar-menu-icon {
    width: 18px;
    text-align: center;
    font-size: 16.2px;
}

.sidebar-menu-text {
    flex: 1;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sidebar.collapsed .sidebar-menu-text {
    display: none;
}

/* .sidebar-submenu e .sidebar-submenu.show são governados pelo sidebar.css
   (esquema max-height + overflow:hidden; altura via sidebar.js). O esquema antigo
   display:none/block daqui foi removido — ele controlava `display` (que o sidebar.css
   não define) e impedia a animação de fechar. Esconder de propósito (mobile fechado,
   desktop colapsado) continua no sidebar.css. */

.sidebar-submenu-link {
    display: block;
    padding: 9px 20px 10px 52px;
    color: #bdc3c7;
    text-decoration: none;
    transition: all 0.2s;
}

.sidebar-submenu-link:hover {
    background-color: rgba(255,255,255,0.1);
    color: white;
}

.sidebar-submenu-link.active {
    background-color: var(--primary-dark);
    color: white;
}

/* Page Container: ancorado ao viewport abaixo da topbar */
.page-container {
    position: fixed !important;
    top: var(--topbar-height) !important;
    left: 0 !important;
    right: 0 !important;
    bottom: 0 !important;
    display: flex !important;
    overflow: hidden !important;
    background: #f8f9fa !important;
    margin-top: 0 !important;
    height: auto !important;
}

/* REMOVIDO: Duplicata - definição correta está na linha ~991 */

.page-header {
    margin: 0 !important;  /* Zera margin (definição consolidada linha ~991) */
    margin-bottom: 27px;  /* Apenas bottom quando NÃO sticky */
    padding-top: 0 !important;  /* CRÍTICO: Remove padding-top que causa gap */
}

/* Título e subtítulo na MESMA linha — o subtítulo é apoio, não merece uma
   faixa própria no topo de cada tela. Ambos viram inline-block em vez de a
   regra virar flex no contêiner: as 119 telas que usam esse par têm dois
   arranjos diferentes (h1 solto no .page-header, ou aninhado num .d-flex com
   um botão ao lado), e como título e subtítulo são irmãos nos dois casos,
   mexer neles funciona em ambos sem tocar em template nenhum.

   Subtítulo longo em tela estreita cai inteiro para a linha de baixo — é a
   degradação desejada; ele nunca quebra no meio da frase. */
.page-title {
    display: inline-block;
    font-size: 25.2px;
    font-weight: 600;
    color: var(--dark-text);
    margin-top: 0 !important;  /* Remove margin-top padrão de h1 */
    margin-bottom: 0;
    padding-top: 0 !important;  /* CRÍTICO: Remove padding-top */
    vertical-align: baseline;
}

.page-subtitle {
    display: inline-block;
    color: var(--secondary-color);
    font-size: 12.6px;
    margin: 0 0 0 12px;
    vertical-align: baseline;
}

/* Cards */
.card {
    background: white;
    border-radius: 10.8px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.08);
    padding: 21.6px;
    margin-bottom: 21.6px;
}

.card-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 18px;
    padding-bottom: 13.5px;
    border-bottom: 1px solid #e9ecef;
}

.card-title {
    font-size: 16.2px;
    font-weight: 600;
    color: var(--dark-text);
}

/* Buttons */
.btn {
    display: inline-block;
    /* Padding is 1px short of the visual 9px/20px because of the transparent border
       below — the two add up to the original size, so solid buttons are unchanged. */
    padding: 8px 19px;
    font-size: 12.6px;
    font-weight: 500;
    text-align: center;
    text-decoration: none;
    /* Transparent (not `none`) so the .btn-outline-* variants below can just recolour
       it. With `border: none` they had no background AND no border, and rendered as
       naked coloured text. */
    border: 1px solid transparent;
    border-radius: 5.4px;
    cursor: pointer;
    transition: all 0.2s;
}

.btn-primary {
    background-color: var(--primary-color);
    color: white;
}

.btn-primary:hover {
    background-color: var(--primary-dark);
}

.btn-secondary {
    background-color: var(--secondary-color);
    color: white;
}

.btn-success {
    background-color: var(--success-color);
    color: white;
}

.btn-danger {
    background-color: var(--danger-color);
    color: white;
}

.btn-sm {
    /* 1px short of 5.4px/12px — see the border note on .btn. */
    padding: 4.4px 11px;
    font-size: 12px;
}

/* Outline variants. They carry no background, so they rely entirely on the border
   declared by .btn — keep these rules AFTER .btn (equal specificity, last wins). */
.btn-outline-primary {
    background-color: transparent;
    border-color: var(--primary-color);
    color: var(--primary-color);
}

.btn-outline-primary:hover {
    background-color: var(--primary-color);
    color: #fff;
}

.btn-outline-secondary {
    background-color: transparent;
    border-color: var(--secondary-color);
    color: var(--secondary-color);
}

.btn-outline-secondary:hover {
    background-color: var(--secondary-color);
    color: #fff;
}

/* Para botão sobre fundo colorido escuro — caso do .kanban-card-header, que é
   cinza (#808080 claro / #404040 escuro). Ali o btn-outline-secondary some,
   porque a borda e o texto dele são do mesmo cinza do fundo. */
.btn-outline-light {
    background-color: transparent;
    border-color: rgba(255, 255, 255, 0.7);
    color: #fff;
}

.btn-outline-light:hover {
    background-color: #fff;
    border-color: #fff;
    color: #444;
}

.btn-outline-danger {
    background-color: transparent;
    border-color: var(--danger-color);
    color: var(--danger-color);
}

.btn-outline-danger:hover {
    background-color: var(--danger-color);
    color: #fff;
}

/* Forms */
.form-group {
    margin-bottom: 18px;
}

.form-label {
    display: block;
    margin-bottom: 5.4px;
    font-weight: 500;
    color: var(--dark-text);
}

.form-control {
    width: 100%;
    padding: 9px 12px;
    font-size: 12.6px;
    border: 1px solid #ced4da;
    border-radius: 5.4px;
    transition: border-color 0.2s;
}

.form-control:focus {
    outline: none;
    border-color: var(--primary-color);
    box-shadow: 0 0 0 3px rgba(0, 103, 181, 0.1);
}

.form-select {
    width: 100%;
    padding: 9px 12px;
    font-size: 12.6px;
    border: 1px solid #ced4da;
    border-radius: 5.4px;
}

/* Selects de ano/período no cabeçalho — auto-width com espaço suficiente para a seta */
.page-header select.form-select {
    width: auto;
    min-width: 80px;
    padding-right: 2.2rem;
}

.form-check-input {
    margin-right: 7.2px;
}

/* Tables */
.table {
    width: 100%;
    /* border-collapse: SEPARATE (não `collapse`) é OBRIGATÓRIO em todo o sistema —
       ver a seção "CÉLULAS CONGELADAS" logo abaixo para o motivo. Com
       border-spacing 0 o desenho é idêntico ao collapse. */
    border-collapse: separate;
    border-spacing: 0;
}

.table th {
    padding: 10.8px;
    text-align: left;
    font-weight: 600;
    border-bottom: 2px solid #dee2e6;
}

/* Fundo padrão apenas em thead/tfoot sem variante Bootstrap de cor */
.table thead:not(.table-dark):not(.table-light):not(.table-secondary):not(.table-primary):not(.table-danger):not(.table-warning):not(.table-info):not(.table-success) th,
.table tfoot:not(.table-dark):not(.table-light):not(.table-secondary):not(.table-primary):not(.table-danger):not(.table-warning):not(.table-info):not(.table-success) td {
    background-color: var(--light-bg);
}

.table td {
    padding: 10.8px;
    border-bottom: 1px solid #dee2e6;
}

/* Hover apenas em linhas de dados — exclui cabeçalhos de seção.
   Cor OPACA (= rgba(13,110,253,.10) achatado sobre branco). Um hover translúcido
   deixa o conteúdo que rola por baixo aparecer atravessado de qualquer célula
   congelada — ver a seção "CÉLULAS CONGELADAS" abaixo. */
.table tbody tr:not(.table-dark):not(.section-row):hover,
.table tbody tr:not(.table-dark):not(.section-row):hover td,
.table tbody tr:not(.table-dark):not(.section-row):hover th {
    background-color: var(--tbl-frozen-hover-bg);
}
/* Dark mode: especificidade (0,5,4) > variantes (0,3,x) — ganha independente de posição */
body.dark-mode .table tbody tr:not(.table-dark):not(.section-row):hover td,
body.dark-mode .table tbody tr:not(.table-dark):not(.section-row):hover th {
    background-color: var(--dm-elevated, #404040) !important;
}

/* ============================================================================
   CÉLULAS CONGELADAS EM TABELAS (position: sticky) — FUNDAÇÃO GLOBAL
   ============================================================================
   TODO congelamento de linha/coluna do sistema depende deste bloco. Antes de
   escrever CSS novo para congelar algo, LEIA — e use os utilitários daqui.

   ── As duas causas de "a borda fica transparente e o conteúdo vaza" ──────────

   1) BORDA COLAPSADA NÃO ANDA COM A CÉLULA.
      Com `border-collapse: collapse` (padrão do Bootstrap) a borda deixa de ser
      da célula e passa a ser pintada numa camada de coordenadas da TABELA, que
      ignora o `position: sticky` e o `z-index` da célula. Ao rolar, a faixa de
      1-2px onde a borda deveria estar fica sendo a tabela rolando: o conteúdo de
      baixo aparece atravessado ali. Por isso `.table` usa
      `border-collapse: separate` + `border-spacing: 0` SYSTEM-WIDE (lá em cima):
      assim cada célula é DONA das suas bordas e elas viajam junto. O desenho
      final é idêntico ao do collapse.

   2) FUNDO TRANSPARENTE (ou translúcido).
      O Bootstrap dá `--bs-table-bg: transparent` às células, e o hover/striped
      pintam por cima com rgba(). Célula congelada só cobre o que rola por baixo
      se o fundo for OPACO — inclusive no hover, na variante de cor e no dark
      mode. Daí as variáveis --tbl-frozen-*: NUNCA use rgba() nem deixe sem
      fundo. (O truque antigo `box-shadow: inset 0 0 0 9999px #fff` existia para
      furar a especificidade do hover translúcido; com o hover opaco ele virou
      desnecessário — `background-color` basta.)

   ── Como congelar algo novo ─────────────────────────────────────────────────
   * Cabeçalho: nada a fazer se a tabela está em `.table-responsive` (regra
     global) — senão, ponha `.table-scroll` no contêiner que rola.
   * Cabeçalho de VÁRIAS linhas (rowspan/colspan): `.table-grouped-head`.
   * Coluna(s): classe `.freeze` nas células + só o `left` por tabela.
   * A classe `.table` do Bootstrap NÃO é requisito: os seletores abaixo são por
     elemento `table`, então tabela com estilo próprio (`.schedule-table`) também
     usa a fundação — mas ela precisa declarar `border-collapse: separate` (a
     `.table` já herda isso lá de cima; ver a causa 1 acima).
   Nunca redeclare fundo/borda/z-index à mão, e nunca use `<style>` inline.     */

/* Redesenho da grade do .table-bordered. Em `separate` as 4 bordas de células
   vizinhas ficam lado a lado (2px) em vez de fundirem; então declaramos bordas
   de uma direção só — bottom+right, +top na 1ª linha, +left na 1ª coluna. */
.table-bordered > :not(caption) > * > * {
    border-width: 0 1px 1px 0;
    border-style: solid;
    border-color: var(--tbl-grid);
}
.table-bordered > thead:first-child > tr:first-child > *,
.table-bordered > tbody:first-child > tr:first-child > * { border-top-width: 1px; }
.table-bordered > :not(caption) > tr > *:first-child { border-left-width: 1px; }
body.dark-mode .table-bordered > :not(caption) > * > * { border-color: var(--dm-border, #505050); }

/* Densidade compacta para tabela-relatório de MUITAS colunas (mês a mês, ano a
   ano). Sem ela o padding padrão de 10.8px e a fonte cheia empurram metade das
   colunas para fora da tela — é a densidade que as telas de Sales Analysis já
   tinham por CSS próprio. `.table-sm` do Bootstrap não serve: perde para
   `.table td` (mesma especificidade, main.css vem depois). */
.table-compact { font-size: 0.78rem; }
.table-compact > * > tr > th,
.table-compact > * > tr > td { padding: 3px 5px; }

/* Contêiner de rolagem genérico + cabeçalho congelado. Use no lugar de
   `style="max-height:...; overflow-y:auto"` com `<thead style="position:sticky">`:
   aquele padrão inline não tem fundo no dark mode nem borda que acompanhe. */
.table-scroll {
    overflow: auto;
    -webkit-overflow-scrolling: touch;
    max-height: var(--table-scroll-h, 400px);
}
/* Seletor por ELEMENTO `table` (e não `.table`): a fundação também atende tabelas
   com estilo próprio que não usam a classe do Bootstrap — ex.: `.schedule-table`. */
.table-scroll > table > thead > tr > th,
.table-scroll > table > thead > tr > td {
    position: sticky;
    top: 0;
    z-index: 10;
    background-color: var(--tbl-frozen-head-bg);
    border-bottom: 2px solid var(--tbl-grid);
}
/* 2ª linha de cabeçalho gruda embaixo da 1ª (offset via --tbl-head-row2-top). */
.table-scroll > table > thead > tr:nth-child(2) > th {
    top: var(--tbl-head-row2-top, 0px);
}

/* Coluna congelada — mecanismo único. O `left` (e a largura) é a única coisa que
   cada tabela declara; fundo, sombra separadora e z-index vêm daqui. */
table > tbody > tr > .freeze,
table > tfoot > tr > .freeze {
    position: sticky;
    left: var(--freeze-left, 0);
    z-index: 5;
    background-color: var(--tbl-frozen-bg);
}
table > tfoot > tr > .freeze { background-color: var(--tbl-frozen-head-bg); }
/* Célula de canto (1ª linha + 1ª coluna). O `th`/`td` no seletor não é enfeite:
   sem ele a regra empata em especificidade com a do cabeçalho congelado logo
   acima e PERDE (aquela tem um elemento a mais), o canto fica com z-index 10 —
   mesmo nível dos demais <th>, que por virem depois no DOM pintam por cima dele
   ao rolar na horizontal. Com `th.freeze` o empate é resolvido pela ordem. */
table > thead > tr > th.freeze,
table > thead > tr > td.freeze {
    position: sticky;
    left: var(--freeze-left, 0);
    /* acima do thead congelado no topo (z 10) E da coluna congelada do corpo */
    z-index: 12;
    background-color: var(--tbl-frozen-head-bg);
}
/* Última coluna congelada: marca a divisa com sombra. `!important` porque o
   `.table-hover` do Bootstrap escreve `box-shadow` em `> tbody > tr:hover > *`
   com especificidade maior — sem isso a divisa some justo na linha sob o mouse.
   O fundo opaco da célula já cobre o conteúdo; a sombra é só o marcador. */
table > * > tr > .freeze-edge { box-shadow: var(--tbl-frozen-shadow) !important; }
table > tbody > tr:hover > .freeze { background-color: var(--tbl-frozen-hover-bg); }

/* Alerts */
.alert {
    padding: 13.5px 20px;
    border-radius: 7.2px;
    margin-bottom: 18px;
}
/* Cores das .alert-* (fundo/texto/borda, claro + escuro) são geradas pelo
   submenu CSS → seção "Mensagens Persistentes" (SystemCSS.generate_css). */

/* Login Page */
.login-container {
    min-height: 100dvh;
    display: flex;
    align-items: center;
    justify-content: center;
    background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}

.login-box {
    background: white;
    border-radius: 14.4px;
    box-shadow: 0 20px 60px rgba(0,0,0,0.3);
    padding: 45px;
    width: 100%;
    max-width: 405px;
}

.login-logo {
    width: 162px;
    margin-bottom: 27px;
    display: block;
    margin-left: auto;
    margin-right: auto;
}

.login-title {
    text-align: center;
    font-size: 21.6px;
    font-weight: 600;
    color: var(--dark-text);
    margin-bottom: 27px;
}

.forgot-password-link {
    display: block;
    text-align: center;
    margin-top: 18px;
    color: var(--primary-color);
    text-decoration: none;
    font-size: 12.6px;
}

.forgot-password-link:hover {
    text-decoration: underline;
}

/* Utilities */
.text-center {
    text-align: center;
}

.text-right {
    text-align: right;
}

.mt-2 {
    margin-top: 0.5rem;
}

.mt-3 {
    margin-top: 0.9rem;
}

.mb-3 {
    margin-bottom: 0.9rem;
}

.d-flex {
    display: flex;
}

.justify-content-between {
    justify-content: space-between;
}

.align-items-center {
    align-items: center;
}

.gap-2 {
    gap: 0.5rem;
}

/* F09-0014-1: Responsive baseado em django-user-agents (device-mobile/device-desktop) */
body.device-mobile .topbar-title {
    font-size: 14.4px;
}

/* Messages */
.messages {
    position: fixed;
    top: 63px;
    right: 18px;
    z-index: 9999;
    max-width: 360px;
}

.message {
    padding: 13.5px 20px;
    border-radius: 7.2px;
    margin-bottom: 9px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15);
    animation: slideIn 0.3s ease;
}

@keyframes slideIn {
    from {
        transform: translateX(400px);
        opacity: 0;
    }
    to {
        transform: translateX(0);
        opacity: 1;
    }
}

/* Sticky Page Header - COMMENTED */
/* .page-header-sticky {
    position: sticky;
    top: var(--topbar-height);
    background: white;
    z-index: 100;
    margin: 0;
    padding: 18px 20px 0 20px;
} */

/* .page-header-sticky .page-header {
    margin-bottom: 0;
    padding-bottom: 13.5px;
} */

/* .page-header-sticky .filter-bar {
    margin: 0;
} */

/* Adjust main content to accommodate sticky header - COMMENTED */
/* .main-content {
    position: relative;
} */


/* Highlight active submenu item */
.sidebar-submenu-link.active {
    background: rgba(0, 103, 181, 0.1);
    color: var(--primary-color);
    font-weight: 600;
    border-left: 2.7px solid var(--primary-color);
    padding-left: 33.3px; /* Compensate for border */
}

.sidebar-submenu-link {
    padding-left: 36px;
    transition: all 0.2s ease;
}


/* Highlight active main menu item */
.sidebar-menu-link.active {
    background: rgba(0, 103, 181, 0.1);
    color: var(--primary-color);
    font-weight: 600;
    border-left: 2.7px solid var(--primary-color);
    padding-left: 15.3px; /* Compensate for border */
}

.sidebar-menu-link {
    padding-left: 18px;
    transition: all 0.2s ease;
}

/* F08-0003-1.3: Sidebar Toggle Header REMOVIDO - estava causando elemento à esquerda */
/* Esta definição foi substituída pela versão sticky que fica DENTRO da sidebar (linha ~665) */

.toggle-icon {
    display: inline-block;
    line-height: 1;
}

/* Header fixo dentro da sidebar, com botão hamburger e "Main Menu" */
.sidebar-toggle-header {
    position: sticky;
    top: 0;
    z-index: 1000;
    background: #2c3e50;
    padding: 13.5px;
    display: flex;
    align-items: center;
    justify-content: flex-start;   /* hamburger + texto lado a lado */
    gap: 9px;                     /* espaço entre icone e label */
    border-bottom: 1px solid rgba(255,255,255,0.1);
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

/* MUITO IMPORTANTE:
   Mesmo quando a sidebar está .collapsed, o header CONTINUA visível.
   (caso exista alguma regra antiga escondendo ele)
*/
.sidebar.collapsed .sidebar-toggle-header {
    display: flex !important;
}

.sidebar-toggle-btn {
    background: transparent;
    border: none;
    color: white;
    font-size: 21.6px;
    cursor: pointer;
    padding: 7.2px 12px;
    border-radius: 3.6px;
    line-height: 1;
    transition: background 0.3s ease;
    flex-shrink: 0;
}

.sidebar-toggle-btn:hover {
    background: rgba(255, 255, 255, 0.1);
}

.sidebar-title {
    font-weight: bold;
    font-size: 14.4px;
    color: white;
    line-height: 1.2;
    flex: 1;
}

.toggle-icon {
    display: inline-block;
    line-height: 1;
}

/* CORREÇÃO: Remover espaço entre topbar e conteúdo - COMMENTED */
/* .main-content {
    padding-top: 54px;
    margin-top: 0;
} */

/* CORREÇÃO: Sidebar não precisa de margin-top extra */
.sidebar {
    margin-top: 0;
}

/* CORREÇÃO: Page headers sticky - COMMENTED */
/* .page-header-sticky {
    position: sticky;
    top: 0;
    z-index: 100;
    background: white;
} */

/* REMOVIDO: Duplicata conflitante */

/* Remover qualquer padding/margin extra do body/html */
body, html {
    margin: 0;
    padding: 0;
    overflow: hidden;  /* Previne scroll no viewport */
}

/* REMOVIDO: Duplicata - definição principal está na linha ~993 */

/* CRITICAL FIX: Main content must be below topbar, not behind it */
/* .main-content {
    padding-top: var(--topbar-height) !important;
    padding-left: 27px;
    padding-right: 27px;
    padding-bottom: 27px;
    margin-top: 0 !important;
} */

/* CRITICAL FIX: Ensure page-header-sticky - COMMENTED */
/* .page-header-sticky {
    position: sticky;
    top: var(--topbar-height);
    z-index: 100;
    background: white;
    margin: 0 !important;
    padding: 0 !important;
} */

/* REMOVIDO: Regras movidas para seção consolidada linha ~1005 */

/* Regular page headers (NÃO dentro de sticky) */
.page-header {
    margin: 0 0 30px 0;
    padding: 0;
}

/* REMOVIDO: Duplicata - definição principal está na linha ~993 */

/* REMOVIDO: Regras antigas de #mainContent - solução IA usa .main-content */

/* FINAL FIX: Prevent content from going behind sticky header */
/* .page-header-sticky {
    position: sticky;
    top: 0 !important;
    z-index: 100;
    background: white;
    padding: 18px;
    margin: 0 !important;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
} */

/* Card that comes after sticky header should not overlap */
.card {
    position: relative;
    z-index: 1;
}

/* Ensure content inside cards starts with proper spacing */
.page-header-sticky + .card .table {
    margin-top: 0;
}

/* Prices table styling - smaller font */
#pricesTable {
    font-size: 11.7px;
}

#pricesTable thead th {
    font-size: 12px;
    font-weight: 600;
    padding: 7.2px 5px;
}

#pricesTable tbody td {
    padding: 5.4px 5px;
    vertical-align: middle;
}

#pricesTable tbody td:first-child {
    font-size: 11.7px;
}

/* Month columns - even smaller */
#pricesTable tbody td:nth-child(n+3):nth-child(-n+14) {
    font-size: 12px;
}

/* =======================================================================
   F08-0003: PAGE HEADER & STICKY - CONSOLIDATED & FIXED
   ======================================== */

/* REMOVIDO: Definições antigas conflitantes - solução IA na linha ~1005 */

/* REMOVIDO: Regras antigas conflitantes - solução IA consolidada linha ~1005 */

/* Garantir que cards tenham z-index menor */
.card {
    position: relative;
    z-index: 1;
}

/* ========================================
   SOLUÇÃO DEFINITIVA - PRICES TABLE
   ======================================== */

/* Reduzir largura das colunas */
#pricesTable {
    font-size: 11.7px;
    min-width: 1080px !important; /* Reduzido de 1400px */
}

#pricesTable thead th {
    font-size: 12px !important;   /* Ainda menor */
    font-weight: 600;
    padding: 5.4px 3px !important;  /* Padding muito reduzido */
    text-align: center;
}

#pricesTable tbody td {
    padding: 4.5px 3px !important;  /* Padding muito reduzido */
    vertical-align: middle;
    font-size: 12px;
}

/* Coluna Product - reduzida */
#pricesTable thead th:first-child,
#pricesTable tbody td:first-child {
    width: 144px !important;      /* Reduzido de 200px */
    text-align: left !important;
}

/* Coluna Year - reduzida */
#pricesTable thead th:nth-child(2),
#pricesTable tbody td:nth-child(2) {
    width: 54px !important;       /* Reduzido de 80px */
    text-align: center !important;
}

/* Colunas de meses - reduzidas */
#pricesTable thead th:nth-child(n+3):nth-child(-n+14),
#pricesTable tbody td:nth-child(n+3):nth-child(-n+14) {
    width: 58.5px !important;       /* Reduzido de 80px */
    text-align: right !important;
    font-size: 12px !important;
}

/* Coluna Actions - reduzida */
#pricesTable thead th:last-child,
#pricesTable tbody td:last-child {
    width: 117px !important;      /* Reduzido de 150px */
    text-align: center !important;
}

/* Botões menores */
#pricesTable .btn-sm {
    /* 1px short of 2.7px/8px — compensates .btn's transparent border. */
    padding: 1.7px 7px !important;
    font-size: 12px !important;
}


/* ========================================================================

/* CORREÇÃO SIMPLES E DIRETA - Conteúdo abaixo do topbar */

/* ============================================================================
   LAYOUT FLEXBOX MODERNO (Topbar + Sidebar + Main Scrollável)
   ============================================================================ */

/* REMOVIDO: Definição duplicada conflitante de .page-container - definição correta na linha ~264 */

/* Sidebar: Fixo na lateral (solução IA) */
.sidebar {
    width: var(--sidebar-width);
    flex: 0 0 var(--sidebar-width);  /* Não cresce nem encolhe */
    overflow-y: auto;
    overflow-x: hidden;
    height: 100%;  /* 100% do page-container (que tem a altura correta) */
    min-width: 0;
}

/* Main Content: container flex — NÃO scrola (content-area é o scroll frame) */
.main-content {
    display: flex !important;
    flex-direction: column !important;
    flex: 1 1 auto !important;
    height: 100% !important;
    overflow: hidden !important;
    background: #f8f9fa;
    min-height: 0 !important;
    min-width: 0 !important;
}

/* Page header — frame fixo, não participa do scroll */
.page-header-sticky {
    flex-shrink: 0;
    background: white;
    padding: 0;
    margin: 0;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    z-index: 150;
}

/* Regras internas do page-header-sticky */
.page-header-sticky .page-header {
    margin: 0;
    padding: 18px 30px;
    background: white;
}

.page-header-sticky .filter-bar {
    margin: 0;
    padding: 13.5px 30px;
    background: white;
    border-top: 1px solid #dee2e6;
}

.page-header-sticky + .card {
    margin-top: 0;
    border-top: none;
}

/* Content area — frame de scroll de página (abaixo do page-header-sticky) */
.content-area {
    flex: 1 1 0%;   /* basis 0% garante altura limitada ao espaço disponível */
    min-height: 0;
    overflow-y: auto;
    overflow-x: hidden;
    /* --content-pad-y espelha o padding VERTICAL e é lido pelas abas fixas (ver
       "ABAS FIXAS"): o sticky ancora no padding do scrollport, então a barra
       precisa descontar esse valor para encostar no topo. Quem mudar o padding
       nas quebras de dispositivo tem que mudar a variável junto. */
    --content-pad-y: 27px;
    padding: 27px;
    background: #f8f9fa;
}

/* Sidebar collapsed state */
.sidebar.collapsed {
    width: var(--sidebar-collapsed-width);
}

/* Card dentro de content-area sem margin extra */
.content-area > .card {
    margin-top: 0 !important;
    border-top: none !important;
}

/* Backwards compatibility */
.page-header-sticky + .card {
    margin-top: 0 !important;
    border-top: none !important;
}

/* Tabelas responsivas — scroll horizontal sempre; vertical limitado pela tabela */
.table-responsive {
    overflow-x: auto;
    overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    /* JS mede o page-header-sticky real e seta --page-header-height;
       120px = card padding (43px) + card margin-bottom (22px) + content-area padding (54px) + border+buffer (1px) */
    max-height: calc(100dvh - var(--topbar-height) - var(--page-header-height) - 125px);
}

/* Cabeçalho de tabela sticky — dentro do table-responsive (scroll container próximo).
   A borda de baixo só acompanha a célula porque `.table` é `border-collapse:
   separate` — ver a seção "CÉLULAS CONGELADAS". */
.table-responsive thead th {
    position: sticky;
    top: 0;
    z-index: 10;
    border-bottom: 2px solid var(--tbl-grid);
    box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1);
}
/* thead sem variante de cor recebe fundo branco para cobrir linhas ao scrollar */
.table-responsive thead:not(.table-dark):not(.table-light):not(.table-secondary):not(.table-primary):not(.table-danger):not(.table-warning):not(.table-info):not(.table-success) th {
    background-color: white;
}

table {
    min-width: 100%;
    white-space: nowrap;
}

/* Filter bar sticky — dentro de content-area (novo scroll container) */
.filter-bar-sticky {
    position: sticky !important;
    top: 0 !important;
    z-index: 99 !important;
    background: white !important;
    padding: 9px 0;
    margin: -27px -27px 20px -27px;  /* compensa padding do content-area */
}

/* ═══════════════════════════════════════════════════════════════════════════
   ABAS FIXAS (position: sticky) — FUNDAÇÃO GLOBAL

   Aba NUNCA rola junto com o conteúdo: a barra fica presa no topo da área de
   scroll da página (`.content-area`) e os painéis passam por baixo dela.

   O lugar PREFERIDO da barra de abas é o `.page-header-sticky`, e é o que todas
   as telas com abas usam hoje: aquele frame é irmão do `.content-area` e por
   definição não rola, então a barra não precisa de nada deste bloco (o visual
   dela sai da seção "VISUAL DAS ABAS", logo abaixo).

   Vale também para as telas cujos painéis são trocados por JS caseiro em vez de
   `.tab-pane` do Bootstrap (Templates e System Cleanup, que precisam lembrar a
   aba escolhida entre redirects): a marcação da BARRA é a mesma `ul.nav.nav-tabs`
   e o JS só alterna a classe `.active` — não existe segundo desenho de aba.

   O que segue é para a barra que precisa morar DENTRO da área rolável — duas
   formas, nenhuma delas pedindo CSS novo na tela:

     1. Abas do Bootstrap (`<ul class="nav nav-tabs">`) dentro do
        `.content-area`: já ganham o comportamento automaticamente.
     2. Barra de abas caseira (grupo de botões que troca os painéis por JS):
        acrescente a classe `.tabs-sticky` no contêiner da barra.

   Notas de implementação:
   - O fundo é OPACO de propósito, pelo mesmo motivo das células congeladas:
     sem ele o conteúdo rolado aparece nos vãos entre as abas. Para usar a
     barra sobre outra cor (dentro de um card, por exemplo) redefina
     `--tabs-sticky-bg` no elemento, nunca o `background` na mão.
   - `z-index: 98` fica logo abaixo do `.filter-bar-sticky` (99) e do
     `.page-header-sticky` (150), e acima dos cards — que são contexto de
     empilhamento próprio (`.card { position: relative; z-index: 1 }`), então
     nem os theads congelados deles vazam por cima da barra.
   - O `top` NEGATIVO não é gambiarra: o sticky ancora no padding do scrollport,
     então com `top: 0` a barra congelaria `--content-pad-y` abaixo do topo
     visível e as linhas roladas apareceriam nessa faixa. Descontar o padding
     encosta a barra no topo e o conteúdo passa inteiro por baixo.
   - Tela com filtro sticky E abas: empilhe as duas setando `--tabs-sticky-top`
     com a altura da barra de filtro.
   - Só funciona se nenhum ancestral entre a barra e o `.content-area` tiver
     `overflow` diferente de `visible` — um `overflow:hidden` no caminho vira o
     scrollport do sticky e o congelamento morre calado.
   ═══════════════════════════════════════════════════════════════════════════ */
.content-area .nav-tabs,
.tabs-sticky {
    position: sticky;
    top: calc(var(--tabs-sticky-top, 0px) - var(--content-pad-y, 27px));
    z-index: 98;
    background: var(--tabs-sticky-bg, var(--light-bg));
    padding-top: 9px;
    box-shadow: 0 6px 6px -6px rgba(0, 0, 0, 0.15);
}

/* A barra encosta no topo TAMBÉM com a tela parada. Sem isto ela nasce
   `--content-pad-y` mais abaixo e sobe de repente ao congelar: além do pulo, a
   tela sem rolagem desperdiçava essa faixa de altura útil. O puxão vale só
   quando a barra é a primeira coisa da área de conteúdo — havendo um alerta de
   mensagem antes dela, ele passaria por baixo da barra. */
.content-area > .nav-tabs:first-child,
.content-area > .tabs-sticky:first-child,
.content-area > *:first-child > .nav-tabs,
.content-area > *:first-child > .tabs-sticky {
    margin-top: calc(-1 * var(--content-pad-y, 27px));
}

body.dark-mode {
    --tabs-sticky-bg: var(--dm-bg, #1a1a1a);
}

/* ═══ VISUAL DAS ABAS (padrão único da aplicação) ═══════════════════════════
   Aba SUBLINHADA, não a caixa padrão do Bootstrap: sem borda lateral/superior,
   só um traço de 3px embaixo que acende na ativa. O mesmo desenho vale esteja
   a barra no cabeçalho fixo (preferido) ou dentro do `.content-area`.

   O traço transparente da aba inativa não é decorativo: ele reserva a altura
   do traço da ativa, senão o rótulo pula 3px a cada troca de aba.
   ═══════════════════════════════════════════════════════════════════════════ */
.nav-tabs {
    border-bottom: 1px solid #dee2e6;
}

.nav-tabs .nav-link {
    border: none;
    border-bottom: 3px solid transparent;
    border-radius: 0;
    padding: 10px 16px;
    color: var(--secondary-color, #6c757d);
    background: transparent;
}

.nav-tabs .nav-link:hover {
    border-bottom-color: #dee2e6;
    color: var(--primary-color, #0067b5);
}

.nav-tabs .nav-link.active {
    background: transparent;
    border-bottom-color: var(--primary-color, #0067b5);
    color: var(--primary-color, #0067b5);
    font-weight: 600;
}

/* Barra de abas DENTRO do cabeçalho fixo — o lugar preferido. Recebe o mesmo
   tratamento da `.filter-bar`: colada ao título, largura inteira, e o conteúdo
   rola por baixo do cabeçalho. Como o frame do cabeçalho não rola, ela não
   precisa de nada do bloco "ABAS FIXAS" acima (aquilo é para a barra que mora
   na área rolável). O fundo acompanha o do cabeçalho — ver o override de
   `body.dark-mode` junto das outras regras do tema escuro. */
.page-header-sticky .nav-tabs {
    margin: 0;
    padding: 0 30px;
    background: white;
    border-top: 1px solid #dee2e6;
    border-bottom: none;
}

/* O primeiro cartão do painel ativo não pede respiro extra: quem dá o
   espaçamento é o padding do frame de conteúdo. */
.content-area > .tab-content > .tab-pane > .card:first-child {
    margin-top: 0;
}

/* ═══════════════════════════════════════════════════════════════════════════
   CARTÃO QUE PREENCHE O FRAME (`.content-area.frame-fill`) — FUNDAÇÃO GLOBAL

   Inverte o padrão normal: em vez de o cartão crescer e a PÁGINA rolar, o
   cartão fica do tamanho do frame e quem rola é a tabela dentro dele — o
   cabeçalho do cartão, a busca e os botões de ação em massa ficam sempre à
   vista. Use em tela de lista longa cujo topo do cartão é ferramenta de
   trabalho, não enfeite.

   Por que a fundação de `.table-responsive` não basta: lá a altura sai de uma
   conta por viewport com desconto FIXO (`- 125px`), que só cobre o padding do
   cartão e do frame. Havendo barra de abas, cabeçalho de cartão e campo de
   busca acima da tabela, a conta sobra, o cartão estoura o frame e o
   `.content-area` volta a rolar o cartão inteiro. Aqui nada é calculado: o
   frame reparte a altura que existe.

   Notas de implementação:
   - `max-height` (e NÃO `height`) no cartão: lista curta continua com cartão
     curto, sem faixa vazia embaixo.
   - `min-height: 0` em todo elo da corrente é obrigatório — o mínimo
     automático de um item flex é o conteúdo dele, então sem isso o item se
     recusa a encolher e a rolagem volta para a página.
   - Os filhos do cartão levam `flex-shrink: 0`: sem isso o flex espreme o
     cabeçalho e a busca junto com a tabela. Só o contêiner que rola (e o
     `<form>` de ações em massa que fica no caminho dele) pode encolher.
   - `max-height: none` no contêiner que rola desliga a conta por viewport da
     `.table-responsive`; as duas juntas dariam uma altura menor que o frame.
   - A barra de abas fixa continua funcionando, mas vira decoração: com o
     cartão dentro do frame, o `.content-area` não rola mais.
   ═══════════════════════════════════════════════════════════════════════════ */
.content-area.frame-fill {
    display: flex;
    flex-direction: column;
}

/* O painel de abas (ou o cartão solto) recebe a sobra de altura. */
.content-area.frame-fill > .tab-content,
.content-area.frame-fill > .card {
    flex: 1 1 0%;
    min-height: 0;
}

.content-area.frame-fill > .tab-content > .tab-pane.active {
    height: 100%;
}

.content-area.frame-fill .card {
    display: flex;
    flex-direction: column;
    max-height: 100%;
    margin-bottom: 0;
}

.content-area.frame-fill .card > *,
.content-area.frame-fill .card > form > * {
    flex-shrink: 0;
}

/* Elo transparente: o <form> de ações em massa repassa a altura à tabela. */
.content-area.frame-fill .card > form {
    display: flex;
    flex-direction: column;
    min-height: 0;
}

/* Quem rola. Precisa vencer o `flex-shrink: 0` acima — daí o seletor com o
   elo do meio explícito, e não `.frame-fill .table-responsive` solto. */
.content-area.frame-fill .card > form,
.content-area.frame-fill .card > .table-responsive,
.content-area.frame-fill .card > .table-scroll,
.content-area.frame-fill .card > form > .table-responsive,
.content-area.frame-fill .card > form > .table-scroll {
    flex-shrink: 1;
    min-height: 0;
}

.content-area.frame-fill .table-responsive,
.content-area.frame-fill .table-scroll {
    max-height: none;
}

/* Page header interno - DENTRO do page-header-sticky */
.page-header {
    margin: 0 !important;  /* Remove margin que causa gap */
    padding: 18px 30px !important;  /* Padding apenas no header */
}

/* Filter bar - DENTRO do page-header-sticky */
.page-header-sticky .filter-bar {
    padding: 13.5px 30px !important;  /* Padding consistente */
    background: white;
    border-top: 1px solid #dee2e6;
}

/* About Modal Styles */
.modal-overlay {
    display: none;
    position: fixed;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background-color: rgba(0, 0, 0, 0.5);
    z-index: 2000;
    justify-content: center;
    align-items: center;
}

.modal-content {
    background: white;
    padding: 27px 40px;
    border-radius: 7.2px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
    text-align: center;
    max-width: 360px;
    width: 90%;
    animation: modalFadeIn 0.3s ease;
}

@keyframes modalFadeIn {
    from {
        opacity: 0;
        transform: translateY(-20px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

.modal-content h2 {
    font-size: 21.6px;
    font-weight: 600;
    margin-bottom: 13.5px;
}

.modal-content p {
    font-size: 14.4px;
    line-height: 1.6;
    margin-bottom: 22.5px;
}

.modal-content .btn {
    min-width: 90px;
    padding: 9px 20px;
    font-size: 14.4px;
    cursor: pointer;
}

/* ==================== SELECT2 CUSTOM STYLES ==================== */

/* Select2 Container */
.select2-container--classic .select2-selection--multiple {
    border: 1px solid #ced4da;
    border-radius: 3.6px;
    min-height: 34.2px;
    padding: 2px 5px;
}

.select2-container--classic.select2-container--focus .select2-selection--multiple {
    border-color: var(--primary-color);
    box-shadow: 0 0 0 0.2rem rgba(0, 103, 181, 0.25);
}

/* Pillbox/Tags Styling.
   NOTA: select2.min.css carrega DEPOIS do main.css (base.html) e define estas
   mesmas classes com igual especificidade (pill cinza #e4e4e4) — o prefixo `body`
   sobe a especificidade (0,3,1 > 0,3,0) p/ o azul da marca vencer. Mesmo truque
   que as regras de dark mode usam (ver M.62). */
body .select2-container--classic .select2-selection--multiple .select2-selection__choice {
    background-color: var(--primary-color);
    border: 1px solid var(--primary-dark);
    border-radius: 18px;
    padding: 2.7px 10px;
    margin: 2.7px 5px 3px 0;
    color: white;
    font-size: 11.7px;
    display: inline-flex;
    align-items: center;
}

body .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
    color: white;
    margin-right: 4.5px;
    font-weight: bold;
    cursor: pointer;
}

body .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove:hover {
    color: #ffdddd;
}

/* Dropdown */
.select2-container--classic .select2-dropdown {
    border: 1px solid #ced4da;
    border-radius: 3.6px;
    box-shadow: 0 2px 8px rgba(0,0,0,0.15);
}

.select2-container--classic .select2-results__option--highlighted {
    background-color: var(--primary-light);
    color: white;
}

.select2-container--classic .select2-results__option[aria-selected=true] {
    background-color: #e9ecef;
}

/* Search Input */
.select2-container--classic .select2-search--inline .select2-search__field {
    margin: 2.7px 0;
    padding: 4.5px;
    font-size: 12.6px;
}

/* Placeholder */
.select2-container--classic .select2-selection--multiple .select2-selection__placeholder {
    color: #6c757d;
    font-size: 12.6px;
}

/* Width adjustment */
.select2-container {
    width: 100% !important;
}

/* ==================== CUSTOMER DETAILS MODAL ==================== */

/* Modal Overlay */
.modal {
    display: none;
    position: fixed;
    z-index: 9999;
    left: 0;
    top: 0;
    width: 100%;
    height: 100%;
    overflow: auto;
    background-color: rgba(0, 0, 0, 0.5);
    justify-content: center;
    align-items: center;
}

.modal-content {
    background-color: white;
    border-radius: 7.2px;
    box-shadow: 0 4px 20px rgba(0, 0, 0, 0.3);
    animation: modalFadeIn 0.3s ease;
}

@keyframes modalFadeIn {
    from {
        opacity: 0;
        transform: translateY(-50px);
    }
    to {
        opacity: 1;
        transform: translateY(0);
    }
}

/* `.customer-detail-modal` saiu junto com o modal caseiro do customer_list
   (10/08/2026) — a ficha do cliente agora é a de General Info dentro do
   #viewModal. As classes .detail-section/.detail-grid/.detail-item abaixo
   CONTINUAM em uso: são elas que montam o trip_detail. */

/* ── Bootstrap modals reset ──────────────────────────────────────────────────
   The app's legacy `.modal`/`.modal-content` (custom JS modals) set max-width:360px,
   text-align:center, padding and a dark background — which leak into Bootstrap
   modals (same class names). Scope a reset to Bootstrap modals only (they wrap
   content in `.modal-dialog`), so modal-lg/centered/sizing work and there's no
   double backdrop. Legacy modals (no `.modal-dialog`) are untouched. */
.modal-dialog > .modal-content {
    max-width: none;
    width: 100%;
    padding: 0;
    text-align: left;
    animation: none;
}
.modal:has(> .modal-dialog) {
    background-color: transparent;
    display: none;
}

.modal-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    padding: 18px;
    border-bottom: 2px solid var(--primary-color);
    background-color: #f8f9fa;
}

.modal-header h2 {
    margin: 0;
    font-size: 21.6px;
    color: var(--primary-color);
}

.close-btn {
    font-size: 25.2px;
    font-weight: bold;
    color: #999;
    cursor: pointer;
    line-height: 1;
}

.close-btn:hover {
    color: #333;
}

.modal-body {
    padding: 18px;
}

.detail-section {
    margin-bottom: 22.5px;
    padding-bottom: 18px;
    border-bottom: 1px solid #e0e0e0;
}

.detail-section:last-child {
    border-bottom: none;
}

.detail-section h3 {
    font-size: 16.2px;
    font-weight: 600;
    color: var(--primary-color);
    margin-bottom: 13.5px;
    padding-bottom: 7.2px;
    border-bottom: 2px solid var(--primary-light);
}

.detail-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
    gap: 13.5px;
}

.detail-item {
    display: flex;
    flex-direction: column;
    gap: 4.5px;
}

.detail-item.full-width {
    grid-column: 1 / -1;
}

.detail-item strong {
    font-size: 11.7px;
    color: #666;
    text-transform: uppercase;
    letter-spacing: 0.5px;
}

.detail-item span {
    font-size: 13.5px;
    color: #333;
    font-weight: 500;
}

.tags-container {
    display: flex;
    flex-wrap: wrap;
    gap: 7.2px;
    margin-top: 4.5px;
}

.tag {
    background-color: var(--primary-color);
    color: white;
    padding: 3.6px 12px;
    border-radius: 13.5px;
    font-size: 11.7px;
    font-weight: 500;
}

.modal-footer {
    padding: 13.5px 20px;
    border-top: 1px solid #e0e0e0;
    display: flex;
    gap: 9px;
    justify-content: flex-end;
    background-color: #f8f9fa;
}

.modal-footer .btn {
    min-width: 90px;
}

/* Modal estreito (avisos / confirmações) */
.modal-content.modal-sm {
    max-width: 480px;
    width: 90%;
}

/* Campos de coordenadas (Cidades/Aeroportos) — somente leitura protegida */
.coord-header {
    display: flex;
    justify-content: space-between;
    align-items: center;
    margin-bottom: 9px;
}

.coord-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 13.5px;
}

@media (max-width: 600px) {
    .coord-grid {
        grid-template-columns: 1fr;
    }
}

/* Modais genéricos (.modal/.modal-content) — modo escuro. Cobre o modal de
   aviso de coordenadas (Cidades/Aeroportos) e o de detalhes do cliente; segue
   o CSS global (--dm-*) em vez do branco fixo. */
body.dark-mode .modal-content { background-color: var(--dm-surface, #2d2d2d); color: var(--dm-text, #e0e0e0); }
body.dark-mode .modal-header { background-color: var(--dm-elevated, #404040); }
body.dark-mode .modal-footer { background-color: var(--dm-elevated, #404040); border-top-color: var(--dm-border, #505050); }
body.dark-mode .modal-body { color: var(--dm-text, #e0e0e0); }
body.dark-mode .close-btn { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .close-btn:hover { color: var(--dm-text, #e0e0e0); }
body.dark-mode .detail-section { border-bottom-color: var(--dm-border, #505050); }
body.dark-mode .detail-item strong { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .detail-item span { color: var(--dm-text, #e0e0e0); }

/* ================================================================ */
/* MOBILE RESPONSIVE - SUPORTE COMPLETO PARA CELULARES E TABLETS */
/* ================================================================ */

/* Mobile Hamburger Button - Escondido em desktop */
.mobile-hamburger {
    display: none;
    position: fixed;
    top: 9px;
    right: 9px;
    z-index: 1110; /* Acima do topbar */
    background: var(--primary-color);
    border: none;
    border-radius: 10.8px;
    width: 54px; /* Maior para touch */
    height: 54px; /* Maior para touch */
    color: white;
    font-size: 28.8px; /* Ícone maior */
    cursor: pointer;
    box-shadow: 0 4px 12px rgba(0,0,0,0.3);
    transition: all 0.3s ease;
    display: flex;
    align-items: center;
    justify-content: center;
    font-weight: 300; /* Ícone mais fino */
}

.mobile-hamburger:hover {
    background: #34495e;
    transform: scale(1.05);
}

.mobile-hamburger:active {
    transform: scale(0.95);
}

/* F09-0014-1: Mobile - Ajustar posição com safe area iOS */
body.device-mobile .mobile-hamburger {
    top: calc(6.3px + env(safe-area-inset-top, 0));
}

/* F09-0014-1: Tablets e Mobile - Ajustes gerais */
body.device-mobile .page-title {
    font-size: 1.3rem;
}

body.device-mobile .page-subtitle {
    font-size: 0.8rem;
}

/* Forms — reduz gap vertical do Bootstrap g-3 (1rem → 0.5rem) no mobile */
body.device-mobile .row.g-3 {
    --bs-gutter-y: 0.5rem;
    --bs-gutter-x: 0.5rem;
}

/* F09-0014-1: Mobile - SMARTPHONES (baseado em django-user-agents) */
/* Layout principal */
body.device-mobile {
    font-size: 12.6px;
    overflow-x: hidden !important;
}

/* FORÇA LAYOUT MOBILE */
body.device-mobile {
    width: 100% !important;
    max-width: 100% !important;
    overflow-x: hidden !important;
}
    
    /* Topbar FIXO no topo - CRÍTICO PARA MOBILE */
    body.device-mobile .topbar {
        position: fixed;
        top: 0;
        left: 0;
        right: 0;
        width: 100%;
        height: 54px;
        padding: 0 10px; /* hamburger agora é interno à sidebar */
        z-index: 1100 !important; /* Acima de tudo */
        background: white;
        box-shadow: 0 2px 8px rgba(0,0,0,0.15);
        /* iOS Safe Area */
        padding-top: env(safe-area-inset-top, 0);
        height: calc(54px + env(safe-area-inset-top, 0));
    }
    
    body.device-mobile .topbar-title {
        font-size: 13.5px;
    }
    
    body.device-mobile .topbar-logo {
        height: 28.8px; /* Logo menor em mobile */
    }
    
    body.device-mobile .topbar-right {
        gap: 7.2px;
    }
    
    body.device-mobile .user-menu-button {
        font-size: 12px;
        padding: 5.4px 10px;
    }
    
    /* Overlay quando sidebar aberta */
    body.device-mobile .sidebar-overlay {
        display: none;
        position: fixed;
        top: calc(54px + env(safe-area-inset-top, 0));
        left: 0;
        width: 100%;
        height: calc(100dvh - 54px - env(safe-area-inset-top, 0));
        background: transparent; /* sem escurecer o conteúdo; só captura clique p/ fechar */
        z-index: 1039;
        pointer-events: none; /* F09-0014: não bloqueia cliques quando fechado */
    }
    
    body.device-mobile .sidebar-overlay.show {
        display: block;
        pointer-events: auto; /* F09-0014: só captura cliques quando aberto */
    }
    
    /* 🔴 NÃO exibimos nenhum .mobile-hamburger externo */
    body.device-mobile .mobile-hamburger {
        display: none !important;
    }
    
    /* 🔴 Mantemos o header interno da sidebar VISÍVEL em mobile */
    body.device-mobile .sidebar-toggle-header {
        display: flex !important;
    }
    
    /* Page Header */
    body.device-mobile .page-header {
        padding: 13.5px 10px;
    }
    
    body.device-mobile .page-title {
        font-size: 1.1rem;
    }
    
    body.device-mobile .page-subtitle {
        font-size: 0.7rem;
    }
    
    /* Filter bar */
    body.device-mobile .filter-bar {
        padding: 9px;
    }
    
    body.device-mobile .filter-bar .row {
        margin: 0 !important;
    }
    
    /* REMOVIDO: col-md-X stacka naturalmente abaixo de 768px (Bootstrap) */

    /* Content area */
    body.device-mobile .content-area {
        --content-pad-y: 13.5px;  /* ver "ABAS FIXAS" */
        padding: 13.5px 10px;
    }

    /* Cards */
    body.device-mobile .card {
        margin-bottom: 13.5px;
    }
    
    body.device-mobile .card-body {
        padding: 13.5px;
    }
    
    /* KPI Cards */
    body.device-mobile .kpi-value {
        font-size: 1.3rem !important;
    }
    
    body.device-mobile .kpi-label {
        font-size: 0.7rem !important;
    }
    
    /* Tabelas */
    body.device-mobile .table {
        font-size: 12px;
    }
    
    body.device-mobile .table thead th {
        padding: 7.2px 4px;
        font-size: 12px;
    }
    
    body.device-mobile .table tbody td {
        padding: 7.2px 4px;
        font-size: 12px;
    }

    /* Tabelas no mobile: rolagem horizontal nativa do .table-responsive (Bootstrap).
       Thead sticky funciona dentro de .table-responsive com max-height. */
    body.device-mobile .table-responsive {
        -webkit-overflow-scrolling: touch;
    }

    /* EXCLUI .table-grouped-head: nela quem gruda é o <thead> inteiro, justamente
       porque o cabeçalho tem duas linhas. Grudar cada <th> aqui reintroduzia no
       celular o defeito que o utilitário resolve — ver "CÉLULAS CONGELADAS".

       EXCLUI também as tabelas que declaram congelamento PRÓPRIO (a mesma lista da
       regra de 1ª coluna sticky do mobile, mais abaixo): esta regra é genérica mas
       tem especificidade ALTA (cada :not() conta como classe), então o `z-index: 10`
       daqui vencia o 12/11 que .so-sticky-cols dá às células de cabeçalho
       congeladas. Empatadas em 10, as células que ROLAM — posteriores no DOM —
       pintavam POR CIMA do cabeçalho congelado: no celular a 1ª coluna continuava
       congelada no corpo, mas o cabeçalho dela sumia ao rolar de lado (Sales Orders,
       Pipeline e o andamento do financeiro). O `background: #fff` daqui também
       apagava o tom próprio do cabeçalho congelado (--tbl-frozen-head-bg).
       Nenhuma delas perde o sticky-top: todas vivem dentro de `.table-responsive`,
       cuja regra global já gruda o <th> em top:0. */
    body.device-mobile .table:not(.table-grouped-head):not(.so-sticky-cols):not(.sales-analysis-table):not(.forecast-table):not(.forecast-review-table) thead th {
        position: sticky;
        top: 0;
        z-index: 10;
        background: #fff;
        box-shadow: 0 2px 2px -1px rgba(0, 0, 0, 0.1);
    }

    /* Botões — min-height 44px para touch target (Apple HIG / Material Design) */
    body.device-mobile .btn {
        font-size: 14px;
        padding: 10px 16px;
        min-height: 44px;
    }

    body.device-mobile .btn-sm {
        font-size: 13px;
        padding: 8px 12px;
        min-height: 44px;
    }

    /* Forms — font-size 16px obrigatório no iOS pra evitar auto-zoom */
    body.device-mobile .form-label {
        font-size: 13px;
        margin-bottom: 4.5px;
    }

    body.device-mobile .form-control,
    body.device-mobile .form-select {
        font-size: 16px;
        padding: 10px 12px;
        min-height: 44px;
    }

    body.device-mobile .form-control-sm,
    body.device-mobile .form-select-sm {
        font-size: 16px; /* mantém 16px no mobile pra não dar auto-zoom no iOS */
        padding: 8px 10px;
        min-height: 44px;
    }
    
    /* Modals */
    body.device-mobile .modal-content {
        margin: 9px;
        max-height: 90vh;
        overflow-y: auto;
    }
    
    body.device-mobile .modal-header h2 {
        font-size: 16.2px;
    }
    
    body.device-mobile .modal-body {
        padding: 13.5px;
    }
    
    /* Messages */
    body.device-mobile .messages {
        top: 58.5px;
        right: 9px;
        left: 9px;
        max-width: 100%;
    }
    
    body.device-mobile .message {
        font-size: 11.7px;
        padding: 9px 15px;
    }
    
    /* Dashboard Kanban - Gridstack Mobile */
    body.device-mobile .grid-stack {
        margin: 0 !important;
    }
    
    body.device-mobile .grid-stack-item {
        position: relative !important;
        width: 100% !important;
        left: 0 !important;
        margin-bottom: 13.5px !important;
    }
    
    body.device-mobile .grid-stack-item-content {
        position: relative !important;
    }
    
    body.device-mobile .kanban-card-header {
        padding: 9px 15px;
        font-size: 12.6px;
    }
    
    body.device-mobile .kanban-card-body {
        padding: 9px;
    }
    
    /* Plotly charts mobile */
    body.device-mobile #plotly-chart {
        height: 270px !important;
    }
    
    /* Hide grip icons em mobile */
    body.device-mobile .grip-icon {
        display: none;
    }
    
    /* Action buttons: NÃO empilham (ver a seção "Action buttons in list/table
       rows" — o empilhamento full-width foi medido e removido em 28/08/2026). */

/* Mobile pequeno (até 480px) - SMARTPHONES PEQUENOS */


/* Landscape mobile */


/* ================================================================ */
/* iOS SPECIFIC FIXES - iPhone Safe Area & Chrome */
/* ================================================================ */

/* Garantir que elementos respeitem safe area no iOS */
@supports (padding-top: env(safe-area-inset-top)) {
    
}

/* Chrome iOS específico - Webkit fix */
@supports (-webkit-touch-callout: none) {
    
}

/* ============================================
   MOBILE TOP GAP NUKE v2
   Remove QUALQUER gap entre topbar e content no mobile
   ============================================ */


/* ============================================
   MOBILE TOP GAP FINAL FIX
   Mata margin/padding responsivo tipo mt-4, pt-4 no page-header
   ============================================ */


/* ============================================
   MOBILE HEADER TIGHT: Remove o "colchão" branco antes do título
   SOLUÇÃO DEFINITIVA para gap mobile
   ============================================ */


/* ============================================
   MOBILE GAP KILLER: Remove spacer fantasma (::before)
   CAUSA RAIZ: pseudo-elemento ::before criando barra vazia
   ============================================ */


/* ============================================
   MOBILE HEADER HEIGHT FIX: Remove o "gap visual"
   CAUSA RAIZ: min-height + align-items:center fazem título ficar pendurado no meio
   ============================================ */


/* ============================================
   F09-0012-2: DARK MODE STYLES
   ============================================ */
body.dark-mode {
    background-color: var(--dm-bg, #1a1a1a);
    color: var(--dm-text, #e0e0e0);

    /* Grade/fundos das células congeladas — ver "CÉLULAS CONGELADAS". Trocar só
       as variáveis aqui já cobre todo congelamento do sistema no dark mode. */
    --tbl-grid: var(--dm-border, #505050);
    --tbl-frozen-bg: var(--dm-surface, #2d2d2d);
    --tbl-frozen-head-bg: var(--dm-elevated, #404040);
    --tbl-frozen-hover-bg: var(--dm-elevated, #404040);
    --tbl-frozen-shadow: 2px 0 5px rgba(0, 0, 0, 0.5);
}

body.dark-mode .topbar {
    background-color: var(--dm-surface, #2d2d2d);
    border-bottom-color: var(--dm-elevated, #404040);
}

body.dark-mode .sidebar {
    background-color: var(--dm-surface, #2d2d2d);
}

body.dark-mode .sidebar-menu-link {
    color: var(--dm-text, #e0e0e0);
}

body.dark-mode .sidebar-menu-link:hover {
    background-color: var(--dm-elevated, #404040);
}

body.dark-mode .sidebar-submenu-link {
    color: var(--dm-text-muted, #b0b0b0);
}

body.dark-mode .sidebar-submenu-link:hover {
    background-color: var(--dm-elevated, #404040);
    color: #ffffff;
}

body.dark-mode .main-content {
    background-color: var(--dm-bg, #1a1a1a);
}

body.dark-mode .page-header-sticky {
    background-color: var(--dm-surface, #2d2d2d);
    border-bottom-color: var(--dm-elevated, #404040);
}

body.dark-mode .filter-bar {
    background-color: var(--dm-surface, #2d2d2d);
}

body.dark-mode .content-area {
    background-color: var(--dm-bg, #1a1a1a);
}

body.dark-mode .card {
    background-color: var(--dm-surface, #2d2d2d);
    border-color: var(--dm-elevated, #404040);
}

/* Membros do time (Management › Teams): 4 etiquetas por linha no desktop e 2 no
   celular. Em fila única os nomes esticavam a coluna e espremiam o resto da
   tabela.

   Colunas em `max-content` + `justify-self: start`: a grade só controla QUANTAS
   cabem por linha; cada etiqueta fica do tamanho do nome, como era antes. Com
   coluna `1fr` a etiqueta era esticada até a largura da coluna. Quando a soma
   não couber, quem rola é o .table-responsive, como nas outras listas. */
.team-members-grid {
    display: grid;
    grid-template-columns: repeat(4, max-content);
    justify-content: start;
    gap: 4px 8px;
}
.team-members-grid .badge {
    justify-self: start;
}
@media (max-width: 767px) {
    .team-members-grid { grid-template-columns: repeat(2, max-content); }
}

/* Abas (.nav-tabs) — modo escuro. A aba é sublinhada (sem caixa), então aqui
   só trocam as cores: a linha de base, o texto e o traço de hover. O traço da
   ativa é o `--primary-color`, que já contrasta nos dois temas. O `:not(.active)`
   no hover evita que o cinza da borda apague o traço azul da aba ativa. */
body.dark-mode .nav-tabs {
    border-bottom-color: var(--dm-border, #505050);
}
body.dark-mode .nav-tabs .nav-link {
    color: var(--dm-text-muted, #b0b0b0);
    background: transparent;
}
body.dark-mode .nav-tabs .nav-link:hover:not(.active) {
    border-bottom-color: var(--dm-border, #505050);
    color: var(--dm-text, #e0e0e0);
}
body.dark-mode .nav-tabs .nav-link.active {
    background: transparent;
    border-bottom-color: var(--primary-light, #3385c6);
    color: var(--primary-light, #3385c6);
}

/* A barra no cabeçalho fixo herda o `background: white` da regra clara; sem
   este override ela fica uma faixa branca no tema escuro. */
body.dark-mode .page-header-sticky .nav-tabs {
    background-color: var(--dm-surface, #2d2d2d);
    border-top-color: var(--dm-border, #505050);
}

/* !important necessário para sobrescrever inline style="background:..." em card-headers */
body.dark-mode .card-header {
    background-color: #363636 !important;
    border-bottom-color: var(--dm-border, #505050);
    color: var(--dm-text, #e0e0e0);
}

body.dark-mode .table {
    color: var(--dm-text, #e0e0e0);
    background-color: var(--dm-surface, #2d2d2d);
}

/* !important cobre theads com inline style="background-color:#f8f9fa" */
body.dark-mode .table thead th {
    background-color: var(--dm-elevated, #404040) !important;
    color: var(--dm-text, #e0e0e0) !important;
    border-color: var(--dm-border, #505050) !important;
}

/* Dark mode - thead sticky (table-responsive ou overflow container manual) */
body.dark-mode .table-responsive thead th {
    background-color: var(--dm-elevated, #404040) !important;
    color: var(--dm-text, #e0e0e0);
    border-bottom: 2px solid var(--dm-border, #505050);
}

body.dark-mode .table tbody td {
    border-color: var(--dm-elevated, #404040);
    background-color: var(--dm-surface, #2d2d2d);
}

/* ── Bootstrap table variant banding — dark mode ────────────────────────────
   body.dark-mode .table tbody td (specificity 0,2,3) overrides Bootstrap's
   var(--bs-table-bg) mechanism. Estes seletores têm especificidade 0,3,3+
   e !important para preservar o banding semântico de cada variante.        */
body.dark-mode .table tr.table-dark td,
body.dark-mode .table tr.table-dark th {
    background-color: var(--dm-bg, #1a1a1a) !important;
    color: #d0d0d0 !important;
    border-color: var(--dm-border, #505050) !important;
}
body.dark-mode .table tr.table-secondary td,
body.dark-mode .table tr.table-secondary th {
    background-color: rgba(108, 117, 125, 0.22) !important;
    color: #c8cdd2 !important;
    border-color: rgba(108, 117, 125, 0.3) !important;
}
body.dark-mode .table tr.table-danger td,
body.dark-mode .table tr.table-danger th {
    background-color: rgba(220, 53, 69, 0.18) !important;
    color: #f5bcc0 !important;
    border-color: rgba(220, 53, 69, 0.25) !important;
}
body.dark-mode .table tr.table-success td,
body.dark-mode .table tr.table-success th {
    background-color: rgba(25, 135, 84, 0.2) !important;
    color: #a8d5b8 !important;
    border-color: rgba(25, 135, 84, 0.28) !important;
}
body.dark-mode .table tr.table-light td,
body.dark-mode .table tr.table-light th {
    background-color: rgba(255, 255, 255, 0.05) !important;
    color: #c0c0c0 !important;
    border-color: #484848 !important;
}
body.dark-mode .table tr.table-warning td,
body.dark-mode .table tr.table-warning th {
    background-color: rgba(255, 193, 7, 0.15) !important;
    color: #fbe6a0 !important;
    border-color: rgba(255, 193, 7, 0.22) !important;
}
body.dark-mode .table tr.table-info td,
body.dark-mode .table tr.table-info th {
    background-color: rgba(13, 202, 240, 0.15) !important;
    color: #a0e8f8 !important;
    border-color: rgba(13, 202, 240, 0.22) !important;
}
body.dark-mode .table tr.table-primary td,
body.dark-mode .table tr.table-primary th {
    background-color: rgba(13, 110, 253, 0.18) !important;
    color: #c0d4fc !important;
    border-color: rgba(13, 110, 253, 0.25) !important;
}
/* Variantes em células individuais (td/th com classe diretamente) */
body.dark-mode .table td.table-dark, body.dark-mode .table th.table-dark { background-color: var(--dm-bg, #1a1a1a) !important; color: #d0d0d0 !important; }
body.dark-mode .table td.table-secondary, body.dark-mode .table th.table-secondary { background-color: rgba(108,117,125,0.22) !important; color: #c8cdd2 !important; }
body.dark-mode .table td.table-danger, body.dark-mode .table th.table-danger { background-color: rgba(220,53,69,0.18) !important; color: #f5bcc0 !important; }
body.dark-mode .table td.table-success, body.dark-mode .table th.table-success { background-color: rgba(25,135,84,0.2) !important; color: #a8d5b8 !important; }
body.dark-mode .table td.table-light, body.dark-mode .table th.table-light { background-color: rgba(255,255,255,0.05) !important; color: #c0c0c0 !important; }
body.dark-mode .table td.table-warning, body.dark-mode .table th.table-warning { background-color: rgba(255,193,7,0.15) !important; color: #fbe6a0 !important; }
body.dark-mode .table td.table-info, body.dark-mode .table th.table-info { background-color: rgba(13,202,240,0.15) !important; color: #a0e8f8 !important; }
body.dark-mode .table td.table-primary, body.dark-mode .table th.table-primary { background-color: rgba(13,110,253,0.18) !important; color: #c0d4fc !important; }

/* thead com inline style="background:..." — cobrir o elemento pai além das células */
body.dark-mode .table thead {
    background-color: var(--dm-elevated, #404040) !important;
}

body.dark-mode .table-hover tbody tr:hover {
    background-color: var(--dm-elevated, #404040);
}

body.dark-mode .table tfoot td,
body.dark-mode .table tfoot th {
    background-color: #363636 !important;
    color: var(--dm-text, #e0e0e0);
    border-color: var(--dm-border, #505050);
}

/* ============================================================================
   TABELAS DE FORECAST
   ============================================================================ */

/* Coluna Total e banding de colunas — light mode */
.total-col {
    background-color: #e9ecef;
    border-left: 2px solid #dee2e6;
}
.table td.month-band,
.table th.month-band {
    background-color: #cfe2ff !important;
}
tfoot td {
    border-top: 2px solid #adb5bd;
}
.forecast-review-table td.group-cell {
    vertical-align: middle;
    font-weight: 500;
}
/* Separador visual após colunas fixas — aplicado via box-shadow no nth-child(3) (ver sticky rules abaixo) */

/* Estilo uniforme para colunas fixas em forecast-table (sem group-cell) */
.forecast-table tbody td:nth-child(1),
.forecast-table tbody td:nth-child(2),
.forecast-table tbody td:nth-child(3),
.forecast-table tfoot td:nth-child(1),
.forecast-table tfoot td:nth-child(2) {
    vertical-align: middle;
}
.forecast-table tbody td:nth-child(1) {
    font-weight: 500;
}

/* Banding trimestral — via classe .month-band em <th> e <td> de cada template */

/* ── table-layout: fixed + larguras de coluna via <colgroup> ─────────────────
   Garante que os offsets left dos sticky sejam exatos.
   Classes para <col> no <colgroup> de cada template de forecast.               */
.forecast-table,
.forecast-review-table {
    table-layout: fixed;
    /* As larguras das colunas fixas viram VARIÁVEIS porque os offsets `left` do
       congelamento saem delas por calc() logo abaixo. Enquanto eram números
       soltos nos dois lugares eles saíam de sincronia: o badge estava em 320px
       para uma soma real de 90+160+90 = 340px, e no modo revenue (preço 110px)
       a diferença ia para 360px — a coluna congelada parava em cima da anterior. */
    --fc-w-tipo: 90px;
    --fc-w-produto: 160px;
    --fc-w-preco: 90px;
    --fc-w-badge: 50px;
    --fc-w-month: 80px;
    --fc-w-total: 100px;
}
.fc-col-tipo    { width: var(--fc-w-tipo, 90px); }
.fc-col-produto { width: var(--fc-w-produto, 160px); }
.fc-col-preco   { width: var(--fc-w-preco, 90px); }
.fc-col-badge   { width: var(--fc-w-badge, 50px); }
.fc-col-month   { width: var(--fc-w-month, 80px); }
.fc-col-total   { width: var(--fc-w-total, 100px); }
.fc-col-update  { width: 110px; }
.fc-col-actions { width: 140px; }

/* Forecast vs Review (sales_reports): tela só de leitura, sem os botões que a
   lista de Gestão tem na mesma classe — as 4 primeiras colunas encolhem para o
   tamanho do conteúdo (medido: 76/117/94/48px) em vez de 90/160/90/50. */
.forecast-review-table.table-compact {
    --fc-w-tipo: 78px;
    --fc-w-produto: 118px;
    --fc-w-preco: 88px;
    --fc-w-badge: 48px;
}
.forecast-review-table.table-compact.revenue-view {
    --fc-w-preco: 100px;
    --fc-w-month: 96px;
    --fc-w-total: 116px;
}

/* ── Action buttons in list/table rows ──────────────────────────────
   Left-aligned so the first button of every row starts at the same
   column. Replaces the per-template `d-flex gap-2 justify-content-center`.

   EM LINHA TAMBÉM NO CELULAR. Até 28/08/2026 havia
   `body.device-mobile .action-buttons { flex-direction: column }` com
   `.btn { width: 100% }`. A premissa era poupar largura, mas ela não se
   sustenta: das 15 telas que usam a classe, 13 JÁ rolam na horizontal no
   celular (medido a 390px: de 15px a 1.481px de sobra), então a largura da
   coluna nunca foi o gargalo. Nas 2 que cabiam (isp-types, segments) voltar
   para linha custa 37–40px de rolagem lateral e devolve METADE da altura da
   linha. O custo do empilhamento era alto: 108px por linha com 2 botões,
   156px com 3, 253px em sales-orders (5 botões) — ou seja, 2 linhas por tela
   contra 8. Alvo de toque não era o motivo: `body.device-mobile .btn` já dá
   `min-height: 44px`, e 64×44px passa no mínimo do Apple HIG sem o
   `width: 100%`. Empilhar só faria sentido em lista que vira CARTÃO no
   celular — padrão que não existe neste sistema (as regras de
   `body.device-mobile .table` mantêm a tabela como tabela). */
.action-buttons {
    display: flex;
    flex-wrap: nowrap;
    gap: 0.5rem;
    justify-content: flex-start;
    align-items: center;
    white-space: nowrap;
}

/* Destructive actions must POST (CSRF + no prefetch), which means wrapping the button
   in a <form>. `display: contents` drops the form from the box tree so its button stays
   a direct flex item of .action-buttons and lines up exactly like a plain <a> button. */
.action-buttons .action-form {
    display: contents;
}


/* Revenue view carries large money values — widen the numeric columns so they
   don't overlap. The table scrolls horizontally (table-responsive) while the
   Type/Product/Price columns stay sticky. */
.forecast-table.revenue-view,
.forecast-review-table.revenue-view {
    --fc-w-preco: 110px;
    --fc-w-month: 118px;
    --fc-w-total: 140px;
}

/* Keep numeric cells from spilling into neighbours if a value is still wider. */
.forecast-table td.data-cell,
.forecast-table td.col-total,
.forecast-table td.row-total,
.forecast-review-table td.data-cell,
.forecast-review-table td.col-total,
.forecast-review-table td.row-total {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* Truncar texto longo nas colunas fixas */
.forecast-table thead th:nth-child(1),
.forecast-table thead th:nth-child(2),
.forecast-table tbody td:nth-child(1),
.forecast-table tbody td:nth-child(2),
.forecast-review-table thead th:nth-child(1),
.forecast-review-table thead th:nth-child(2),
.forecast-review-table tbody td.group-cell:nth-child(1),
.forecast-review-table tbody td.group-cell:nth-child(2) {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

/* ── Colunas fixas à esquerda: Tipo / Produto / Preço ────────────────────────
   Tipo: 90px  |  Produto: 160px  |  Preço: 70px  (left offsets: 0 / 90 / 250)
   z-index thead: 2000  |  tbody/tfoot: 1000  (igual ao padrão sa-col-fixed)

   Fundo, cor da grade, hover e dark mode vêm da fundação (--tbl-frozen-*, ver
   "CÉLULAS CONGELADAS"): aqui ficam só os offsets e o z-index. O hover global já
   é OPACO, então `background-color` basta — o antigo truque
   `box-shadow: inset 0 0 0 9999px` (que existia só para furar a especificidade
   do hover translúcido) saiu.                                                 */

/* thead — congelado no topo E à esquerda, acima de tudo */
.forecast-table thead th:nth-child(-n+3),
.forecast-review-table thead th:nth-child(-n+3),
.forecast-review-table thead th.fc-badge {
    position: sticky;
    z-index: 2000;
    background-color: var(--tbl-frozen-head-bg);
}
/* tbody — .group-cell evita as células com rowspan das linhas pares da review */
.forecast-table tbody td:nth-child(-n+3),
.forecast-review-table tbody td.group-cell:nth-child(-n+3),
.forecast-review-table tbody td.fc-badge {
    position: sticky;
    z-index: 1000;
    background-color: var(--tbl-frozen-bg);
}
/* tfoot: DOM col 1 = TOTAL (colspan 2), DOM col 2 = Preço vazio */
.forecast-table tfoot td:nth-child(-n+2),
.forecast-review-table tfoot td:nth-child(-n+2) {
    position: sticky;
    z-index: 1000;
    background-color: var(--tbl-frozen-head-bg);
}

/* offsets — iguais em thead/tbody; o tfoot tem uma coluna a menos (colspan) */
.forecast-table thead th:nth-child(1),
.forecast-review-table thead th:nth-child(1),
.forecast-table tbody td:nth-child(1),
.forecast-review-table tbody td.group-cell:nth-child(1),
.forecast-table tfoot td:nth-child(1),
.forecast-review-table tfoot td:nth-child(1) { left: 0; }

.forecast-table thead th:nth-child(2),
.forecast-review-table thead th:nth-child(2),
.forecast-table tbody td:nth-child(2),
.forecast-review-table tbody td.group-cell:nth-child(2) { left: var(--fc-w-tipo); }

/* o TOTAL do tfoot é um colspan 2, então a 2ª célula dele começa onde a 3ª
   coluna começa: mesma conta. */
.forecast-table thead th:nth-child(3),
.forecast-review-table thead th:nth-child(3),
.forecast-table tbody td:nth-child(3),
.forecast-review-table tbody td.group-cell:nth-child(3),
.forecast-table tfoot td:nth-child(2),
.forecast-review-table tfoot td:nth-child(2) { left: calc(var(--fc-w-tipo) + var(--fc-w-produto)); }

/* Coluna badge FCST/REV — só em forecast_review; fecha o bloco congelado */
.forecast-review-table thead th.fc-badge,
.forecast-review-table tbody td.fc-badge {
    left: calc(var(--fc-w-tipo) + var(--fc-w-produto) + var(--fc-w-preco));
}

/* Sombra separadora na divisa do congelamento. `!important` porque estas
   tabelas são `.table-hover`, e o hover do Bootstrap escreve `box-shadow` em
   `> tbody > tr:hover > *` (especificidade maior que estes seletores). */
.forecast-table thead th:nth-child(3),
.forecast-table tbody td:nth-child(3),
.forecast-table tfoot td:nth-child(2),
.forecast-review-table thead th:nth-child(3),
.forecast-review-table tbody td.group-cell:nth-child(3),
.forecast-review-table tfoot td:nth-child(2),
.forecast-review-table thead th.fc-badge,
.forecast-review-table tbody td.fc-badge {
    box-shadow: var(--tbl-frozen-shadow) !important;
}

/* ── Coluna "Channel" (perfis Both): a coluna extra desloca o nth-child e
   quebra os offsets fixos das colunas congeladas. Nesse modo, desligamos o
   congelamento e deixamos a tabela rolar normalmente (table-responsive).       */
.forecast-table.has-channel thead th,
.forecast-table.has-channel tbody td,
.forecast-table.has-channel tfoot td,
.forecast-review-table.has-channel thead th,
.forecast-review-table.has-channel tbody td,
.forecast-review-table.has-channel tfoot td {
    position: static !important;
    box-shadow: none !important;
}

/* Lista de produtos do modal Export Forecast (cabeçalho congelado vem do
   `.table-scroll`; aqui só a moldura e a altura). */
.fc-export-list {
    --table-scroll-h: 320px;
    border: 1px solid var(--tbl-grid);
    border-radius: 4px;
}

/* ============================================================================
   FILTER CARD — card de filtros reutilizável
   ============================================================================ */

.filter-card {
    background: white;
    padding: 20px;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    margin-bottom: 20px;
}
.filter-card label {
    font-weight: bold;
    margin-bottom: 5px;
    display: block;
    font-size: 14px;
}
.filter-card select {
    width: 100%;
    padding: 8px 12px;
    border: 1px solid #ddd;
    border-radius: 4px;
    font-size: 14px;
    margin-bottom: 15px;
}

/* ============================================================================
   SCHEDULE / CALENDÁRIO DE VIAGENS
   ============================================================================ */

/* A agenda é larga (PIC + um dia por coluna) e tem uma linha por PIC: rola nos
   DOIS eixos dentro do `.table-scroll` do template, com a linha de cabeçalho e a
   coluna PIC congeladas (`.freeze`) pela fundação — ver "CÉLULAS CONGELADAS".
   Por isso `separate`: com `collapse` a borda de 1px não acompanha a célula
   congelada e o conteúdo que rola por baixo aparece atravessado nela. */
.schedule-table {
    width: 100%;
    border-collapse: separate;
    border-spacing: 0;
}
/* Grade em `separate`: bordas de uma direção só (bottom+right), +top na 1ª linha
   e +left na 1ª coluna — mesmo desenho do `collapse`, sem dobrar para 2px. */
.schedule-table th,
.schedule-table td {
    border: 0 solid var(--tbl-grid);
    border-width: 0 1px 1px 0;
    padding: 10px;
    text-align: center;
    vertical-align: top;
    min-width: 150px;
}
.schedule-table > thead > tr:first-child > * { border-top-width: 1px; }
.schedule-table > * > tr > *:first-child { border-left-width: 1px; }
.schedule-table th {
    background: #f8f9fa;
    font-weight: 600;
}
.schedule-table th.today {
    background: var(--sched-today-bg, #2c3e50);
    color: var(--sched-today-text, #ffffff);
}
/* Coluna PIC (congelada): fundo OPACO — é ela que cobre os dias que rolam por baixo. */
.schedule-table th.pic-column,
.schedule-table td.pic-column {
    background: #f8f9fa;
    font-weight: 600;
    text-align: left;
    min-width: 150px;
    width: 150px;
}

@media (max-width: 991px) {
    .schedule-table { font-size: 0.85rem; }
    .schedule-table th,
    .schedule-table td { padding: 5px; min-width: 120px; }
    /* No celular a coluna congelada divide a tela com os dias: sem quebrar a
       linha, um nome comprido (`table { white-space: nowrap }` é global) estica
       a coluna para ~200px e não sobra espaço para rolar até os dias. */
    .schedule-table th.pic-column,
    .schedule-table td.pic-column {
        min-width: 110px;
        width: 110px;
        white-space: normal;
    }
}

/* Semana corrente no filtro Week: negrito na opção da lista E no campo fechado
   (o <select> herda a fonte do próprio elemento, não da <option> selecionada). */
option.current-week { font-weight: 700; }
.form-select.is-current-week { font-weight: 700; }

/* Cabeçalho de DUAS linhas (Upcoming Trips: "Status" mesclado sobre
   Flight/Hotel/Car Rental). Duas coisas mudam em relação a uma tabela comum:

   1) O sticky é do <thead> INTEIRO, não de cada célula. Pela regra global
      (.table-responsive thead th) todo <th> gruda sozinho em top:0: as duas
      linhas paravam no mesmo lugar, a de baixo cobria o "Status" e as células
      com rowspan — que pertencem às duas — ficavam soltas sobre o corpo.
   2) O `border-collapse: separate` que impede o vazamento hoje é global (ver
      "CÉLULAS CONGELADAS"); aqui só redeclaramos as bordas de linha, porque a
      borda de baixo do cabeçalho virou sombra no `<thead>`. */
.table-grouped-head tbody td {
    border-top: 1px solid #dee2e6;
    border-bottom: 0;
}
.table-grouped-head thead {
    position: sticky;
    top: 0;
    z-index: 10;
    /* Fundo no <thead>, não só nas células: cobre a faixa inteira do cabeçalho
       enquanto as linhas passam por baixo. */
    background-color: #fff;
    /* A borda de baixo vira SOMBRA. Com border-collapse a borda é pintada na
       camada de bordas da tabela, fora da caixa do thead grudado — era por ali
       que o conteúdo rolando vazava. A sombra anda junto com o elemento. */
    box-shadow: 0 2px 0 0 #dee2e6, 0 4px 3px -2px rgba(0, 0, 0, 0.1);
}
.table-grouped-head thead th {
    position: static;
    border-bottom: 0;
}
/* Linha fina só entre "Status" e as três subcolunas — esta fica DENTRO do
   thead, então não vaza. Especificidade alta porque a regra global (.table-
   responsive thead th) põe 2px em todo th. */
.table-grouped-head thead th.status-group-header { border-bottom: 1px solid #dee2e6; }

body.dark-mode .table-grouped-head thead {
    background-color: var(--dm-elevated, #404040);
    box-shadow: 0 2px 0 0 var(--dm-border, #505050), 0 4px 3px -2px rgba(0, 0, 0, 0.35);
}
body.dark-mode .table-grouped-head thead th { border-bottom: 0; }
body.dark-mode .table-grouped-head tbody td { border-top: 1px solid var(--dm-border, #505050); border-bottom: 0; }
body.dark-mode .table-grouped-head thead th.status-group-header { border-bottom: 1px solid var(--dm-border, #505050); }

/* Week calendar grid (schedule_card.html) */
.week-calendar {
    display: grid;
    grid-template-columns: repeat(7, 1fr);
    gap: 10px;
    margin-bottom: 20px;
}
.day-column {
    border: 1px solid #dee2e6;
    border-radius: 8px;
    background: white;
    min-height: 150px;
}
.day-header {
    background: #f8f9fa;
    padding: 10px;
    border-bottom: 1px solid #dee2e6;
    text-align: center;
    font-weight: 600;
}
.day-header.today {
    background: #2c3e50;
    color: white;
}
.day-date {
    font-size: 0.85rem;
    color: #6c757d;
}
.day-header.today .day-date { color: white; }
.day-body { padding: 10px; }

/* Chip de viagem da AGENDA semanal. Mesmas variáveis do chip do calendário —
   ver "Calendário de viagens" e a seção Calendar & Schedule do system.css. */
.trip-item {
    background: var(--cal-trip-bg, #e3f2fd);
    border-left: 3px solid var(--cal-trip-border, #2196f3);
    padding: 8px;
    margin-bottom: 8px;
    border-radius: 4px;
    font-size: 0.85rem;
}
.trip-item.vacation {
    background: #fff3e0;
    border-left-color: #ff9800;
}
.trip-item.training {
    background: #f3e5f5;
    border-left-color: #9c27b0;
}
.trip-owner {
    font-weight: 600;
    color: #2c3e50;
}
.trip-destination {
    color: #6c757d;
    font-size: 0.8rem;
}

/* Coluna Total do forecast e similares — dark mode */
body.dark-mode .total-col {
    background-color: #2a3a4a !important;
    border-left-color: var(--dm-border, #505050);
}

/* Banding de colunas em dark mode — via .month-band (ver light mode acima) */

/* ── Plotly charts em dark mode ──────────────────────────────────────────────
   SVG usa atributos de apresentação (não inline style), então CSS pode
   sobrescrevê-los com !important sem JS.
   Cobre: títulos, eixos, tick labels, legend text, annotations.         */
body.dark-mode .js-plotly-plot text {
    fill: #d0d0d0 !important;
}
/* Exceção: o mapa de rotas (trips.plan) desenha a própria massa de terra em
   #e8f4f8 — clara nos DOIS modos. O cinza claro acima sumia sobre ela, e as
   cores dos rótulos ali são semânticas: laranja = cidade atendida por via
   terrestre, azul marinho = aeroporto.
   ⚠️ A regra NÃO mora aqui: as cores são configuráveis em Administração do
   Sistema → CSS (chaves `trip_route_ground`/`trip_route_airport`), então o
   seletor — que casa pelo rgb() escrito inline pelo Plotly — é GERADO em
   `SystemCSS.generate_css()`. Um literal aqui deixaria de casar na primeira
   troca de cor, e o rótulo sumiria no escuro sem ninguém entender por quê. */
/* Linhas de grade */
body.dark-mode .js-plotly-plot .gridlayer path,
body.dark-mode .js-plotly-plot .gridlayer line {
    stroke: rgba(200, 200, 200, 0.12) !important;
}
/* Zero-line */
body.dark-mode .js-plotly-plot .zerolinelayer path,
body.dark-mode .js-plotly-plot .zerolinelayer line {
    stroke: rgba(200, 200, 200, 0.25) !important;
}
/* Borda do plot e eixos */
body.dark-mode .js-plotly-plot .crisp path.domain {
    stroke: rgba(200, 200, 200, 0.2) !important;
}

/* ── Inputs readonly/disabled — Bootstrap define #e9ecef, templates repetem inline ── */
body.dark-mode .form-control[readonly],
body.dark-mode .form-control:disabled {
    background-color: #353535 !important;
    color: #9a9a9a !important;
    border-color: var(--dm-border, #505050) !important;
}

/* ── Bulk-action bars (id=bulkBar) — inline style=#f8f9fa em vários templates ── */
body.dark-mode #bulkBar {
    background: #363636 !important;
    border-bottom-color: var(--dm-border, #505050) !important;
    color: var(--dm-text, #e0e0e0);
}

/* ── Banding de colunas — dark mode: apenas por classe, sem nth-child ── */
body.dark-mode .table td.month-band,
body.dark-mode .table th.month-band {
    background-color: #1e3052 !important;
}

/* ── Hover de grupo (forecast review) ── */
body.dark-mode .forecast-review-table tbody tr.group-row:hover td {
    background-color: rgba(99, 155, 255, 0.15) !important;
}

/* ── Linhas tbody com classe .table-light ── */
body.dark-mode .table tbody tr.table-light > td,
body.dark-mode .table tbody tr.table-light > th {
    background-color: #383838 !important;
    color: #c0c0c0 !important;
    border-color: var(--dm-border, #505050) !important;
}

/* ── Primeira coluna fixa em tabelas mensais (monthlyTable) ── */
body.dark-mode #monthlyTable tbody td:first-child {
    background-color: #363636 !important;
    color: var(--dm-text, #e0e0e0) !important;
}

/* ── Inputs desabilitados dentro de linhas .table-secondary ── */
body.dark-mode .table-secondary td input,
body.dark-mode .table-secondary th input {
    background-color: var(--dm-surface, #2d2d2d) !important;
    color: #9a9a9a !important;
    cursor: not-allowed;
}

/* ── Org chart ── */
body.dark-mode .orgchart {
    background: var(--dm-surface, #2d2d2d) !important;
}
body.dark-mode .orgchart .node {
    background-color: #363636 !important;
    color: var(--dm-text, #e0e0e0) !important;
    border-color: var(--primary-color) !important;
}
body.dark-mode .orgchart .lines .downLine,
body.dark-mode .orgchart .lines .rightLine,
body.dark-mode .orgchart .lines .leftLine {
    border-color: var(--dm-border, #505050) !important;
    background-color: var(--dm-border, #505050) !important;
}

body.dark-mode .form-control,
body.dark-mode .form-select {
    background-color: var(--dm-elevated, #404040);
    color: var(--dm-text, #e0e0e0);
    border-color: var(--dm-border, #505050);
}

body.dark-mode .form-control:focus,
body.dark-mode .form-select:focus {
    background-color: #4a4a4a;
    border-color: var(--primary-color);
    color: var(--dm-text, #e0e0e0);
}

body.dark-mode .btn-primary {
    background-color: var(--primary-color);
    border-color: var(--primary-color);
}

body.dark-mode .btn-secondary {
    background-color: var(--dm-border, #505050);
    border-color: var(--dm-border, #505050);
}

/* Outline variants on dark: the light-theme inks (#0067b5, #6c757d, #dc3545) sit too
   close to the dark surface, so lift them to the lighter tints. */
body.dark-mode .btn-outline-primary {
    border-color: var(--primary-light, #3385c6);
    color: var(--primary-light, #3385c6);
}

body.dark-mode .btn-outline-primary:hover {
    background-color: var(--primary-light, #3385c6);
    color: #fff;
}

body.dark-mode .btn-outline-secondary {
    border-color: var(--dm-border, #505050);
    color: var(--dm-text, #e0e0e0);
}

body.dark-mode .btn-outline-secondary:hover {
    background-color: var(--dm-border, #505050);
    color: var(--dm-text, #e0e0e0);
}

body.dark-mode .btn-outline-danger {
    border-color: #e4606d;
    color: #e4606d;
}

body.dark-mode .btn-outline-danger:hover {
    background-color: #e4606d;
    color: #fff;
}

/* !important necessário: templates de card têm <style> embutido com background:white */
body.dark-mode .dashboard-card {
    background-color: var(--dm-surface, #2d2d2d) !important;
}

/* grid-stack widget wrapper e filter-card — backgrounds white hardcoded nos templates */
body.dark-mode .grid-stack-item-content {
    background: var(--dm-surface, #2d2d2d) !important;
}

body.dark-mode .filter-card {
    background: var(--dm-surface, #2d2d2d) !important;
    color: var(--dm-text, #e0e0e0) !important;
    border-color: var(--dm-border, #505050) !important;
}

body.dark-mode .dashboard-card-body,
body.dark-mode .kanban-card-body {
    background-color: var(--dm-surface, #2d2d2d) !important;
    color: var(--dm-text, #e0e0e0);
}

/* Fundo SVG do Plotly — rect.bg é o paper/plot background gerado pelo Plotly */
body.dark-mode .js-plotly-plot .bg {
    fill: var(--dm-surface, #2d2d2d) !important;
}

/* Tooltip hover — .hoverlayer contém APENAS elementos de hover (tooltip principal,
   rótulo <extra> e spike labels). Targeting sem restrição de classe filha é seguro. */
body.dark-mode .js-plotly-plot .hoverlayer path {
    fill: #1e1e1e !important;
    stroke: #555555 !important;
}
body.dark-mode .js-plotly-plot .hoverlayer text {
    fill: #ffffff !important;
}

body.dark-mode .dashboard-card-header,
body.dark-mode .kanban-card-header {
    background-color: var(--dm-elevated, #404040);
    color: var(--dm-text, #e0e0e0);
    border-bottom-color: var(--dm-border, #505050);
}

/* Forecast mensal (management/forecast_review_form_monthly.html) — <style> inline claro */
body.dark-mode #monthlyTable tbody td:first-child { background-color: var(--dm-elevated, #404040); }
body.dark-mode .table-secondary input { background-color: var(--dm-elevated, #404040); color: var(--dm-text, #e0e0e0); }

body.dark-mode .text-muted {
    color: #999999 !important;
}

/* O .form-text do Bootstrap é cinza-escuro (rgba(33,37,41,.75)): em cima do
   fundo escuro de modal/card ele some. Mesmo tom do .text-muted acima. */
body.dark-mode .form-text {
    color: var(--dm-text-muted, #b0b0b0);
}

/* ── Calendário de viagens (trip_schedule.html, calendar_cards.html) ──
   COR daqui em diante sai de VARIÁVEL, e a variável é editável em
   System Administration › CSS › Calendar & Schedule. Os valores no `var(..., x)`
   são o fallback de fábrica — os mesmos de antes, então nada muda até alguém
   editar. O modo escuro NÃO repete as regras: o system.css redefine as mesmas
   variáveis dentro de body.dark-mode (e o bloco `body.dark-mode` logo abaixo faz
   o mesmo, para o caso de o system.css não carregar). */
.calendar-week-label {
    text-align: center;
    background-color: var(--cal-week-bg, #f8f9fa);
    color: var(--cal-week-text, #212529);
    font-weight: bold;
    vertical-align: middle;
}
.calendar-day-cell {
    vertical-align: top;
    height: 100px;
}
.calendar-day-number {
    font-weight: bold;
    margin-bottom: 5px;
}
.today-cell {
    background-color: var(--cal-today-bg, #fff3cd);
}
/* Chip de viagem DO CALENDÁRIO. Irmão do .trip-item da agenda semanal: mesmas
   cores (mesmas variáveis), tamanho menor porque divide a célula com o dia
   inteiro. Antes era style inline no template, que nenhuma folha vencia. */
.calendar-trip {
    font-size: 0.7rem;
    padding: 2px 4px;
    margin-bottom: 2px;
    border-radius: 2px;
    background-color: var(--cal-trip-bg, #e7f3ff);
    border-left: 3px solid var(--cal-trip-border, #0d6efd);
}
.calendar-trip-destination {
    color: var(--cal-trip-accent, #198754);
}
/* Fallback de modo escuro: se o system.css não carregar, estas variáveis
   mantêm o calendário legível no escuro. Quando ele carrega, sobrepõe. */
body.dark-mode {
    --cal-week-bg: #3d3d3d;
    --cal-week-text: #c0c0c0;
    --cal-today-bg: #3a3320;
    --cal-trip-bg: #1e3a5f;
    --cal-trip-border: #4a90d9;
    --cal-trip-accent: #75d88a;
    --cal-event-personal-bg: #3d3520;
    --cal-event-personal-border: #9a9a9a;
    --cal-event-global-bg: #3d3520;
    --cal-event-global-border: #e0a800;
    --cal-event-holiday-bg: #3b2020;
    --cal-event-holiday-border: #e05c6a;
    --sched-today-bg: #1a2f45;
    --sched-today-text: #e0e0e0;
}
body.dark-mode .calendar-week-label {
    background-color: var(--cal-week-bg) !important;
    color: var(--cal-week-text) !important;
}
body.dark-mode .today-cell {
    background-color: var(--cal-today-bg) !important;
}
body.dark-mode .calendar-trip {
    color: var(--dm-text, #e0e0e0);
}

/* ── Eventos de agenda (submenu trips.events) ──────────────────────────────
   Desenhados no calendário mensal (trip_schedule.html), na agenda semanal
   (schedule_updated.html) e no aviso do formulário de viagem, lado a lado com
   as viagens. Cor própria porque dividem a célula com elas: viagem é azul,
   evento é âmbar — e o FERIADO é vermelho, porque não é "mais um evento": é o
   dia em que ninguém trabalha.
   Cor por variável: editável em System Administration › CSS › Calendar &
   Schedule (os valores do var(..., x) são o fallback de fábrica). */
.event-item {
    background-color: var(--cal-event-global-bg, #fff8e1);
    border-left: 3px solid var(--cal-event-global-border, #f0ad4e);
    border-radius: 2px;
    padding: 2px 4px;
    margin-bottom: 2px;
    font-size: 0.72rem;
    line-height: 1.25;
}
.event-item.event-personal {
    background-color: var(--cal-event-personal-bg, #fff8e1);
    border-left-color: var(--cal-event-personal-border, #6c757d);
}
.event-item.event-holiday {
    background-color: var(--cal-event-holiday-bg, #fdecea);
    border-left-color: var(--cal-event-holiday-border, #dc3545);
}
.event-item .event-name {
    font-weight: 600;
}
/* Selo do tipo na LISTA de eventos e no modal de aviso: a mesma cor que
   identifica o tipo no calendário, aqui como fundo sólido. */
.badge.event-badge-personal {
    background-color: var(--cal-event-personal-border, #6c757d) !important;
    color: #fff !important;
}
.badge.event-badge-global {
    background-color: var(--cal-event-global-border, #f0ad4e) !important;
    color: #212529 !important;
}
.badge.event-badge-holiday {
    background-color: var(--cal-event-holiday-border, #dc3545) !important;
    color: #fff !important;
}
/* Mesmo chip, tamanho de leitura — usado no modal de aviso do formulário de
   viagem, onde o texto não divide espaço com um dia inteiro de calendário.
   (O .list-group do Bootstrap ficava branco no modo escuro; o chip já sabe se
   virar nos dois modos.) */
.event-item-lg {
    font-size: 0.9rem;
    padding: 8px 10px;
    margin-bottom: 6px;
}
.event-item .event-scope {
    color: #6c757d;
}
/* No escuro só o TEXTO precisa de regra: fundo e barra já vêm das variáveis
   redefinidas em body.dark-mode (acima e no system.css), então o chip troca de
   cor sem duplicar uma regra por tipo. */
body.dark-mode .event-item {
    color: var(--dm-text, #e0e0e0) !important;
}
body.dark-mode .event-item .event-scope {
    color: var(--dm-text-muted, #b0b0b0) !important;
}

/* ── Schedule table (schedule_card.html — não usa classe .table) ── */
body.dark-mode .schedule-table th,
body.dark-mode .schedule-table td {
    background-color: #363636 !important;
    border-color: var(--dm-border, #505050) !important;
    color: var(--dm-text, #e0e0e0) !important;
}
body.dark-mode .schedule-table th.today,
body.dark-mode .schedule-table td.today {
    background-color: var(--sched-today-bg, #1a2f45) !important;
    color: var(--sched-today-text, #e0e0e0) !important;
}
body.dark-mode .schedule-table th.pic-column,
body.dark-mode .schedule-table td.pic-column {
    background-color: var(--dm-elevated, #404040) !important;
}
/* Week calendar */
body.dark-mode .day-column {
    background-color: #363636 !important;
    border-color: var(--dm-border, #505050) !important;
}
body.dark-mode .day-header {
    background-color: var(--dm-elevated, #404040) !important;
    color: var(--dm-text, #e0e0e0) !important;
    border-bottom-color: var(--dm-border, #505050) !important;
}
body.dark-mode .day-header.today {
    background-color: #1a2f45 !important;
}
body.dark-mode .trip-owner {
    color: #d0d0d0 !important;
}
body.dark-mode .trip-destination {
    color: #9a9a9a !important;
}
body.dark-mode .trip-item {
    background-color: var(--cal-trip-bg) !important;
    border-left-color: var(--cal-trip-border) !important;
    color: var(--dm-text, #e0e0e0) !important;
}
body.dark-mode .trip-item.vacation {
    background-color: #3d2b00 !important;
    border-left-color: #e08a00 !important;
}
body.dark-mode .trip-item.training {
    background-color: #2d1a3d !important;
    border-left-color: #9c27b0 !important;
}

/* F09-0013-3: Dark Mode Corrections */

/* F09-0013-3.3: Título ISP Dashboard em branco */
body.dark-mode .topbar-title {
    color: #ffffff !important;
}

/* F09-0013-3.4: Menu do usuário fechado com preenchimento branco */
body.dark-mode .user-menu-btn {
    background-color: #ffffff !important;
    color: #000000 !important;
}

body.dark-mode .user-menu-btn:hover {
    background-color: #f0f0f0 !important;
}

/* F09-0013-3.1: Sticky headers */
body.dark-mode .page-header {
    background-color: var(--dm-surface, #2d2d2d);
    color: var(--dm-text, #e0e0e0);
}

body.dark-mode .page-title {
    color: var(--dm-text, #e0e0e0);
}

body.dark-mode .page-subtitle {
    color: var(--dm-text-muted, #b0b0b0);
}

/* ============================================
   F09-0014-1: MOBILE STYLES (django-user-agents)
   Baseado em body.device-mobile ao invés de @media queries
   ============================================ */

/* Layout principal mobile */
body.device-mobile {
    font-size: 12.6px;
    overflow-x: hidden !important;
}

/* (Blocos duplicados de .main-content/.page-container/.topbar/.topbar-title/.topbar-logo/.page-header/.page-title/.page-subtitle removidos — versões canônicas estão acima nas linhas 1359-1500) */

body.device-mobile .content-area {
    --content-pad-y: 13.5px;  /* ver "ABAS FIXAS" */
    padding: 13.5px 10px;
}

body.device-mobile .filter-bar {
    padding: 9px;
}

/* REMOVIDO: 2 blocos forçando col-md-X a 100% no mobile — Bootstrap já faz isso
   automaticamente abaixo de 768px. As regras quebravam col-4/col-6 dos filter-bars. */

/* REMOVIDO: regra antiga forçava TODOS col-* (inclusive col-4, col-6 intencionalmente
   horizontais) a empilhar verticalmente. Bootstrap já faz isso corretamente:
   col-md-X stacka naturalmente em viewport < 768px (device-mobile). col-X foi escolhido
   deliberadamente para ficar horizontal em todas as larguras. */

body.device-mobile .mobile-hamburger {
    top: calc(6.3px + env(safe-area-inset-top, 0));
}

body.device-mobile .messages {
    top: 58.5px;
    right: 9px;
    left: 9px;
    max-width: 100%;
}

/* Mobile gap fixes */
body.device-mobile .page-header-sticky {
    top: 0 !important;
    margin-top: 0 !important;
    padding-top: 0 !important;
}

body.device-mobile .page-header-sticky .page-header {
    margin-top: 0 !important;
    padding-top: 0 !important;
    padding-bottom: 14.4px !important;
    padding-left: 18px !important;
    padding-right: 18px !important;
    background: #fff !important;
}

body.device-mobile .main-content > *:first-child {
    margin-top: 0 !important;
    padding-top: 0 !important;
}

/* F09-0014-2.2: Dark Mode - Sticky headers no mobile */
body.device-mobile.dark-mode .page-header-sticky {
    background-color: var(--dm-surface, #2d2d2d);
}

body.device-mobile.dark-mode .page-header {
    background-color: var(--dm-surface, #2d2d2d) !important;
    color: var(--dm-text, #e0e0e0);
}

body.device-mobile.dark-mode .page-title {
    color: var(--dm-text, #e0e0e0);
}

body.device-mobile.dark-mode .page-subtitle {
    color: var(--dm-text-muted, #b0b0b0);
}

body.device-mobile.dark-mode .topbar {
    background-color: var(--dm-surface, #2d2d2d) !important;
    border-bottom-color: var(--dm-elevated, #404040);
}

/* F09-0015-1.1: Menu do usuário branco SEMPRE no dark mode (desktop) */
body.dark-mode .user-menu-button {
    background-color: #ffffff !important;
    color: #000000 !important;
}

body.dark-mode .user-menu-button:hover {
    background-color: #f0f0f0 !important;
}

/* F09-0015-FIX: Remover regra que quebrava layout desktop */
/* body.device-desktop .sidebar - REMOVIDO (causava sobreposição) */

/* Safety nets no mobile: remove padding lateral e impede overflow horizontal */
body.device-mobile .main-content {
    padding-left: 0 !important;
    padding-right: 0 !important;
    overflow-x: hidden !important;
}

body.device-mobile .page-container {
    overflow-x: hidden !important;
}

/* ============================================================================
   MOBILE PORTRAIT — compactação geral pra dar mais espaço a tabelas/conteúdo
   (≤1199px, sempre — não depende de orientação)
   ============================================================================ */
@media (max-width: 1199px) {
    /* Page-header compactado */
    body.device-mobile .page-header-sticky .page-header,
    body.device-tablet .page-header-sticky .page-header {
        padding: 8px 14px;
    }
    body.device-mobile .page-title,
    body.device-tablet .page-title {
        font-size: 1rem;
        margin-bottom: 2px;
        line-height: 1.2;
    }
    body.device-mobile .page-subtitle,
    body.device-tablet .page-subtitle {
        font-size: 0.72rem;
        margin-bottom: 0;
        line-height: 1.2;
        color: #6c757d;
    }

    /* Botões do page-header menores (mas mantém min-height 44px do P0) */
    body.device-mobile .page-header > .d-flex.justify-content-between > .btn,
    body.device-mobile .page-header > .d-flex.justify-content-between > a.btn {
        padding: 8px 16px;
        font-size: 13px;
        flex: 0 0 auto;
    }

    /* Filter-bar mais compacto */
    body.device-mobile .filter-bar,
    body.device-tablet .filter-bar {
        padding: 8px 12px !important;
    }
    body.device-mobile .filter-bar .form-label,
    body.device-mobile .filter-bar .form-label-sm,
    body.device-tablet .filter-bar .form-label,
    body.device-tablet .filter-bar .form-label-sm {
        display: none; /* esconde labels — o select tem opção descritiva */
    }

    /* Filtros no celular: FAIXA HORIZONTAL rolável (uma linha só), não empilhados.
       As colunas usam col-3/col-4 (sem breakpoint) e truncam ("All Sta…") se espremidas;
       empilhar 1-por-linha comeria a área da tabela. Aqui cada filtro tem largura confortável
       e legível (~160px) e a linha rola na horizontal para alcançar os demais — a tabela
       mantém a altura útil, e escala para qualquer quantidade de filtros. */
    body.device-mobile .filter-bar .row {
        flex-wrap: nowrap;
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
    }
    body.device-mobile .filter-bar .row > [class^="col-"],
    body.device-mobile .filter-bar .row > [class*=" col-"] {
        flex: 0 0 auto;
        width: 160px;
        max-width: 160px;
    }

    /* Filtro em `.col` PURO (sem col-N) é outro caso: `.col` do Bootstrap é
       `flex: 1 0 0%`, ou seja, sem largura própria — na faixa de linha única
       acima os 5 filtros do Year over Year se espremiam a ~74px e o texto saía
       cortado ("Both (Al", "Quantit"). Aqui a linha QUEBRA e cada filtro ocupa
       um terço: 5 filtros viram 2 linhas (3 + 2) de larguras iguais, porque o
       flex-grow reparte a sobra da última linha entre os que ficaram nela.
       O cabeçalho mais alto é medido pelo JS (--page-header-height), então a
       tabela encolhe e ganha rolagem em vez de vazar para fora da tela. */
    body.device-mobile .filter-bar .row:has(> .col) {
        flex-wrap: wrap;
        overflow-x: visible;
    }
    body.device-mobile .filter-bar .row > .col {
        flex: 1 0 33.333%;
        min-width: 0;
    }
    /* Exatamente 4 filtros ficariam 3 + 1; com metade cada um o par fecha 2 + 2. */
    body.device-mobile .filter-bar .row > .col:first-child:nth-last-child(4),
    body.device-mobile .filter-bar .row > .col:first-child:nth-last-child(4) ~ .col {
        flex-basis: 50%;
    }

    /* Content-area com menos padding */
    body.device-mobile .content-area,
    body.device-tablet .content-area {
        --content-pad-y: 10px;  /* ver "ABAS FIXAS" */
        padding: 10px 8px !important;
    }

    /* Sticky 1ª coluna em tabelas com muitas colunas — mantém o rótulo visível.
       É o padrão de quem NÃO declara congelamento próprio; a lista de :not() são
       justamente as que declaram (.so-sticky-cols, .sales-analysis-table,
       .forecast-table, .forecast-review-table). Nelas esta regra fazia dois
       estragos: o `th:first-child` casa com a 1ª célula de CADA linha do
       cabeçalho (a 2ª linha ia grudar à esquerda em cima da 1ª coluna) e o
       z-index 11/5 daqui desmontava a ordem de pintura da própria tabela — o
       corpo (z 1000) passava a cobrir o cabeçalho (z 11).
       Fundos/hover/dark mode saem das variáveis --tbl-frozen-* (ver "CÉLULAS CONGELADAS"). */
    body.device-mobile .table-responsive .table:not(.so-sticky-cols):not(.sales-analysis-table):not(.forecast-table):not(.forecast-review-table) tbody td:first-child,
    body.device-mobile .table-responsive .table:not(.so-sticky-cols):not(.sales-analysis-table):not(.forecast-table):not(.forecast-review-table) tfoot td:first-child,
    body.device-tablet .table-responsive .table:not(.so-sticky-cols):not(.sales-analysis-table):not(.forecast-table):not(.forecast-review-table) tbody td:first-child,
    body.device-tablet .table-responsive .table:not(.so-sticky-cols):not(.sales-analysis-table):not(.forecast-table):not(.forecast-review-table) tfoot td:first-child {
        position: sticky;
        left: 0;
        background-color: var(--tbl-frozen-bg);
        z-index: 5;
        box-shadow: 2px 0 2px -1px rgba(0,0,0,0.08);
    }
    /* `tr:first-child` importa: `thead th:first-child` sozinho casa com a 1ª
       célula de CADA linha do cabeçalho, então num cabeçalho de duas linhas a
       2ª (ex.: "Flight" do Upcoming Trips) era grudada à esquerda em cima da 1ª
       coluna. Com cabeçalho de uma linha só o resultado é o mesmo de antes. */
    body.device-mobile .table-responsive .table:not(.so-sticky-cols):not(.sales-analysis-table):not(.forecast-table):not(.forecast-review-table) thead tr:first-child th:first-child,
    body.device-tablet .table-responsive .table:not(.so-sticky-cols):not(.sales-analysis-table):not(.forecast-table):not(.forecast-review-table) thead tr:first-child th:first-child {
        position: sticky;
        left: 0;
        background-color: var(--tbl-frozen-head-bg);
        z-index: 11; /* acima do thead sticky (top) e tbody sticky (left) */
        box-shadow: 2px 0 2px -1px rgba(0,0,0,0.08);
    }
    body.device-mobile .table-responsive .table:not(.so-sticky-cols):not(.sales-analysis-table):not(.forecast-table):not(.forecast-review-table) tbody tr:hover td:first-child,
    body.device-tablet .table-responsive .table:not(.so-sticky-cols):not(.sales-analysis-table):not(.forecast-table):not(.forecast-review-table) tbody tr:hover td:first-child {
        background-color: var(--tbl-frozen-hover-bg);
    }

    /* ── .so-sticky-cols no CELULAR (device-mobile ≤767): congela SÓ a 1ª coluna, estreita;
       descongela a 2ª. 240+120=360px fixos enchem a tela do celular e rolar não revela nada.
       A 1ª coluna (identificador) fica ~130px com reticências e vira a única congelada; a 2ª
       volta ao fluxo, liberando o resto para rolar. No tablet o congelamento de 2 colunas
       fica como está (há espaço de sobra). */
    body.device-mobile .so-sticky-cols th:nth-child(1),
    body.device-mobile .so-sticky-cols td:nth-child(1) {
        min-width: 130px;
        max-width: 130px;
        border-right-width: 2px; /* separador do congelamento passa p/ a 1ª coluna */
    }
    body.device-mobile .so-sticky-cols tbody td:nth-child(2),
    body.device-mobile .so-sticky-cols tfoot td:nth-child(2) {
        position: static;
        left: auto;
        min-width: 0;
        max-width: none;
        border-right-width: 1px;
    }
    body.device-mobile .so-sticky-cols thead th:nth-child(2) {
        left: auto;        /* mantém o sticky-top (top:0); tira só o sticky-left */
        min-width: 0;
        max-width: none;
        border-right-width: 1px;
        z-index: auto;
    }

    /* ── .sales-analysis-table no CELULAR: AS DUAS colunas continuam congeladas
       (diferente do .so-sticky-cols acima, que solta a 2ª) — identificar a linha
       aqui exige cliente/tipo E produto juntos. Para caberem nos 390px da tela
       elas encolhem com reticências: 110 + 90 = 200px, sobrando o resto para
       rolar os meses. O `left` da 2ª continua vindo do --sa-fixed2-left medido
       no base.html, que acompanha sozinho a largura nova da 1ª. */
    body.device-mobile .sales-analysis-table th.sa-col-fixed:nth-child(1),
    body.device-mobile .sales-analysis-table td.sa-col-fixed:nth-child(1) {
        min-width: 110px;
        max-width: 110px;
        overflow: hidden;
        text-overflow: ellipsis;
    }
    body.device-mobile .sales-analysis-table th.sa-col-fixed:nth-child(2),
    body.device-mobile .sales-analysis-table td.sa-col-fixed:nth-child(2) {
        min-width: 90px;
        max-width: 90px;
        overflow: hidden;
        text-overflow: ellipsis;
    }

    /* ── Forecast vs Review no CELULAR: o bloco congelado são 4 colunas
       (tipo/produto/preço/badge) — sem o badge não dá para saber se a linha é
       FCST ou REV, então nenhuma delas pode sair. No desktop somam 332px, que
       tomariam 85% dos 390px da tela; aqui encolhem para 244px e sobram ~145px
       para rolar os meses. Os offsets seguem sozinhos (saem das mesmas vars). */
    body.device-mobile .forecast-review-table.table-compact {
        --fc-w-tipo: 54px;
        --fc-w-produto: 88px;
        --fc-w-preco: 60px;
        --fc-w-badge: 42px;
        --fc-w-month: 64px;
        --fc-w-total: 76px;
    }
    body.device-mobile .forecast-review-table.table-compact.revenue-view {
        --fc-w-preco: 74px;
        --fc-w-month: 88px;
        --fc-w-total: 96px;
    }
}

/* ============================================================================
   MOBILE LANDSCAPE — altura ≤500px (celular deitado, tablet split-view)
   Compacta tudo agressivamente pra dar espaço útil pra tabela/conteúdo
   ============================================================================ */
@media (max-width: 1199px) and (max-height: 500px) {
    /* Page-header e filter ainda mais compactos */
    .page-header-sticky .page-header {
        padding: 2px 10px;
    }
    .page-header-sticky .filter-bar {
        padding: 2px 10px !important;
    }
    .page-header,
    .page-header-sticky .page-header {
        margin-bottom: 0 !important;
    }
    .page-title,
    .page-header-sticky .page-title {
        font-size: 0.9rem !important;
        margin-bottom: 0 !important;
        line-height: 1.2;
    }
    .page-subtitle,
    .page-header-sticky .page-subtitle {
        display: none; /* esconde subtitle em landscape — economiza espaço */
    }

    /* Title + botão "+ Criar" voltam a ficar lado-a-lado em landscape */
    body.device-mobile .page-header > .d-flex.justify-content-between,
    body.device-tablet .page-header > .d-flex.justify-content-between {
        flex-direction: row !important;
        align-items: center !important;
        gap: 8px;
    }
    /* Botão menor em landscape (mínimo aceitável p/ toque ainda) */
    body.device-mobile .page-header > .d-flex.justify-content-between > .btn,
    body.device-mobile .page-header > .d-flex.justify-content-between > a.btn,
    body.device-tablet .page-header > .d-flex.justify-content-between > .btn,
    body.device-tablet .page-header > .d-flex.justify-content-between > a.btn {
        padding: 4px 10px;
        font-size: 12px;
        min-height: 32px;
        flex: 0 0 auto;
    }

    /* Inputs/selects menores em landscape (mantém font 16px contra auto-zoom iOS) */
    body.device-mobile .form-control,
    body.device-mobile .form-select,
    body.device-tablet .form-control,
    body.device-tablet .form-select {
        padding: 4px 8px;
        min-height: 36px;
    }
    body.device-mobile .form-control-sm,
    body.device-mobile .form-select-sm,
    body.device-tablet .form-control-sm,
    body.device-tablet .form-select-sm {
        padding: 4px 8px;
        min-height: 32px;
    }

    /* Reduz padding do content-area */
    body.device-mobile .content-area,
    body.device-tablet .content-area {
        --content-pad-y: 4px;  /* ver "ABAS FIXAS" */
        padding: 4px 6px !important;
    }
    /* Cards mais compactos */
    body.device-mobile .card .card-body,
    body.device-tablet .card .card-body {
        padding: 6px !important;
    }
    /* Form em row.g-3 ainda mais compacto */
    body.device-mobile .row.g-3,
    body.device-tablet .row.g-3 {
        --bs-gutter-y: 0.2rem;
        --bs-gutter-x: 0.25rem;
    }
    body.device-mobile .row.g-2,
    body.device-tablet .row.g-2 {
        --bs-gutter-y: 0.2rem;
        --bs-gutter-x: 0.2rem;
    }
    /* Topbar e variável --topbar-height ajustados juntos pra consistência */
    body.device-mobile .topbar,
    body.device-tablet .topbar {
        height: 40px;
        padding-top: 0;
    }
    body.device-mobile .topbar-logo,
    body.device-tablet .topbar-logo {
        height: 22px;
    }
    body.device-mobile,
    body.device-tablet {
        --topbar-height: 40px;
    }

    /* Thead da tabela com padding menor */
    body.device-mobile .table thead th,
    body.device-tablet .table thead th {
        padding: 4px 6px !important;
        font-size: 12px;
    }
    body.device-mobile .table tbody td,
    body.device-tablet .table tbody td {
        padding: 4px 6px !important;
    }
}

/* ============================================================================
   DASHBOARD KANBAN CARDS — estilos base, extraídos dos <style> inline nos templates
   ============================================================================ */

.dashboard-card {
    background: white;
    border-radius: 0.5rem;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
    overflow: hidden;
    display: flex;
    flex-direction: column;
}

.kanban-card-header {
    padding: 9px 12px;
    background: #808080;
    color: white;
    border-bottom: 1px solid #dee2e6;
    display: flex;
    justify-content: space-between;
    align-items: center;
    font-weight: 600;
    font-size: 0.9rem;
    line-height: 1.3;
}

.kanban-card-body {
    padding: 15px;
}

/* ── Schedule (dashboards.schedule) — era <style> inline no template ──────────
   Layout responsivo vem do grid do Bootstrap; cores/dark mode de .dashboard-card. */
#schedule-kanban .kanban-card-header,
#trips-status-kanban .kanban-card-header,
#upcoming-trips-kanban .kanban-card-header { padding: 15px; border-radius: 8px 8px 0 0; }
#schedule-kanban .kanban-card-body { padding: 10px; }
/* Quem rola a AGENDA é o `.table-scroll` interno, não o corpo do card: o
   congelamento (cabeçalho + coluna PIC) ancora no scrollport mais próximo, e
   um segundo scroller por fora deixaria o cabeçalho subir junto com a tabela. */
#schedule-kanban .table-scroll { --table-scroll-h: min(560px, 65dvh); }
#trips-status-kanban .kanban-card-body,
#upcoming-trips-kanban .kanban-card-body { max-height: 420px; overflow: auto; }

/* Mapa de rotas AMPLIADO: o dedo arrasta o mapa nos dois eixos. Sem isto o
   navegador assume o gesto vertical para rolar a página e só o arrasto
   horizontal funcionava no celular.
   A classe é aplicada só enquanto há zoom — no enquadramento inteiro não há pan,
   e o mapa ocupa boa parte da tela no celular: se ele engolisse o gesto vertical
   o tempo todo, o usuário não conseguiria rolar a página passando o dedo nele. */
#flight-map.map-panning {
    touch-action: none;
}

/* Zoom do mapa de rotas (trips.plan). Ficam no cabeçalho e não sobre o mapa,
   que é estreito (col-lg-3) e tem o rótulo do trecho animado no topo. */
.map-zoom-controls {
    display: flex;
    gap: 4px;
    flex-shrink: 0;
}

.map-zoom-controls .btn {
    /* .btn tem border de 1px, então o padding aqui já vem descontado dela */
    padding: 0 7px;
    min-width: 26px;
    font-size: 14px;
    line-height: 22px;
    font-weight: 600;
}

/* ── Trip Planning (trips.plan): mapa de rotas + Trip List ─────────────────
   Layout responsivo vem do grid do Bootstrap; cores/dark mode de .dashboard-card. */
#flight-routes-kanban .kanban-card-header,
#trip-list-kanban .kanban-card-header { padding: 15px; border-radius: 8px 8px 0 0; }

/* Mapa Plotly: o card estica até a altura do card Trip List (mesma linha do grid) e o
   gráfico (autosize) preenche o corpo. min-height evita o colapso para 0 no mobile. */
#flight-routes-kanban { height: 100%; }
#flight-routes-kanban .kanban-card-body { padding: 5px; flex: 1; display: flex; min-height: 320px; }
#flight-map { width: 100%; flex: 1; min-height: 320px; position: relative; }

/* Camada própria de rótulos do mapa (só com a caixa ligada). Fica FORA do SVG do
   Plotly porque ele reconstrói o próprio conteúdo a cada quadro da animação e
   apaga qualquer nó estranho — ver o comentário em trips/trip_list.html. */
.map-label-layer { position: absolute; inset: 0; pointer-events: none; overflow: hidden; }
.map-label {
    position: absolute; transform: translate(-50%, -50%);
    /* Sem folga VERTICAL: o rótulo já nasce deslocado do marcador (o Plotly o põe
       acima ou abaixo do ponto), e alguns pixels a mais faziam a placa tapar o
       próprio marcador que ela deveria acompanhar. */
    white-space: nowrap; padding: 0 4px; border-radius: 3px; line-height: 1.25;
}

/* ── Tabela do Trip Planning: espaço para a coluna Route ──────────────────
   Route é a única coluna que não abrevia (nome de cidade inteiro, duas cidades
   por linha) e foi a que sofreu quando a coluna PIC entrou. As vizinhas cedem:
   PIC e Customer com fonte menor, um cliente por linha (a lista com vírgulas
   esticava a célula) e botões de ação compactos. Vale no desktop e no celular —
   é a mesma tabela, dentro do mesmo scroll horizontal. */
.trip-table .tt-route { white-space: normal; word-wrap: break-word; min-width: 165px; }
.trip-table .tt-customer { font-size: 0.76rem; line-height: 1.35; max-width: 130px; }
.trip-table .tt-customer div { white-space: normal; word-break: break-word; }
.trip-table .tt-pic { font-size: 0.76rem; max-width: 92px; white-space: normal; line-height: 1.3; }
.trip-table .tt-actions .btn {
    /* .btn tem 1px de borda: o padding já vem descontado dela */
    padding: 1px 6px;
    font-size: 0.72rem;
    line-height: 1.35;
}
.trip-table td, .trip-table th { padding-left: 0.4rem; padding-right: 0.4rem; }

@media (max-width: 767px) {
    /* No celular a coluna PIC cabia em ~30px e quebrava o nome em duas linhas.
       Trunca numa linha só; o nome inteiro fica no title (a tabela rola na
       horizontal, então esconder a coluna tiraria a atribuição sem necessidade). */
    .trip-table .tt-pic {
        max-width: 62px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    }
    .trip-table .tt-customer { max-width: 96px; }
}

/* Lista de viagens: margens mínimas. Quem rola é o `.table-responsive` interno,
   e não o corpo do card — é nele que o cabeçalho sticky ancora, e um segundo
   scroller por fora faria o cabeçalho subir junto com a tabela (mesma razão
   documentada no #schedule-kanban). O max-height sai junto: com o corpo do card
   cortando em 560px, as últimas linhas da tabela ficavam por baixo da borda. */
#trip-list-kanban .kanban-card-body { padding: 3px; }
#trip-list-kanban .card { margin: 0; padding: 0; }
/* Sem padding no TOPO: o cabeçalho sticky ancora no topo do scrollport, e a
   faixa de padding acima dele é uma fresta por onde as linhas aparecem ao rolar. */
#trip-list-kanban .table-responsive { margin: 0; padding: 0 2px 2px; }
#trip-list-kanban .table { margin-bottom: 0; }

/* Card views (_card templates) — header/body para layout sem Gridstack */
.dashboard-card-header {
    padding: 15px;
    background: #808080;
    color: white;
    font-weight: 600;
    border-bottom: 1px solid #dee2e6;
    display: flex;
    justify-content: space-between;
    align-items: center;
}

.dashboard-card-body {
    padding: 15px;
    flex: 1;
    display: flex;
    flex-direction: column;
}

/* Alturas fixas para card views (equivalente às células do Gridstack) */
.card-h-3 .dashboard-card { height: 230px; }
.card-h-4 .dashboard-card { height: 310px; }
.card-h-6 .dashboard-card { height: 470px; }
.card-h-7 .dashboard-card { height: 550px; }
.card-h-8 .dashboard-card { height: 630px; }
.card-h-9 .dashboard-card { height: 710px; }

.card-h-3 .dashboard-card-body,
.card-h-4 .dashboard-card-body,
.card-h-6 .dashboard-card-body,
.card-h-7 .dashboard-card-body,
.card-h-8 .dashboard-card-body,
.card-h-9 .dashboard-card-body { overflow: auto; }

@media (max-width: 991px) {
    .card-h-3 .dashboard-card,
    .card-h-4 .dashboard-card,
    .card-h-6 .dashboard-card,
    .card-h-7 .dashboard-card,
    .card-h-8 .dashboard-card,
    .card-h-9 .dashboard-card { height: auto !important; }
}

/* Cards com gráfico Plotly precisam de altura explícita mesmo abaixo de 991px:
   o JS mede o contêiner para desenhar, e "auto" (regra acima) fica em 0 até o
   gráfico existir. Sobrescreve a altura auto só para quem tem .plotly-chart-container. */
@media (max-width: 991px) {
    .card-h-6 .dashboard-card.plotly-chart-container { height: 360px !important; }
    .card-h-7 .dashboard-card.plotly-chart-container { height: 440px !important; }
}

/* Gridstack: widget wrapper base */
.grid-stack-item-content {
    background: white;
    border-radius: 8px;
    box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}

/* Ícone de arraste nos kanbans Gridstack */
.grip-icon {
    color: white;
    cursor: move;
}

.kpi-value {
    font-size: 2rem;
    font-weight: bold;
    margin: 10px 0;
}

.kpi-label {
    font-size: 0.9rem;
    color: #6c757d;
    text-transform: uppercase;
}

.kpi-center {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    text-align: center;
}

.kpi-two-columns {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
}

.kpi-numbers-row {
    display: flex;
    gap: 40px;
    margin-bottom: 10px;
}

.kpi-number-item {
    display: flex;
    flex-direction: column;
    align-items: center;
}

.kpi-number-value {
    font-size: 2.5rem;
    font-weight: bold;
    line-height: 1;
}

.kpi-number-label {
    font-size: 0.75rem;
    color: #6c757d;
    text-transform: uppercase;
    margin-top: 5px;
}

/* ============================================================================
   GRIDSTACK MOBILE — quando ≤1199px, força stacking vertical e desabilita drag
   (Chrome estava interpretando scroll como move-card)
   ============================================================================ */
@media (max-width: 1199px) {
    .grid-stack {
        height: auto !important;
    }
    .grid-stack > .grid-stack-item {
        position: relative !important;
        width: 100% !important;
        left: 0 !important;
        top: auto !important;
        right: auto !important;
        bottom: auto !important;
        transform: none !important;
        height: auto !important;
        min-height: 250px;
        margin-bottom: 12px;
    }
    .grid-stack-item-content {
        position: relative !important;
        inset: auto !important;
        height: 100% !important;
    }
    /* Esconde ícone de "agarrar" que insinua arrasto */
    .grip-icon {
        display: none !important;
    }
    /* Cursor de move no header não faz sentido em touch */
    .kanban-card-header {
        cursor: default !important;
    }
}

/* ============================================================================
   P2 MOBILE UX — ajustes pra usabilidade real (modal, page-header, filter, etc.)
   ============================================================================ */

/* Modal customizado responsivo no mobile (.modal caseiro, sem .modal-dialog) */
@media (max-width: 768px) {
    .modal {
        padding: 8px;
    }

    .modal-content {
        width: 100% !important;
        max-width: 100% !important;
        max-height: 90dvh !important;
        margin: 0 auto;
        padding: 0;
    }

    .modal-header {
        padding: 12px 16px;
        flex-wrap: wrap;
    }

    .modal-header h2 {
        font-size: 16px;
        margin-right: 8px;
        flex: 1;
        min-width: 0;
    }

    .modal-body {
        padding: 12px 16px;
    }

    /* botão de fechar com touch target adequado */
    .close-btn {
        min-width: 44px;
        min-height: 44px;
        display: flex;
        align-items: center;
        justify-content: center;
    }
}

/* Page header: stack título e botão em telas pequenas */
body.device-mobile .page-header > .d-flex.justify-content-between {
    flex-direction: column;
    align-items: stretch !important;
    gap: 10px;
}

body.device-mobile .page-header > .d-flex.justify-content-between > .btn,
body.device-mobile .page-header > .d-flex.justify-content-between > a.btn {
    align-self: flex-start;
}

/* Filter-bar: força input de busca a ocupar toda a largura no mobile
   (sobrescreve inline style max-width: 400px dos templates) */
body.device-mobile .filter-bar input[type="text"],
body.device-mobile .filter-bar .form-control {
    max-width: 100% !important;
}

/* Select2 tunado pro mobile: touch targets 44px + search 16px (anti auto-zoom iOS) */
body.device-mobile .select2-container--classic .select2-selection {
    min-height: 44px;
}

body.device-mobile .select2-container--classic .select2-selection--multiple .select2-selection__choice {
    min-height: 32px;
    padding: 4px 10px;
    line-height: 1.6;
    font-size: 14px;
}

body.device-mobile .select2-container--classic .select2-selection--multiple .select2-selection__choice__remove {
    min-width: 24px;
    min-height: 32px;
    padding: 0 6px;
    display: inline-flex;
    align-items: center;
    justify-content: center;
}

body.device-mobile .select2-dropdown .select2-search__field {
    font-size: 16px !important; /* impede auto-zoom no iOS ao focar no search */
    min-height: 40px;
    padding: 8px 12px;
}

body.device-mobile .select2-results__option {
    min-height: 44px;
    padding: 12px 14px;
    display: flex;
    align-items: center;
    font-size: 14px;
}

/* ============================================================================
   F09-0024: PLOTLY + GRIDSTACK - SOLUÇÃO DEFINITIVA SCROLL
   Aplicar APENAS em cards com classe .plotly-chart-container
   ============================================================================ */

/* Container do card: flex column para distribuir altura entre header e body */
.plotly-chart-container {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0; /* CRUCIAL: permite flex children encolherem */
}

/* Header: ocupa apenas o necessário */
.plotly-chart-container .kanban-card-header {
    flex: 0 0 auto;
    padding: 9px 12px;
    line-height: 1.2;
}

/* Body: ocupa todo o espaço restante, sem permitir overflow */
.plotly-chart-container .kanban-card-body {
    flex: 1 1 auto;
    min-height: 0; /* CRUCIAL: sem isso, 100% não "cabe" */
    overflow: hidden; /* Evita scroll; conteúdo deve se ajustar */
    display: flex; /* Permite o chart esticar */
}

/* Chart div: herda 100% da altura/largura do body */
.plotly-chart-container .kanban-card-body > div[id^="chart-"] {
    flex: 1 1 auto;
    min-height: 0;
    width: 100%;
    height: 100%;
}

/* Força contêineres internos do Plotly a usarem 100% */
.js-plotly-plot,
.js-plotly-plot .plot-container,
.js-plotly-plot .svg-container {
    width: 100% !important;
    height: 100% !important;
}

/* ============================================================================
   F09-0026: PLOTLY MÚLTIPLOS GRÁFICOS - Kanbans com 2+ gráficos lado a lado
   ============================================================================ */

/* Container do card: flex column + padding para gráficos ficarem visíveis */
.plotly-multiple-chart-container {
    display: flex;
    flex-direction: column;
    height: 100%;
    min-height: 0;  /* CRUCIAL: evita estouro */
    overflow: hidden;
}

/* Header: ocupa apenas o necessário */
.plotly-multiple-chart-container .kanban-card-header {
    flex: 0 0 auto;
    padding: 9px 12px;
    line-height: 1.2;
}

/* Body: ocupa todo o espaço restante + padding para gráficos */
.plotly-multiple-chart-container .kanban-card-body {
    flex: 1 1 auto;
    min-height: 0;  /* CRUCIAL: evita estouro */
    overflow: hidden;
    padding: 13.5px;
    display: flex;  /* Garante controle total da altura interna */
    flex-direction: column;
}

/* A row precisa herdar 100% de altura do body */
.plotly-multiple-chart-container .kanban-card-body > .row {
    flex: 1 1 auto;
    min-height: 0;  /* CRUCIAL: evita estouro */
    height: 100%;
    margin: 0;
    display: flex;  /* Permite align-items funcionar */
    flex-wrap: wrap;  /* Mantém comportamento Bootstrap */
}

/* Colunas ocupam 100% da altura disponível */
.plotly-multiple-chart-container .kanban-card-body .row > [class*="col-"] {
    display: flex;
    flex-direction: column;
    flex: 1 1 0;
    min-height: 0;  /* CRUCIAL: evita estouro */
}

/* Quando row tem align-items-center, centralizar conteúdo das colunas também */
.plotly-multiple-chart-container .kanban-card-body .row.align-items-center > [class*="col-"] {
    justify-content: center;  /* Centraliza conteúdo verticalmente dentro da coluna */
}

/* Título do mini-bloco não rouba altura do gráfico */
.plotly-multiple-chart-container .kanban-card-body h6 {
    flex: 0 0 auto;
    margin-bottom: 7.2px;
}

/* A área do gráfico ocupa o restante, SEM encolher a 0 */
.plotly-multiple-chart-container .kanban-card-body .plotly-chart {
    flex: 1 1 auto;
    min-height: 0;  /* CRUCIAL: evita estouro */
    width: 100%;
    height: 100%;
}

/* Contêineres internos do Plotly respeitam 100% */
.plotly-multiple-chart-container .plotly-chart .js-plotly-plot,
.plotly-multiple-chart-container .plotly-chart .plot-container,
.plotly-multiple-chart-container .plotly-chart .svg-container {
    width: 100% !important;
    height: 100% !important;
}

/* ============================================================================
   F09-0026: CENTRALIZAÇÃO VERTICAL - Kanban "Total per Mode" (Trip Dashboard)
   ============================================================================ */

/* Body centraliza o gráfico vertical e horizontalmente */
#mode-kanban .kanban-card-body {
    display: flex;
    align-items: center;      /* Centraliza VERTICAL */
    justify-content: center;  /* Centraliza HORIZONTAL */
    padding: 13.5px;
    min-height: 0;
    overflow: hidden;
}

/* O container do gráfico ocupa a área útil, com limites saudáveis */
#mode-kanban .plotly-chart {
    width: 100%;
    height: 100%;
    max-width: 100%;
    max-height: 100%;
    min-height: 216px;  /* Evita "ponto" minúsculo */
}

/* Plotly interno deve obedecer ao 100% acima */
#mode-kanban .plotly-chart .js-plotly-plot,
#mode-kanban .plotly-chart .plot-container,
#mode-kanban .plotly-chart .svg-container {
    width: 100% !important;
    height: 100% !important;
}

/* ==============================================
   ATW Line - Correção Kanban Total Distance
   Review 0222 - 02/11/2025
   Classes específicas que NÃO afetam outros .kpi-label
   ============================================== */

/* Linha 2: Número + ATW (flexbox para alinhamento) */
.kpi-line-atw {
    display: flex;
    justify-content: center;
    align-items: baseline;
    gap: 0.3rem;
    line-height: 1;
    text-transform: none !important;  /* Neutraliza uppercase apenas aqui */
}

/* Número das voltas ao mundo (negrito, maior) */
.kpi-line-atw .atw-number {
    font-weight: 700;
    font-size: 1.1rem;
}

/* Texto "ATW" (normal, menor) */
.kpi-line-atw .atw-suffix {
    font-weight: 400;
    font-size: 0.9rem;
}

/* Linha 3: Legenda "(Around the World)" (bem pequena, discreta) */
.kpi-help {
    font-size: 0.5rem;
    color: #999;
    text-transform: none !important;  /* Garante case correto */
    letter-spacing: normal;
}

/* Garantir que strong dentro de kpi-label sempre renderize */
.kpi-label strong { 
    display: inline !important;
    font-size: inherit;
    color: inherit;
    visibility: visible !important;
}

/* Corpo de card não rola por padrão — quem rola é o container interno da tabela
   (.table-responsive / .table-scroll), que é onde o cabeçalho sticky ancora.
   ⚠️ SEM `!important`: com ele, esta regra genérica vencia as específicas que
   PEDEM rolagem (#trip-list-kanban, #trips-status-kanban, #upcoming-trips-kanban,
   todas declaradas acima com `overflow: auto`) e o conteúdo além do max-height
   ficava inalcançável — no celular a lista de viagens simplesmente não rolava. */
.dashboard-card-body,
.kanban-card-body {
    overflow: hidden;
}

/* Garantir que grid-stack-item-content também não tenha scroll */
.grid-stack-item-content {
    overflow: hidden !important;
}

/* ============================================================
   Sales Analysis Table (sales_reports/sales_analysis.html)
   ============================================================ */
.sales-analysis-table {
    font-size: 0.78rem;
}
.sales-analysis-table th.sa-sub {
    font-size: 0.72rem;
    padding: 3px 5px;
    white-space: nowrap;
}
.sales-analysis-table th.sa-month-group {
    background-color: #eef2f8;
    font-size: 0.78rem;
    padding: 4px 2px;
}
.sales-analysis-table th.sa-total-group {
    background-color: #dde5f5;
}
.sales-analysis-table td {
    padding: 3px 5px;
    white-space: nowrap;
}
.sales-analysis-table td.data-cell,
.sales-analysis-table td.total-fcst,
.sales-analysis-table td.total-rev,
.sales-analysis-table td.total-real,
.sales-analysis-table td.total-delta {
    min-width: 58px;
}
/* Cabeçalho de DUAS linhas (mês + FCST/REV/Real/Δ) → a tabela usa
   `.table-grouped-head`: gruda o <thead> INTEIRO. Sem isso a regra global
   (.table-responsive thead th) gruda cada <th> sozinho em top:0, as duas linhas
   param no mesmo lugar e a de baixo cobre a dos meses.

   O z-index precisa subir aqui: o <thead> grudado cria contexto de empilhamento
   e o valor do utilitário (10) fica ABAIXO das colunas congeladas do corpo
   (1000), que passariam por cima do cabeçalho ao rolar na vertical. */
.sales-analysis-table thead {
    z-index: 2000;
}

/* Colunas congeladas — fundo/hover/dark mode vêm da fundação (--tbl-frozen-*,
   ver "CÉLULAS CONGELADAS"); aqui só offset, largura e z-index. */
.sales-analysis-table th.sa-col-fixed,
.sales-analysis-table td.sa-col-fixed {
    position: sticky;
    z-index: 1000;
    background-color: var(--tbl-frozen-bg);
}
.sales-analysis-table thead th.sa-col-fixed,
.sales-analysis-table tfoot td.sa-col-fixed {
    z-index: 2000;
    background-color: var(--tbl-frozen-head-bg);
}
.sales-analysis-table th.sa-col-fixed:nth-child(1),
.sales-analysis-table td.sa-col-fixed:nth-child(1) {
    left: 0;
    min-width: 80px;
}
/* 2ª (última) coluna congelada: sombra marca a divisa. `!important` porque a
   tabela é `.table-hover` e o hover do Bootstrap escreve box-shadow com
   especificidade maior. */
/* `left` = largura real da 1ª coluna, medida no base.html (--sa-fixed2-left): ela
   depende do conteúdo/idioma, e o 80px fixo que havia aqui deixava esta coluna
   parar em cima do fim da anterior ao rolar de lado. O 80px segue de fallback. */
.sales-analysis-table th.sa-col-fixed:nth-child(2),
.sales-analysis-table td.sa-col-fixed:nth-child(2) {
    left: var(--sa-fixed2-left, 80px);
    min-width: 100px;
    box-shadow: var(--tbl-frozen-shadow) !important;
}
.sa-fcst-hdr  { color: #0d6efd; }
.sa-rev-hdr   { color: #198754; }
.sa-real-hdr  { color: #6c757d; }
.sa-delta-hdr { color: #fd7e14; text-align: center; }

/* ============================================================================
   VALIDATION TOOLTIP — feedback de validação inline em formulários
   ============================================================================ */

.validation-tooltip {
    position: absolute;
    z-index: 1000;
    background: white;
    border: 1px solid #dee2e6;
    border-radius: 8px;
    padding: 12px 16px;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1), 0 2px 4px rgba(0,0,0,0.06);
    display: flex;
    align-items: center;
    gap: 10px;
    margin-top: 8px;
    max-width: 400px;
}
.validation-tooltip .icon {
    width: 28px;
    height: 28px;
    background: #ff9800;
    border-radius: 4px;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-weight: bold;
    font-size: 18px;
    flex-shrink: 0;
}
.validation-tooltip .message {
    color: #333;
    font-size: 14px;
    line-height: 1.4;
}
.validation-tooltip::before {
    content: '';
    position: absolute;
    top: -8px;
    left: 20px;
    width: 0;
    height: 0;
    border-left: 8px solid transparent;
    border-right: 8px solid transparent;
    border-bottom: 8px solid white;
}

/* ============================================================================
   AUTOCOMPLETE DROPDOWN — dropdown de busca reutilizável
   ============================================================================ */

.autocomplete-dropdown {
    position: absolute;
    background: white;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    max-height: 200px;
    overflow-y: auto;
    z-index: 1000;
    box-shadow: 0 4px 6px rgba(0,0,0,0.1);
    width: calc(100% - 30px);
    margin-top: 2px;
}
.autocomplete-item {
    padding: 10px;
    cursor: pointer;
    border-bottom: 1px solid #f0f0f0;
}
.autocomplete-item:hover { background-color: #f8f9fa; }
.autocomplete-item:last-child { border-bottom: none; }
.autocomplete-item .autocomplete-uf { color: #6c757d; font-size: 0.875em; }

body.dark-mode .autocomplete-dropdown {
    background: #2b3035;
    border-color: #495057;
    color: #e9ecef;
}
body.dark-mode .autocomplete-item { border-bottom-color: #3a4046; }
body.dark-mode .autocomplete-item:hover { background-color: #3a4046; }
body.dark-mode .autocomplete-item .autocomplete-uf { color: #adb5bd; }

/* ============================================================================
   FORECAST FORM — inputs de tabela de revisão mensal
   ============================================================================ */

.quantity-input,
.price-input,
.total-input {
    text-align: right;
    padding: 4px 8px;
    min-width: 100px;
}

/* ============================================================================
   LANDING PAGE — tela de boas-vindas / informações do sistema
   ============================================================================ */

.landing-wrapper {
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    height: 100%;
    padding: 2rem 1rem;
    overflow-y: auto;
}
.landing-logo {
    max-width: 200px;
    max-height: 110px;
    object-fit: contain;
    margin-bottom: 2rem;
    filter: drop-shadow(0 2px 6px rgba(0,0,0,.12));
}
body.dark-mode .landing-logo {
    /* O JS (landing.html) já troca o src para o logo escuro; nada de inverter
       cores aqui — brightness(0) invert(1) transformava logos de fundo opaco
       numa caixa branca sólida. Mantém só a sombra. */
    filter: drop-shadow(0 2px 6px rgba(0,0,0,.4));
}
.landing-welcome {
    font-size: 1.7rem;
    font-weight: 700;
    color: #2c3e50;
    text-align: center;
    margin-bottom: 0.4rem;
}
body.dark-mode .landing-welcome { color: #e8eaf6; }
.landing-subtitle {
    font-size: 1rem;
    color: #6c757d;
    text-align: center;
    margin-bottom: 2rem;
}
.landing-card {
    background: #fff;
    border: 1px solid #dee2e6;
    border-radius: 12px;
    box-shadow: 0 4px 18px rgba(0,0,0,.08);
    padding: 2rem 2.5rem;
    min-width: 320px;
    max-width: 480px;
    width: 100%;
}
body.dark-mode .landing-card {
    background: #1e1e2e;
    border-color: #3a3a5c;
    box-shadow: 0 4px 18px rgba(0,0,0,.35);
}
.landing-card-title {
    font-size: 0.75rem;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: #adb5bd;
    margin-bottom: 1.2rem;
}
.landing-info-row {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    padding: 0.55rem 0;
    border-bottom: 1px solid #f0f0f0;
    gap: 1rem;
}
body.dark-mode .landing-info-row { border-bottom-color: #2e2e4a; }
.landing-info-row:last-child { border-bottom: none; }
.landing-info-label {
    font-size: 0.85rem;
    color: #6c757d;
    white-space: nowrap;
    flex-shrink: 0;
}
body.dark-mode .landing-info-label { color: #9fa8da; }
.landing-info-value {
    font-size: 0.9rem;
    font-weight: 600;
    color: #2c3e50;
    text-align: right;
    word-break: break-all;
}
body.dark-mode .landing-info-value { color: #e8eaf6; }
.landing-badge {
    display: inline-block;
    font-size: 0.75rem;
    font-weight: 700;
    padding: 2px 10px;
    border-radius: 20px;
    background: var(--primary-color);
    color: #fff;
    letter-spacing: 0.04em;
}

/* ===================================================================
   Gestão de Menus — alerta de pendências, engrenagem de submenu,
   chave pendente e tela de revisão de chaves (CSS global, não inline)
   =================================================================== */
.menu-toolbar .status-text.dirty {
    font-size: 13px;
    font-weight: 700;
    color: #663c00;
    background: #ffe066;
    border: 1px solid #f0c000;
    padding: 6px 14px;
    border-radius: 6px;
    animation: dirtyPulse 1.6s ease-in-out infinite;
}
@keyframes dirtyPulse {
    0%   { box-shadow: 0 0 0 0 rgba(240,192,0,0.55); }
    70%  { box-shadow: 0 0 0 8px rgba(240,192,0,0); }
    100% { box-shadow: 0 0 0 0 rgba(240,192,0,0); }
}
/* Submenu card: top row (label + gear) */
.menu-card .card-top-row { display: flex; align-items: center; gap: 4px; }
.menu-card .card-top-row .card-label-input { flex: 1; min-width: 0; }
.card-gear-wrapper { position: relative; flex-shrink: 0; }
.card-rename-btn {
    background: #f1f3f5; border: 1px solid #ced4da; border-radius: 4px;
    font-size: 12px; line-height: 1.3; padding: 1px 5px; cursor: pointer; color: #495057;
}
.card-rename-btn:hover { background: #e7f3ff; border-color: #0067b5; }
.card-gear-menu {
    display: none; position: absolute; top: calc(100% + 4px); right: 0;
    background: #fff; border: 1px solid #ced4da; border-radius: 6px;
    box-shadow: 0 4px 12px rgba(0,0,0,0.15); z-index: 1500; min-width: 160px; overflow: hidden;
}
.card-gear-menu.show { display: block; }
.card-gear-item {
    display: block; width: 100%; text-align: left; padding: 8px 12px;
    background: none; border: none; cursor: pointer; font-size: 13px; color: #495057; white-space: nowrap;
}
.card-gear-item:hover { background: #f1f3f5; }
.menu-card .card-key.pending { color: #b35a00; font-style: italic; }
.section-pending-badge {
    display: none; font-family: 'Courier New', monospace; font-size: 10px;
    background: #ffe066; color: #663c00; border-radius: 4px; padding: 1px 5px; margin-left: 4px;
    max-width: 130px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; flex-shrink: 0;
}
/* Tela de revisão de chaves */
.mmgmt-modal.wide { max-width: 560px; }
.mmgmt-keyedit-list { margin-top: 10px; max-height: 340px; overflow-y: auto; }
.mmgmt-keyedit-row {
    display: flex; flex-direction: column; gap: 3px; padding: 8px;
    border: 1px solid #e9ecef; border-radius: 6px; margin-bottom: 8px;
}
.mmgmt-keyedit-row .kx-label { font-size: 12px; font-weight: 600; color: #2c3e50; }
.mmgmt-keyedit-row .kx-input-row { display: flex; align-items: center; gap: 4px; }
.mmgmt-keyedit-row .kx-prefix { font-family: 'Courier New', monospace; font-size: 12px; color: #868e96; white-space: nowrap; }
.mmgmt-keyedit-row input {
    flex: 1; font-family: 'Courier New', monospace; font-size: 12px;
    padding: 5px 6px; border: 1px solid #ced4da; border-radius: 4px;
}

/* ── Gestão de Menus: modo escuro ────────────────────────────────────
   O <style> inline do template fixa o kanban em claro; estes overrides
   (body.dark-mode + classe = especificidade ≥ 0,2,0) fazem seguir o tema. */
/* Colunas e cartões (cabeçalho azul de coluna fica como está = marca) */
body.dark-mode .menu-column { background: var(--dm-surface, #2d2d2d); border-color: var(--dm-border, #505050); }
body.dark-mode .menu-card { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); color: var(--dm-text, #e0e0e0); }
body.dark-mode .menu-card .card-key { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .menu-card .card-label-input { color: var(--dm-text, #e0e0e0); }
body.dark-mode .menu-card .card-label-input:focus { background: #4a3f00; border-color: var(--warning-color, #ffc107); color: var(--dm-text, #e0e0e0); }
body.dark-mode .menu-card.no-url { background: #3a2e00; border-color: #6b5800; }
body.dark-mode .menu-card.no-url::after { color: #ffd966; }
body.dark-mode .empty-column-hint { color: var(--dm-text-muted, #b0b0b0); }

/* Seções (grupos) dentro da coluna */
body.dark-mode .menu-group { background: var(--dm-bg, #1a1a1a); border-color: var(--dm-border, #505050); }
body.dark-mode .menu-group-header { background: var(--dm-elevated, #404040); }
body.dark-mode .menu-group-grip { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .group-label-input { background: rgba(255,255,255,0.08); color: var(--dm-text, #e0e0e0); }
body.dark-mode .group-label-input:focus { background: var(--dm-elevated, #404040); border-color: var(--primary-color, #0067b5); }
body.dark-mode .group-sort-btn,
body.dark-mode .group-delete-btn { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); color: var(--dm-text, #e0e0e0); }
body.dark-mode .group-sort-btn:hover { background: rgba(0,103,181,0.25); border-color: var(--primary-color, #0067b5); }

/* Botões tracejados (+ Seção, adicionar filho) */
body.dark-mode .menu-column-add-child { border-color: var(--dm-border, #505050); color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .menu-column-add-child:hover { background: rgba(0,103,181,0.20); border-color: var(--primary-color, #0067b5); color: var(--primary-light, #3385c6); }

/* Cartão: engrenagem e menu (estilos do main.css) */
body.dark-mode .card-rename-btn { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); color: var(--dm-text, #e0e0e0); }
body.dark-mode .card-rename-btn:hover { background: rgba(0,103,181,0.25); border-color: var(--primary-color, #0067b5); }
body.dark-mode .card-gear-menu,
body.dark-mode .section-gear-menu { background: var(--dm-surface, #2d2d2d); border-color: var(--dm-border, #505050); }
body.dark-mode .card-gear-item,
body.dark-mode .section-gear-item { color: var(--dm-text, #e0e0e0); }
body.dark-mode .card-gear-item:hover,
body.dark-mode .section-gear-item:hover { background: var(--dm-elevated, #404040); }
body.dark-mode .section-gear-item.danger:hover { background: #3a0d0d; }

/* Modais (nova entrada, revisão de chaves) */
body.dark-mode .mmgmt-modal { background: var(--dm-surface, #2d2d2d); }
body.dark-mode .mmgmt-modal h3 { color: var(--dm-text, #e0e0e0); }
body.dark-mode .mmgmt-modal label { color: var(--dm-text, #e0e0e0); }
body.dark-mode .mmgmt-modal small { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .mmgmt-modal input,
body.dark-mode .mmgmt-modal select { background: var(--dm-elevated, #404040); color: var(--dm-text, #e0e0e0); border-color: var(--dm-border, #505050); }
body.dark-mode .mmgmt-modal-error { background: #3a0d0d; color: #f5a8b0; }
body.dark-mode .mmgmt-keyedit-row { border-color: var(--dm-border, #505050); }
body.dark-mode .mmgmt-keyedit-row .kx-label { color: var(--dm-text, #e0e0e0); }
body.dark-mode .mmgmt-keyedit-row .kx-prefix { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .mmgmt-keyedit-row input { background: var(--dm-elevated, #404040); color: var(--dm-text, #e0e0e0); border-color: var(--dm-border, #505050); }
body.dark-mode .rename-section-key-display { background: var(--dm-bg, #1a1a1a); color: var(--dm-text-muted, #b0b0b0); }

/* Seletor de emoji e botão de pré-visualização de ícone */
body.dark-mode .emoji-picker { background: var(--dm-surface, #2d2d2d); border-color: var(--dm-border, #505050); }
body.dark-mode .emoji-picker-search { background: var(--dm-elevated, #404040); color: var(--dm-text, #e0e0e0); border-color: var(--dm-border, #505050); }
body.dark-mode .emoji-picker-grid button:hover { background: rgba(0,103,181,0.25); border-color: var(--primary-color, #0067b5); }
body.dark-mode .emoji-picker-none { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .icon-preview-btn { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); }
body.dark-mode .icon-preview-btn:hover { background: rgba(0,103,181,0.25); border-color: var(--primary-color, #0067b5); }

/* Barra de status e scrollbar */
body.dark-mode .menu-toolbar .status-text { color: var(--dm-text, #e0e0e0); }
body.dark-mode .menu-kanban-section-row::-webkit-scrollbar-track { background: var(--dm-elevated, #404040); }

/* ── Caixas de destaque (callouts) — claro + escuro ──────────────────
   Substituem backgrounds inline em formulários OpEx/CapEx. O layout
   (padding, margem, raio, alinhamento) permanece inline no template;
   estas classes cuidam só de cor (fundo/borda/texto), que precisa seguir o tema. */
.soft-box-muted          { background: #f8f9fa; border: 1px solid #dee2e6; }
.soft-box-info           { background: #eef6ff; border: 1px solid #c2dcf7; }
.soft-box-warning        { background: #fff8e1; border: 1px solid #ffe082; }
.soft-box-warning-strong { background: #fff3e0; border: 1px solid #ffcc80; }
.soft-fill-success       { background: #e9f7ef; color: #155724; }
.soft-fill-warning       { background: #fff3e0; color: #7a3e00; }
.soft-fill-warning-soft  { background: #fff8f0; color: #7a3e00; }
body.dark-mode .soft-box-muted { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); }
body.dark-mode .soft-box-info { background: #0d2233; border-color: #1a4a6b; }
body.dark-mode .soft-box-warning,
body.dark-mode .soft-box-warning-strong { background: #2d2200; border-color: #6b5800; }
body.dark-mode .soft-fill-success { background: #0d2d1a; color: #75d88a; }
body.dark-mode .soft-fill-warning,
body.dark-mode .soft-fill-warning-soft { background: #2d2200; color: #ffd966; }

/* Barra de ações em massa (#bulkBar) — fundo claro inline (#f8f9fa) nos 12 templates
   de lista; !important porque o style inline tem prioridade sobre a folha. */
body.dark-mode #bulkBar { background: var(--dm-surface, #2d2d2d) !important; border-bottom-color: var(--dm-border, #505050) !important; }
body.dark-mode #bulkCount { color: var(--dm-text, #e0e0e0) !important; }

/* ── Dark mode: escapes de fundo branco em filtros e tabelas (M.62) ───── */
/* (a) Select2 (filtros) — tema "default" do select2.min.css carrega depois;
   prefixo .select2-container (0,3,0) vence as regras do tema (0,2,0). */
body.dark-mode .select2-container .select2-selection--multiple,
body.dark-mode .select2-container .select2-selection--single { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); }
body.dark-mode .select2-container .select2-selection__rendered,
body.dark-mode .select2-container .select2-selection--single .select2-selection__rendered { color: var(--dm-text, #e0e0e0); }
body.dark-mode .select2-container .select2-selection__placeholder { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .select2-container .select2-selection--multiple .select2-selection__choice { background: var(--dm-border, #505050); color: var(--dm-text, #e0e0e0); }
body.dark-mode .select2-dropdown { background: var(--dm-surface, #2d2d2d); border-color: var(--dm-border, #505050); }
body.dark-mode .select2-results__option { color: var(--dm-text, #e0e0e0); }
body.dark-mode .select2-results__option[aria-selected=true] { background-color: var(--dm-elevated, #404040); }
body.dark-mode .select2-search--dropdown .select2-search__field { background: var(--dm-elevated, #404040); color: var(--dm-text, #e0e0e0); border-color: var(--dm-border, #505050); }
/* Multi-select: campo de busca INLINE (área de digitação) — sem esta regra o
   input fica branco sobre o container escuro (ex.: Competitors no Pipeline). */
body.dark-mode .select2-container .select2-search--inline .select2-search__field { background: transparent; color: var(--dm-text, #e0e0e0); }
body.dark-mode .select2-container .select2-search--inline .select2-search__field::placeholder { color: var(--dm-text-muted, #b0b0b0); }

/* (b) Forecast x Revisão / (c) Análise de Vendas — as colunas congeladas não têm
   mais override de dark mode: fundo, hover e sombra saem das variáveis
   --tbl-frozen-* (ver "CÉLULAS CONGELADAS"), que body.dark-mode já redefine.  */

/* (d, e) .card-title segue o tema (tinha color: var(--dark-text) fixo) — Comparativo Anual, partnerships */
body.dark-mode .card-title { color: var(--dm-text, #e0e0e0); }

/* ===================================================================
   Expense Report (trips:expense_report)
   =================================================================== */
/* A área que rola usa `.table-scroll` (altura + cabeçalho congelado com fundo
   opaco e borda que acompanha) — ver "CÉLULAS CONGELADAS". */
.er-apply-bar {
    padding: 15px;
    border-top: 1px solid #dee2e6;
}
.er-selected-count {
    margin-left: 15px;
    color: #6c757d;
}
.er-year-item {
    border-bottom: 1px solid #dee2e6;
}
.er-year-header {
    padding: 10px 15px;
    background-color: #f8f9fa;
    cursor: pointer;
    user-select: none;
}
.er-year-header:hover {
    background-color: #e9ecef;
}
/* A viagem é a unidade de envio: o checkbox e o rótulo dela ocupam as linhas dos
   seus dias (rowspan). A borda mais forte é o que separa uma viagem da próxima —
   sem ela a lista vira um bloco só de dias e some a noção de período. */
.er-trip-col { width: 210px; }
tr.er-entry-start > td { border-top: 2px solid #adb5bd; }
body.dark-mode tr.er-entry-start > td { border-top-color: var(--dm-border, #505050); }

.er-file-info { width: 60%; }
.er-file-actions { width: 40%; text-align: right; }
.er-empty {
    padding: 30px;
    text-align: center;
    color: #6c757d;
}

body.dark-mode .er-apply-bar {
    border-top-color: var(--dm-border, #505050);
}
body.dark-mode .er-year-item {
    border-bottom-color: var(--dm-border, #505050);
}
body.dark-mode .er-year-header {
    background-color: #363636;
}
body.dark-mode .er-year-header:hover {
    background-color: var(--dm-elevated, #404040);
}
body.dark-mode .er-selected-count,
body.dark-mode .er-empty {
    color: #909090;
}

/* ── Partnerships (CRM) — utilitários de lista/funil ─────────────── */
.status-dot { font-size: 0.85em; }
.status-dot.is-active { color: #28a745; }
.status-dot.is-inactive { color: #dc3545; }
.table-empty { padding: 20px; text-align: center; }
.search-input { max-width: 400px; }
.cell-subtext { font-size: 0.85em; }

/* Funil de parcerias (kanban por status) */
.pipeline-board { display: flex; gap: 16px; overflow-x: auto; padding-bottom: 8px; }
.pipeline-col { flex: 0 0 260px; background: var(--card-bg, #f8f9fa); border: 1px solid #dee2e6; border-radius: 8px; }
.pipeline-col-header { padding: 10px 12px; border-bottom: 1px solid #dee2e6; font-weight: 600; display: flex; justify-content: space-between; align-items: center; }
.pipeline-col-count { background: #e9ecef; border-radius: 12px; padding: 1px 9px; font-size: 0.8em; color: #495057; }
.pipeline-card { display: block; padding: 10px 12px; border-bottom: 1px solid #eee; text-decoration: none; color: inherit; }
.pipeline-card:hover { background: rgba(0,0,0,0.03); }
.pipeline-card-title { font-weight: 600; }
.pipeline-card-meta { font-size: 0.82em; color: #6c757d; }
.pipeline-prio { display: inline-block; min-width: 18px; text-align: center; border-radius: 4px; font-size: 0.78em; font-weight: 700; padding: 0 5px; margin-right: 6px; background: #e9ecef; }
body.dark-mode .pipeline-col { background: #2a2a2a; border-color: #444; }
body.dark-mode .pipeline-col-header,
body.dark-mode .pipeline-card { border-color: #444; }
body.dark-mode .pipeline-col-count { background: #444; color: #ddd; }
.card-note { padding: 0 16px 12px; }

/* ── Select de token de cor: bolinha colorida + texto neutro ─────── */
.ctsel { position: relative; }
.ctsel-native { position: absolute; opacity: 0; width: 0; height: 0; pointer-events: none; }
.ctsel-btn { display: flex; align-items: center; gap: 8px; width: 100%; text-align: left; cursor: pointer; }
.ctsel-dot { width: 12px; height: 12px; border-radius: 50%; flex: 0 0 auto; display: inline-block; border: 1px solid rgba(0,0,0,0.15); }
.ctsel-dot-none { background: transparent; border-style: dashed; }
.ctsel-label { color: inherit; }
.ctsel-list { position: absolute; z-index: 50; left: 0; right: 0; top: 100%; margin-top: 2px; background: #fff; border: 1px solid #ced4da; border-radius: 6px; box-shadow: 0 4px 12px rgba(0,0,0,0.12); max-height: 260px; overflow: auto; display: none; }
.ctsel-list.open { display: block; }
.ctsel-item { display: flex; align-items: center; gap: 8px; padding: 6px 10px; cursor: pointer; }
.ctsel-item:hover { background: var(--light-bg, #f8f9fa); }
body.dark-mode .ctsel-list { background: var(--dm-surface); border-color: var(--dm-border); }
body.dark-mode .ctsel-item:hover { background: var(--dm-elevated); }

/* ── Editor CSS: seção de Cores Reutilizáveis (tokens) ───────────── */
/* .css-section-body tem display:grid no <style> do template (carrega após main.css, mesma especificidade).
   Duplo seletor (0,2,0) > (0,1,0) garante que o token section não herde o grid de colunas. */
.css-section-body.token-section-body { display: block; }
.token-intro { margin-bottom: 12px; }

/* Wrapper único para header + built-in rows + custom rows */
.token-all-rows { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
.token-all-rows .token-rows { display: flex; flex-direction: column; gap: 6px; margin-bottom: 0; }

/* Linha de cabeçalho */
.token-row-header { padding-bottom: 5px; border-bottom: 1px solid #dee2e6; margin-bottom: 2px; }
.token-row-header .tc-name,
.token-row-header .tc-slug { flex: 1 1 120px; max-width: 200px; min-width: 0; font-size: 11px; font-weight: 600; color: #6c757d; text-transform: uppercase; letter-spacing: 0.04em; }
.token-row-header .tc-color { flex: 0 0 38px; text-align: center; font-size: 10px; font-weight: 600; color: #6c757d; text-transform: uppercase; letter-spacing: 0.04em; line-height: 1.3; }
.token-row-header .tc-action { flex: 0 0 auto; }

/* Linha de token (base) */
.token-row { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap; }
.token-row .form-control { flex: 1 1 120px; min-width: 0; max-width: 200px; }
.token-row .btn { flex: 0 0 auto; }

/* Input de cor standalone (substituiu label.token-color) */
.token-color-input { width: 38px; height: 30px; padding: 0; border: 1px solid #dee2e6; border-radius: 4px; cursor: pointer; flex: 0 0 auto; }
.token-color-input:disabled { opacity: 0.45; cursor: default; }

/* Linha built-in — rótulo/slug readonly (Bootstrap cuida do visual), cor é editável */
.token-builtin-icon { flex: 0 0 auto; font-size: 13px; opacity: 0.45; line-height: 1; }

.token-add { align-self: flex-start; }

/* Dark mode — cabeçalho dos tokens (o painel agora segue o tema escuro) */
body.dark-mode .token-row-header { border-bottom-color: var(--dm-border, #505050); }
body.dark-mode .token-row-header .tc-name,
body.dark-mode .token-row-header .tc-slug,
body.dark-mode .token-row-header .tc-color { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .token-color-input { border-color: #ccc; }

/* ── Editor CSS: seção Tema (cores claro + escuro lado a lado) ────── */
/* .css-section-body é grid no <style> do template; duplo seletor (0,2,0) desfaz o grid. */
.css-section-body.theme-section-body { display: block; }
.theme-intro { margin-bottom: 14px; }
.theme-table { display: flex; flex-direction: column; }

.theme-row { display: flex; align-items: center; gap: 14px; padding: 7px 0; border-bottom: 1px solid #f0f1f3; }
.theme-row:last-child { border-bottom: none; }
.theme-row .tr-label { flex: 1 1 230px; min-width: 0; display: flex; flex-direction: column; gap: 1px; }
.theme-row .tr-name { font-size: 13px; font-weight: 500; color: #2c3e50; }
.theme-row .tr-desc { font-size: 11px; color: #868e96; }
.theme-row .tr-cell { flex: 0 0 185px; }

/* Cabeçalho da tabela (Claro / Escuro) */
.theme-row-head { border-bottom: 2px solid #dee2e6; padding-bottom: 6px; }
.theme-row-head .tr-head { font-size: 11px; font-weight: 600; color: #6c757d; text-transform: uppercase; letter-spacing: 0.04em; }

/* Separador de grupo (Marca / Status / Superfícies e Texto) */
.tr-group { font-size: 11px; font-weight: 700; color: #adb5bd; text-transform: uppercase; letter-spacing: 0.06em; margin: 12px 0 3px; padding-top: 9px; border-top: 1px dashed #e9ecef; }
.theme-table > .tr-group:first-of-type { border-top: none; padding-top: 2px; margin-top: 4px; }

/* Célula sem valor de modo escuro (mapa de rotas: mesma cor nos dois modos) */
.color-input-row.tr-na { background: transparent; justify-content: center; }
.tr-dash { color: #adb5bd; font-size: 13px; }

/* Tabela em que NENHUMA linha tem modo escuro (paleta dos gráficos): a coluna
   inteira de "—" só ocupa espaço e sugere que falta preencher algo. Some a
   célula, cabeçalho junto — as linhas continuam vindo do mesmo include. */
.theme-table.tt-no-dark .theme-row > .tr-cell:last-child { display: none; }

/* Gestão de Menus: capabilities escopadas ligadas por submenu (engrenagem do
   cartão). O selo no cartão mostra o que está ligado sem precisar abrir a
   engrenagem de cada um; a marca ✓ aparece no item do menu correspondente. */
.card-scope-toggle .scope-mark { display: inline-block; width: 12px; }
.card-scope-toggle.is-on .scope-mark::before { content: '✓'; color: var(--success-color, #28a745); }
.card-scopes { display: flex; gap: 4px; flex-wrap: wrap; margin-top: 3px; }
.card-scope-badge {
    display: none; font-size: 9.5px; line-height: 1.5; padding: 0 5px; border-radius: 8px;
    background: #e7f1ff; color: #0b5ed7; border: 1px solid #cfe2ff; white-space: nowrap;
}
.card-scope-badge.show { display: inline-block; }
body.dark-mode .card-scope-badge { background: #16324f; color: #9ec5fe; border-color: #29527a; }

/* Multi-select da barra de filtros (PIC do Calendário e do Trip Planning).
   `select[multiple]` desenha a lista a partir do TOPO: com size=1 o texto ficava
   colado na borda de cima e sobrava vazio embaixo, o que salta aos olhos quando o
   filtro está sozinho na barra. A altura continua a do `.form-select` vizinho —
   encolher o campo o desalinharia dos filtros ao lado no Calendário. */
.filter-bar select.form-select[multiple] {
    padding-top: 0.55rem;
    background-image: none;   /* sem seta: multiple não abre dropdown */
}

/* ---------- Select2 múltiplo da barra de filtros: SEMPRE uma linha ----------
   O select2 cresce em altura conforme as pills quebram de linha, e a barra
   inteira (que é sticky) cresce junto: no Sales Dashboard os 4 tipos de produto
   já abriam o campo em 2 linhas — 59px em vez de 33px — empurrando os gráficos
   para baixo. Aqui a lista de pills NÃO quebra: rola na horizontal e o campo
   mantém a altura do `.form-select` vizinho, seja qual for a quantidade
   selecionada.
   Tema: o select2 que carrega no navegador é o `--default`; o bloco
   `--classic` lá de cima (pills azuis) não pega em nada.
   ATENÇÃO à especificidade: o select2.min.css entra DEPOIS do main.css (ver
   base.html), então empate perde. Por isso os seletores abaixo repetem a cadeia
   inteira do tema (`… --multiple .select2-…`) e somam `.filter-bar` por cima. */
.filter-bar .select2-container--default .select2-selection--multiple {
    display: flex;
    align-items: center;
    min-height: 39px;   /* = .form-select (padding 9px + font 12.6px + borda) */
    padding: 0 6px;
}

/* O "limpar tudo" é `position: absolute; right: 0` no tema: fica por cima das
   pills, então a faixa da direita continua reservada para ele — só passa a ser
   centralizado na altura, já que agora o campo tem uma linha só. */
.filter-bar .select2-container--default .select2-selection--multiple.select2-selection--clearable {
    padding-right: 26px;
}
.filter-bar .select2-container--default .select2-selection--multiple .select2-selection__clear {
    top: 50%;
    margin-top: 0;
    transform: translateY(-50%);
}

.filter-bar .select2-container--default .select2-selection--multiple .select2-selection__rendered {
    flex: 1 1 auto;
    min-width: 0;             /* sem isto o flex item não encolhe e nada rola */
    display: flex;
    flex-wrap: nowrap;
    align-items: center;
    gap: 4px;
    overflow-x: auto;
    margin: 0;
    padding: 0;
    scrollbar-width: none;    /* a barra de rolagem comeria a altura da linha */
    -ms-overflow-style: none;
}
.filter-bar .select2-container--default .select2-selection--multiple .select2-selection__rendered::-webkit-scrollbar {
    display: none;
}

/* Pill não encolhe: espremer o texto ("Power Sup…") é pior do que rolar. */
.filter-bar .select2-container--default .select2-selection--multiple .select2-selection__choice {
    flex: 0 0 auto;
    margin: 0;
}

/* Neste build o campo de busca é IRMÃO da <ul>, não um <li> dentro dela. Com
   pills selecionadas ele cede o espaço para elas; com a <ul> VAZIA é ele quem
   carrega o placeholder ("Select options"), e aí é a <ul> que não pode esticar
   — esticando, jogava o placeholder para o meio do campo. */
.filter-bar .select2-container--default .select2-selection--multiple > .select2-search--inline {
    flex: 0 1 auto;
    min-width: 0;
    margin: 0;
}
.filter-bar .select2-container--default .select2-selection--multiple > .select2-search--inline .select2-search__field {
    min-width: 0;
    margin: 0;
}
.filter-bar .select2-container--default .select2-selection--multiple > .select2-selection__rendered:empty {
    flex: 0 0 auto;
}
.filter-bar .select2-container--default .select2-selection--multiple > .select2-selection__rendered:empty + .select2-search--inline {
    flex: 1 1 auto;
}

/* ---------- Radio dentro da barra de filtros ----------
   Mesmo par rótulo-em-cima/controle-embaixo dos selects vizinhos, para os
   círculos caírem na mesma linha dos campos em vez de flutuarem no topo. */
.filter-bar .filter-radio-row {
    display: flex;
    align-items: center;
    gap: 14px;
    min-height: 39px;   /* = .form-select, alinha com os selects ao lado */
}
.filter-bar .filter-radio-row .form-check {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    padding: 0;
    min-height: 0;
    white-space: nowrap;
}
.filter-bar .filter-radio-row .form-check-input {
    margin: 0;
    float: none;
}
.filter-bar .filter-radio-row .form-check-label {
    margin: 0;
    font-size: 12.6px;
    cursor: pointer;
}

@media (max-width: 767px) {
    /* No celular todo controle é alvo de toque de 44px (ver `.form-select` do
       device-mobile); o select2 e os radios acompanham, senão a linha de
       filtros fica com campos de alturas diferentes. */
    body.device-mobile .filter-bar .select2-container--default .select2-selection--multiple,
    body.device-mobile .filter-bar .filter-radio-row {
        min-height: 44px;
    }

    /* Na faixa horizontal rolável do celular toda coluna vira 160px fixos (ver
       "Filtros no celular"); o par de radios não cabe nisso e quebraria em duas
       linhas, então esta coluna usa a largura do próprio conteúdo. */
    body.device-mobile .filter-bar .row > .filter-col-radio {
        width: auto;
        max-width: none;
    }
}

/* Checkbox no lugar do "—" (opção que pertence à linha, ex.: almofada do rótulo) */
.color-input-row.tr-check { background: transparent; cursor: pointer; gap: 6px; font-size: 12px; color: #495057; }
.tr-check input { margin: 0; cursor: pointer; }
body.dark-mode .color-input-row.tr-check { color: var(--dm-text, #e0e0e0); }

/* Escolha da forma do marcador: botões com o glifo, ocupando as duas colunas */
.theme-row .tr-cell-wide { flex: 0 0 384px; }
.shape-picker { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }
.shape-opt { cursor: pointer; margin: 0; }
.shape-opt input { position: absolute; opacity: 0; pointer-events: none; }
.shape-glyph {
    display: inline-flex; align-items: center; justify-content: center;
    width: 30px; height: 26px; font-size: 15px; line-height: 1; color: #495057;
    border: 1px solid #dee2e6; border-radius: 4px; background: #fff;
}
.shape-opt:hover .shape-glyph { border-color: #adb5bd; }
.shape-opt input:checked + .shape-glyph {
    border-color: var(--primary-color, #0067b5);
    box-shadow: inset 0 0 0 1px var(--primary-color, #0067b5);
    color: var(--primary-color, #0067b5);
}
.shape-opt input:focus-visible + .shape-glyph { outline: 2px solid var(--primary-light, #3385c6); outline-offset: 1px; }
body.dark-mode .shape-glyph { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); color: var(--dm-text, #e0e0e0); }

/* Contorno próprio do veículo (SystemCSS.MARKER_PATHS), o mesmo que o mapa
   desenha. `fill: currentColor` faz o ícone acompanhar a cor do botão — inclusive
   o destaque de selecionado e o modo escuro, que já mexem em `color`. */
.shape-svg { width: 17px; height: 17px; fill: currentColor; display: block; }
.trl-mk .shape-svg { width: 15px; height: 15px; }

/* Célula somente-leitura (Fase 1: Superfície/Bordas/Texto Secundário no claro) */
.color-input-row.tr-ro { background: #f8f9fa; }
.tr-swatch { width: 22px; height: 22px; border-radius: 3px; border: 1px solid rgba(0,0,0,0.12); flex: 0 0 auto; }
.tr-ro .color-hex { color: #868e96; }
.tr-lock { font-size: 11px; opacity: 0.5; flex: 0 0 auto; }

@media (max-width: 720px) {
    .theme-row { flex-wrap: wrap; }
    .theme-row .tr-cell { flex: 1 1 45%; }
}

/* ── Editor CSS: pré-visualização Claro | Escuro (mini-mockup) ────────────
   Cada coluna recebe CSS vars (--tp-* / --a-*) via JS a partir dos pickers;
   os elementos de amostra referenciam essas vars (auto-contido, independe do
   modo da própria página do editor). */
.theme-preview { margin-top: 16px; border-top: 1px dashed #e9ecef; padding-top: 12px; }
.theme-preview .tp-title-row { font-size: 11px; font-weight: 700; color: #adb5bd; text-transform: uppercase; letter-spacing: 0.06em; margin-bottom: 8px; }
.tp-cols { display: flex; gap: 14px; flex-wrap: wrap; }
.tp-col { flex: 1 1 280px; min-width: 240px; border-radius: 8px; padding: 10px; background: var(--tp-page, #f0f2f5); }
.tp-mode { font-size: 11px; font-weight: 600; margin-bottom: 8px; }
.tp-col[data-mode="light"] .tp-mode { color: #495057; }
.tp-col[data-mode="dark"]  .tp-mode { color: #cfcfcf; }

.tp-surface { border: 1px solid var(--tp-bd, #dee2e6); border-radius: 6px; overflow: hidden; background: var(--tp-bg, #fff); }
.tp-thead { background: var(--tp-elev, #f8f9fa); color: var(--tp-txt, #212529); padding: 6px 10px; font-size: 12px; font-weight: 600; }
.tp-pad { padding: 10px; }
.tp-card-title { color: var(--tp-txt, #212529); font-weight: 600; font-size: 13px; }
.tp-muted { color: var(--tp-mut, #6c757d); font-size: 11px; margin: 2px 0 6px; }
.tp-line { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 6px; }
.tp-btn { font-size: 11px; padding: 3px 10px; border-radius: 4px; color: #fff; font-weight: 500; }
.tp-btn-p { background: var(--tp-p, #0067b5); }
.tp-btn-s { background: var(--tp-s, #6c757d); }
.tp-link { color: var(--tp-link, #0067b5); font-size: 12px; text-decoration: underline; }
.tp-badge { font-size: 10px; padding: 2px 8px; border-radius: 12px; color: #fff; }
.tp-b-suc { background: var(--tp-suc, #28a745); }
.tp-b-dng { background: var(--tp-dng, #dc3545); }
.tp-b-wrn { background: var(--tp-wrn, #ffc107); color: #3a2e00; }
.tp-b-inf { background: var(--tp-inf, #17a2b8); }

/* mensagens (alerts) */
.tp-col-msg .tp-alert { padding: 8px 12px; border-radius: 7px; border-left: 4px solid transparent; font-size: 12px; margin-bottom: 8px; }
.tp-col-msg .tp-alert:last-child { margin-bottom: 0; }
.tp-a-info { background: var(--a-info-bg); color: var(--a-info-txt); border-left-color: var(--a-info-bd); }
.tp-a-suc  { background: var(--a-suc-bg);  color: var(--a-suc-txt);  border-left-color: var(--a-suc-bd); }
.tp-a-dng  { background: var(--a-dng-bg);  color: var(--a-dng-txt);  border-left-color: var(--a-dng-bd); }
.tp-a-wrn  { background: var(--a-wrn-bg);  color: var(--a-wrn-txt);  border-left-color: var(--a-wrn-bd); }

/* tags do Trip List (trips.plan) — preview claro|escuro do editor de CSS */
.tp-col-tags .tt-line { margin-top: 0; }
.tt-chip { font-size: 11px; padding: 3px 9px; border-radius: 4px; font-weight: 500; }
.tt-planned   { background: var(--tt-planned-bg,   #ffc107); color: var(--tt-planned-txt,   #212529); }
.tt-requested { background: var(--tt-requested-bg, #ffc107); color: var(--tt-requested-txt, #212529); }
.tt-issued    { background: var(--tt-issued-bg,    #198754); color: var(--tt-issued-txt,    #ffffff); }
.tt-cancelled { background: var(--tt-cancelled-bg, #dc3545); color: var(--tt-cancelled-txt, #ffffff); }
.tt-finished  { background: var(--tt-finished-bg,  #6c757d); color: var(--tt-finished-txt,  #ffffff); }

/* calendário e agenda — prévia claro|escuro do editor de CSS. Desenha UM dia,
   porque é ali que tudo divide espaço: os três tipos de evento, a viagem, o
   destaque de hoje e a faixa da semana. As variáveis são setadas por JS a partir
   dos campos da seção (nomes --cp-*, para não colidirem com as do sistema). */
.tp-col-cal .cal-grid { display: flex; border: 1px solid var(--tp-bd, #dee2e6); border-radius: 6px; overflow: hidden; }
.tp-col-cal .cal-wk { flex: 0 0 42px; display: flex; align-items: center; justify-content: center;
    font-size: 11px; font-weight: 700; background: var(--cp-week-bg, #f8f9fa); color: var(--cp-week-txt, #212529); }
.tp-col-cal .cal-day { flex: 1; padding: 6px; background: var(--cp-today-bg, #fff3cd); }
.tp-col-cal .cal-num { font-size: 11px; font-weight: 700; margin-bottom: 4px; color: var(--tp-txt, #212529); }
.tp-col-cal .cal-chip, .tp-col-cal .cal-trip {
    font-size: 10.5px; line-height: 1.25; padding: 2px 5px; margin-bottom: 3px;
    border-radius: 2px; border-left: 3px solid transparent; color: var(--tp-txt, #212529);
}
.tp-col-cal .cal-chip span { color: var(--tp-mut, #6c757d); }
.tp-col-cal .cal-chip-personal { background: var(--cp-ev-personal-bg); border-left-color: var(--cp-ev-personal-bd); }
.tp-col-cal .cal-chip-global   { background: var(--cp-ev-global-bg);   border-left-color: var(--cp-ev-global-bd); }
.tp-col-cal .cal-chip-holiday  { background: var(--cp-ev-holiday-bg);  border-left-color: var(--cp-ev-holiday-bd); }
.tp-col-cal .cal-trip { background: var(--cp-trip-bg); border-left-color: var(--cp-trip-bd); margin-bottom: 0; }
.tp-col-cal .cal-dest { color: var(--cp-trip-accent); }
.tp-col-cal .sched-head { margin-top: 8px; font-size: 11px; font-weight: 600; padding: 5px 8px;
    border-radius: 4px; text-align: center; background: var(--cp-sched-bg); color: var(--cp-sched-txt); }

/* traços do mapa de rotas (trips.plan) — prévia do editor de CSS.
   Uma coluna só: o mapa desenha a terra clara nos dois modos, então não há par
   claro|escuro a comparar. Cor = modal do trecho; traço = reserva confirmada. */
.tr-legend { display: flex; flex-wrap: wrap; gap: 8px 18px; align-items: center; }
.trl-item { display: flex; align-items: center; gap: 7px; font-size: 11.5px; color: #6c757d; }
.trl-line { width: 46px; height: 0; flex: 0 0 auto; border-top-width: 2.5px; border-top-style: solid; }
.trl-air     { border-top-color: var(--trl-air, #0066cc); }
.trl-ground  { border-top-color: var(--trl-ground, #e07b00); }
.trl-planned { border-top-color: var(--trl-planned, #ffc107); border-top-style: dashed; }
/* O contorno é do SWATCH, não da cor: o azul-marinho do aeroporto some sobre a
   superfície escura do editor — no mapa ele fica sobre a terra clara. */
.trl-dot { width: 9px; height: 9px; flex: 0 0 auto; border: 1px solid rgba(0,0,0,0.18); }
.trl-city { background: var(--trl-city, #e07b00); }          /* quadrado, como no mapa */
.trl-airport { background: var(--trl-airport, #163E64); border-radius: 50%; }
/* Rótulo de exemplo: mostra a cor fazendo o papel de TEXTO, que é onde ela precisa
   ter contraste — o swatch sozinho não diz se o nome da cidade vai ficar legível.
   O retângulo é a TERRA do mapa (mesma cor dos dois modos), não um fundo de UI:
   sem ele a amostra mentiria sobre onde o rótulo vai parar. */
/* A cena é um pedaço da TERRA do mapa — é sobre ela que o rótulo é desenhado, e é
   o contraste com ela que faz a placa aparecer ou não.
   ⚠️ Já teve uma rota atravessando o rótulo aqui, para mostrar a placa "apagando" a
   linha. Passava a ideia errada: ligar a opção parecia tirar um risco do texto, e
   não pôr uma caixa atrás dele. */
.trl-scene {
    display: inline-flex; align-items: center; justify-content: center;
    /* Folga generosa: a placa precisa se ler como um retângulo MENOR dentro de um
       pedaço de mapa. Com a cena colada no texto, ligar a opção só trocava o tom
       do fundo e não parecia caixa nenhuma. */
    padding: 7px 18px; border-radius: 3px;
    background-color: var(--trip-route-land, #e8f4f8);
}
/* Peso normal por padrão: o negrito é opção da tela, não o estado natural. */
.trl-txt { font-weight: 400; line-height: 1.2; }
.trl-txt.trl-bold { font-weight: 700; }
.trl-txt-city { color: var(--trl-city, #e07b00); }
.trl-txt-airport { color: var(--trl-airport, #163E64); }
/* "Filled" = a placa por trás do rótulo, tapando o que passa por baixo. É o mesmo
   retângulo que o mapa desenha em SVG (drawLabelBoxes) e a MESMA cor: pintá-la da
   cor da terra a deixaria invisível justamente no mapa, que é onde ela serve. */
/* Retângulo de cantos arredondados, igual ao que a camada de rótulos desenha no
   mapa (.map-label). */
.trl-txt.trl-boxed { padding: 0 4px; border-radius: 3px; }
.trl-txt-city.trl-boxed { background: var(--trl-city-box, #ffffff); }
.trl-txt-airport.trl-boxed { background: var(--trl-airport-box, #ffffff); }

/* Ponto animado: cor + FORMA por modal. O conteúdo é o próprio glifo (o JS copia
   do botão escolhido), então a amostra é literalmente o que vai para o mapa. */
.trl-mk { font-size: 15px; line-height: 1; flex: 0 0 auto; }
.trl-mk-air { color: var(--trl-mk-air, #599cde); }
.trl-mk-ground { color: var(--trl-mk-ground, #eba959); }

/* paleta dos gráficos — prévia do editor de CSS.
   Uma coluna só, como o mapa: o gráfico é desenhado sobre fundo transparente e
   usa a MESMA paleta nos dois modos. As cores vêm do JS (a rampa é calculada,
   não existe como variável), então aqui ficam só forma e espaçamento. */
.cp-ramps { display: flex; flex-direction: column; gap: 6px; }
.cp-ramp-row { display: flex; align-items: center; gap: 10px; }
.cp-ramp-label { font-size: 11px; color: #868e96; width: 62px; flex-shrink: 0; text-align: right; }
.cp-ramp { display: flex; flex: 1; height: 20px; border-radius: 4px; overflow: hidden; }
.cp-sw { flex: 1; }

.cp-shapes { display: flex; flex-wrap: wrap; gap: 18px; align-items: flex-end; margin-top: 14px; }
.cp-shape { display: flex; flex-direction: column; align-items: center; gap: 6px; }
.cp-shape-label { font-size: 10px; color: #adb5bd; text-transform: uppercase; letter-spacing: 0.05em; }
.cp-pie { width: 74px; height: 74px; border-radius: 50%; display: block; }
/* Rosca = pizza com o miolo vazado. O furo usa `mask` para o fundo do painel
   aparecer no meio (claro OU escuro) — pintá-lo de branco fixo deixaria uma
   moeda branca no modo escuro. */
.cp-donut { -webkit-mask: radial-gradient(circle, transparent 0 40%, #000 41%); mask: radial-gradient(circle, transparent 0 40%, #000 41%); }
.cp-bars { display: flex; align-items: flex-end; gap: 5px; height: 74px; }
.cp-bar { width: 13px; border-radius: 2px 2px 0 0; }
.cp-exc-row { display: flex; gap: 6px; align-items: center; height: 74px; }
.cp-exc { font-size: 10px; color: #fff; padding: 4px 8px; border-radius: 3px; font-weight: 600; text-shadow: 0 1px 1px rgba(0,0,0,0.35); }
.chart-competitor-note { margin-top: 12px; padding: 8px 12px; background: #f8f9fa; border-radius: 6px; }

body.dark-mode .cp-ramp-label,
body.dark-mode .cp-shape-label { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .chart-competitor-note { background: var(--dm-elevated, #404040); }

body.dark-mode .trl-item { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .trl-dot { border-color: rgba(255,255,255,0.45); }

body.dark-mode .theme-preview { border-top-color: var(--dm-border, #505050); }

/* ── Editor CSS: modo escuro do próprio painel ───────────────────── */
/* O <style> inline do template fixa o painel em claro (especificidade 0,1,0).
   body.dark-mode + classe = (0,2,x) vence e faz o editor seguir o tema escuro. */
body.dark-mode .css-section { background: var(--dm-surface, #2d2d2d); border-color: var(--dm-border, #505050); }
body.dark-mode .css-section-header { background: var(--dm-elevated, #404040); border-bottom-color: var(--dm-border, #505050); }
body.dark-mode .css-section-header h2 { color: var(--dm-text, #e0e0e0); }
body.dark-mode .css-field label { color: var(--dm-text, #e0e0e0); }
body.dark-mode .css-field .field-desc { color: var(--dm-text-muted, #b0b0b0); }

body.dark-mode .color-input-row { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); }
body.dark-mode .color-input-row .color-hex { color: var(--dm-text, #e0e0e0); }
body.dark-mode .color-default-badge { background: var(--dm-surface, #2d2d2d); color: var(--dm-text-muted, #b0b0b0); }

body.dark-mode .number-input-row { border-color: var(--dm-border, #505050); }
body.dark-mode .number-input-row input[type="number"] { background: var(--dm-elevated, #404040); color: var(--dm-text, #e0e0e0); }
body.dark-mode .number-input-row .unit { background: var(--dm-surface, #2d2d2d); border-left-color: var(--dm-border, #505050); color: var(--dm-text-muted, #b0b0b0); }

body.dark-mode .css-select { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); color: var(--dm-text, #e0e0e0); }

body.dark-mode .css-checkbox-row { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); }
body.dark-mode .css-checkbox-row .check-label strong { color: var(--dm-text, #e0e0e0); }
body.dark-mode .css-checkbox-row .check-label span { color: var(--dm-text-muted, #b0b0b0); }

body.dark-mode .css-field .field-warning { background: #3a2e00; border-color: #6b5800; color: #ffd966; }

/* Tabela Tema no escuro */
body.dark-mode .theme-intro,
body.dark-mode .token-intro { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .theme-row { border-bottom-color: var(--dm-border, #505050); }
body.dark-mode .theme-row .tr-name { color: var(--dm-text, #e0e0e0); }
body.dark-mode .theme-row .tr-desc { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .theme-row-head { border-bottom-color: var(--dm-border, #505050); }
body.dark-mode .theme-row-head .tr-head { color: var(--dm-text-muted, #b0b0b0); }
body.dark-mode .tr-group { color: var(--dm-text-muted, #b0b0b0); border-top-color: var(--dm-border, #505050); }
body.dark-mode .color-input-row.tr-ro { background: var(--dm-surface, #2d2d2d); }
body.dark-mode .tr-ro .color-hex { color: var(--dm-text-muted, #b0b0b0); }

/* ── Campanha de Venda: linhas de parceiro + comissão ────────────── */
.campaign-partner-row { display: flex; align-items: flex-end; gap: 10px; flex-wrap: wrap; padding: 8px 0; border-bottom: 1px solid #eee; }
.campaign-partner-row .cpr-field { display: flex; flex-direction: column; }
.campaign-partner-row .cpr-partner { flex: 1 1 260px; min-width: 0; }
.campaign-partner-row .cpr-field .form-label { margin-bottom: 2px; }
.campaign-partner-row .cpr-del { margin-left: auto; }
.campaign-partner-row .cpr-delbox { display: none; }
body.dark-mode .campaign-partner-row { border-color: var(--dm-border); }

/* ── Role permissions matrix (Gestão de Perfis) ──────────────────────── */
.perm-section-card { overflow: hidden; }
.perm-table { margin-bottom: 0; }
.perm-table th,
.perm-table td { vertical-align: middle; padding: .4rem .5rem; }

/* Sticky header: the column-label row and the section (master) row stay locked
   at the top of each section card while its submenu rows scroll past. */
.perm-table thead th { position: sticky; z-index: 3; }
.perm-table thead tr.perm-colhead-row th {
    top: 0;
    background: #eef1f4;
    font-size: 0.64rem;
    text-transform: uppercase;
    letter-spacing: .03em;
    color: #6c757d;
    font-weight: 600;
    border-bottom: 0;
}
.perm-table thead tr.perm-section-row th {
    top: 22px;                 /* sits right below the column-label row */
    background: #e2e6ea;
    border-top: 0;
}

.perm-name-col { text-align: left; width: 40%; }
.perm-cap-col { text-align: center; width: 15%; white-space: nowrap; }

.perm-section-label { font-weight: 700; }
.perm-submenu-label { padding-left: 1.7rem; }
.perm-required-badge { font-size: 0.6rem; }
/* Scoped capability (tech_scope) that has no effect on this submenu: a dash instead of
   a checkbox that would silently do nothing. */
.perm-cap-na { color: #adb5bd; user-select: none; }
body.dark-mode .perm-cap-na { color: #6c757d; }

.perm-collapse-btn {
    border: 0; background: transparent; cursor: pointer;
    padding: 0 .35rem 0 0; font-size: .8rem; color: #495057; line-height: 1;
}
.perm-caret { display: inline-block; width: 1em; }

.perm-table tbody.perm-collapsed { display: none; }

.perm-table .form-check-input { margin: 0 auto; cursor: pointer; float: none; }
.perm-submenu-row:hover { background: rgba(0,0,0,0.02); }

/* Dark mode */
body.dark-mode .perm-section-card { border-color: var(--dm-border); background: var(--dm-surface); }
body.dark-mode .perm-table { color: var(--dm-text); }
body.dark-mode .perm-table th,
body.dark-mode .perm-table td { border-color: var(--dm-border); }
body.dark-mode .perm-table thead tr.perm-colhead-row th { background: var(--dm-elevated); color: var(--dm-text-muted); }
body.dark-mode .perm-table thead tr.perm-section-row th { background: var(--dm-bg); }
body.dark-mode .perm-collapse-btn { color: var(--dm-text); }
body.dark-mode .perm-submenu-row:hover { background: rgba(255,255,255,0.03); }

/* ===== Sales Orders / Pipeline / Fulfillment lists: freeze first two columns ===== */
/* The Number column embeds the customer/distributor name so its width is variable; we fix
   it and ellipsis the tail (the ZBR-YY-NNNNN prefix stays visible, full value in tooltip +
   the Customer column). This gives a deterministic left offset for the Type column.

   O `border-collapse: separate` e o redesenho da grade do `.table-bordered` que fazem a
   borda ACOMPANHAR a célula congelada agora são globais — ver "CÉLULAS CONGELADAS". Aqui
   ficam só as larguras, os offsets e o z-index desta lista. */
.so-sticky-cols th:nth-child(1),
.so-sticky-cols td:nth-child(1) {
    position: sticky;
    left: 0;
    z-index: 3;
    min-width: 240px;
    max-width: 240px;
    overflow: hidden;
    text-overflow: ellipsis;
    background-color: var(--tbl-frozen-bg);
}
.so-sticky-cols th:nth-child(2),
.so-sticky-cols td:nth-child(2) {
    position: sticky;
    left: 240px;
    z-index: 3;
    min-width: 120px;
    max-width: 120px;
    /* Coluna congelada com largura FIXA tem de cortar o excedente. Sem isto o texto
       longo transborda por cima da coluna vizinha — que ainda por cima rola por baixo
       desta, então o vazamento acompanha a rolagem. Passou despercebido enquanto aqui
       morava só um badge curto ("Type"), que sempre coube. */
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    background-color: var(--tbl-frozen-bg);
    border-right-width: 2px; /* separador do congelamento — borda PRÓPRIA, viaja com a célula */
}

/* ── Lista de andamento (financeiro.fulfillment): as duas colunas congeladas são
   Grupo e PIC, não Número e Tipo. O grupo é curto ('GW', 'Fibrasil') e o PIC é longo
   ('Alexandre Inacio (não identificado)'), então as larguras herdadas — 240 para a 1ª
   e 120 para a 2ª — ficam invertidas em relação ao conteúdo. */
.so-sticky-cols.fulfillment-cols th:nth-child(1),
.so-sticky-cols.fulfillment-cols td:nth-child(1) {
    min-width: 150px;
    max-width: 150px;
}
.so-sticky-cols.fulfillment-cols th:nth-child(2),
.so-sticky-cols.fulfillment-cols td:nth-child(2) {
    left: 150px;
    min-width: 210px;
    max-width: 210px;
}
/* ── Listas cuja dupla congelada é NOME + PIC (Pipeline e Sales Orders): o nome do
   cliente/distribuidor é longo e o do PIC também ("Diogo Nakazawa (não
   identificado)"), então nenhum dos dois cabe nas larguras originais desta seção
   (240 para a 1ª e 120 para a 2ª, pensadas para número de proposta + badge). */
.so-sticky-cols.customer-pic-cols th:nth-child(1),
.so-sticky-cols.customer-pic-cols td:nth-child(1) {
    min-width: 220px;
    max-width: 220px;
}
.so-sticky-cols.customer-pic-cols th:nth-child(2),
.so-sticky-cols.customer-pic-cols td:nth-child(2) {
    left: 220px;
    min-width: 170px;
    max-width: 170px;
}

/* `.freeze-first-only`: congela SÓ a 1ª coluna, soltando a 2ª de volta ao fluxo —
   mesma mecânica que o celular já aplica a todo `.so-sticky-cols`. Serve para lista
   cuja 2ª coluna DESAPARECE conforme a permissão: no Pipeline sem see_all_data não
   há coluna de PIC e o Produto assumiria o congelamento, mas ele tem uma linha por
   item e não sobrevive a uma largura fixa com overflow escondido. */
.so-sticky-cols.freeze-first-only tbody td:nth-child(2),
.so-sticky-cols.freeze-first-only tfoot td:nth-child(2) {
    position: static;
    left: auto;
    min-width: 0;
    max-width: none;
    border-right-width: 1px;
}
/* Prefixo `.table-responsive`: sem ele o `z-index: 11` da regra do cabeçalho
   congelado (mais abaixo, mesma especificidade) vencia por ordem de arquivo e a 2ª
   coluna — que aqui rola — continuava pintando acima das vizinhas. */
.table-responsive .so-sticky-cols.freeze-first-only thead th:nth-child(2) {
    left: auto;        /* mantém o sticky-top (top:0); tira só o sticky-left */
    min-width: 0;
    max-width: none;
    border-right-width: 1px;
    z-index: auto;
}
/* O separador de 2px do congelamento passa a ser da 1ª coluna, que agora é a última
   congelada. */
.so-sticky-cols.freeze-first-only th:nth-child(1),
.so-sticky-cols.freeze-first-only td:nth-child(1) {
    border-right-width: 2px;
}

/* Cabeçalho congelado precisa pintar ACIMA das células de cabeçalho que ROLAM (todas em
   z-index 10 via `.table-responsive thead th`; a rolada, mais tarde no DOM, cobria a
   congelada → o título "Proposal" sumia sob "Channel/Product" ao rolar). Prefixo
   `.table-responsive` eleva a especificidade acima da regra dos 10. col1 = a mais alta
   (sempre congelada); col2 acima das roladas, abaixo de col1. */
.table-responsive .so-sticky-cols thead th:nth-child(1) {
    z-index: 12;
    background-color: var(--tbl-frozen-head-bg);
}
.table-responsive .so-sticky-cols thead th:nth-child(2) {
    z-index: 11;
    background-color: var(--tbl-frozen-head-bg);
}
/* Hover: fundo OPACO — senão as células roladas vazam sob o fundo do hover de linha */
.so-sticky-cols tbody tr:not(.table-dark):not(.section-row):hover td:nth-child(1),
.so-sticky-cols tbody tr:not(.table-dark):not(.section-row):hover td:nth-child(2) {
    background-color: var(--tbl-frozen-hover-bg);
}

/* ── Order Fulfillment (andamento) read-only snapshot header ── */
.fulfillment-readonly label {
    display: block;
    font-size: 0.72rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.03em;
    color: #6c757d;
    margin-bottom: 2px;
}
.fulfillment-readonly > div > div {
    font-size: 0.95rem;
    word-break: break-word;
}
body.dark-mode .fulfillment-readonly label {
    color: var(--dm-text-muted, #9aa0a6);
}

/* ── Click-to-sort column headers (fulfillment list) ── */
th.sortable {
    cursor: pointer;
    user-select: none;
    -webkit-user-select: none;
    white-space: nowrap;
}
th.sortable:hover {
    background-color: rgba(0, 0, 0, 0.04);
}
body.dark-mode th.sortable:hover {
    background-color: rgba(255, 255, 255, 0.06);
}
th.sortable .sort-ind {
    font-size: 0.8em;
    color: #0d6efd;
}

/* ── Entrega PREVISTA na lista de andamento ──
   Entrega consumada e previsão de entrega dividem a mesma coluna, e a previsão
   precisa se anunciar como tal. Não usa .text-muted: no modo escuro
   `--dm-text-muted` está igual ao texto normal (System Administration › CSS), e a
   distinção sumiria justamente lá. Opacidade + itálico não dependem de variável de
   tema e valem nos dois. */
.deliv-forecast {
    font-style: italic;
    opacity: 0.6;
}

/* ══════════════════════════════════════════════════════════════════════════
   PÁGINA PÚBLICA DE FIRMWARE (/fw/<token>/)
   Página standalone, servida a um visitante EXTERNO sem login: não herda o
   base.html nem o sidebar, então precisa do seu próprio enquadramento.
   ══════════════════════════════════════════════════════════════════════════ */
/* O shell do app trava a rolagem em html/body (`overflow: hidden !important`,
   `height: 100dvh`, `display: flex`, `padding: 0 !important`) porque quem rola lá
   é a área de conteúdo. Esta página não tem esse shell: sem devolver a rolagem ao
   documento, um link com três arquivos ficava cortado, sem barra e sem como
   alcançar o terceiro botão. Daí o `!important` — é para vencer o do app. */
html.fw-public-html {
    overflow: auto !important;
    height: auto !important;
}
html.fw-public-html body.fw-public-body {
    background: #f4f6f8;
    display: block;
    height: auto !important;
    min-height: 100dvh;
    overflow: visible !important;
    margin: 0 !important;
    padding: 32px 16px !important;
    font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Arial, sans-serif;
    font-size: 14px;
    color: #212529;
}
.fw-public-wrap {
    max-width: 560px;
    margin: 0 auto;
}
.fw-public-logo {
    display: block;
    max-height: 44px;
    max-width: 200px;
    margin: 0 auto 24px;
}
.fw-public-card {
    background: #fff;
    border: 1px solid #e3e6ea;
    border-radius: 10px;
    padding: 28px 28px 24px;
    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
}
.fw-public-title {
    font-size: 1.4rem;
    font-weight: 600;
    margin: 0 0 12px;
    color: var(--primary-color, #0067b5);
}
.fw-public-intro {
    font-size: 0.95rem;
    line-height: 1.55;
    margin: 0 0 20px;
}
.fw-public-meta {
    display: grid;
    grid-template-columns: minmax(120px, auto) 1fr;
    gap: 6px 16px;
    margin: 0 0 20px;
    font-size: 0.9rem;
}
.fw-public-meta dt {
    color: #6c757d;
    font-weight: 500;
}
.fw-public-meta dd {
    margin: 0;
    font-weight: 600;
    word-break: break-word;
}
.fw-public-ip {
    display: flex;
    flex-direction: column;
    gap: 2px;
    background: #fff8e6;
    border: 1px solid #ffe1a3;
    border-radius: 8px;
    padding: 12px 14px;
    margin-bottom: 16px;
}
.fw-public-ip-label {
    font-size: 0.78rem;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: #8a6d1f;
}
.fw-public-ip-value {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 1.05rem;
    font-weight: 600;
    color: #5c4813;
}
.fw-public-privacy {
    font-size: 0.82rem;
    line-height: 1.5;
    color: #5a6169;
    margin: 0 0 20px;
}
.fw-public-form {
    margin: 0;
}
.fw-public-btn {
    width: 100%;
}

/* Um bloco por arquivo compartilhado (firmware / preset / ExpressBoot) */
.fw-public-files {
    display: flex;
    flex-direction: column;
    gap: 10px;
}
.fw-public-file {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    border: 1px solid #e3e6ea;
    border-radius: 8px;
    padding: 12px 14px;
}
.fw-public-file form {
    margin: 0;
}
.fw-public-file-info {
    display: flex;
    flex-direction: column;
    gap: 2px;
    min-width: 0;
}
.fw-public-file-kind {
    font-weight: 600;
    font-size: 0.95rem;
}
.fw-public-file-name {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.78rem;
    color: #6c757d;
    word-break: break-all;
}
.fw-public-file-btn {
    white-space: nowrap;
}
.fw-public-footer {
    font-size: 0.8rem;
    color: #6c757d;
    margin: 18px 0 0;
    text-align: center;
}
.fw-public-brand {
    text-align: center;
    font-size: 0.75rem;
    color: #9aa0a6;
    margin: 20px 0 0;
}

@media (prefers-color-scheme: dark) {
    /* Mesma especificidade da regra clara acima, senão o `body` do app vence. */
    html.fw-public-html body.fw-public-body { background: #16191d; color: #e4e6eb; }
    .fw-public-card { background: #1f2329; border-color: #333940; box-shadow: none; }
    .fw-public-meta dt { color: #9aa0a6; }
    .fw-public-ip { background: #33291070; border-color: #6b551d; }
    .fw-public-ip-label { color: #d9b95c; }
    .fw-public-ip-value { color: #f0d78a; }
    .fw-public-privacy { color: #a9b0b8; }
    .fw-public-file { border-color: #333940; }
    .fw-public-file-name { color: #9aa0a6; }
}

/* ── Campos de arquivo do formulário de firmware (3 colunas responsivas) ── */
.fw-file-field {
    height: 100%;
}
.fw-file-current {
    margin-bottom: 8px;
    font-size: 0.85rem;
    word-break: break-all;
}
.fw-file-hint {
    display: block;
    color: #6c757d;
    font-size: 0.75rem;
    margin-top: 4px;
}
.fw-file-hint code {
    word-break: break-all;
}
body.dark-mode .fw-file-hint {
    color: var(--dm-text-muted, #9aa0a6);
}

/* ── Share links / audit trail (customers.firmware) ── */
.fw-link-url {
    font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
    font-size: 0.78rem;
    word-break: break-all;
}

/* Faixa de preview da página pública (Templates › Firmware Page) */
.fw-public-preview-flag {
    background: #e7f1ff;
    border: 1px solid #b6d4fe;
    color: #084298;
    border-radius: 6px;
    padding: 8px 12px;
    font-size: 0.82rem;
    font-weight: 600;
    text-align: center;
    margin-bottom: 16px;
}
@media (prefers-color-scheme: dark) {
    .fw-public-preview-flag { background: #10243d; border-color: #2b4c74; color: #9ec5fe; }
}

/* ══════════════════════════════════════════════════════════════════════════
   LISTA DE CONTATOS (A/C + e-mails dos modais de Sales Order / Sample)
   Cada linha é um input-group com dropdown dos contatos cadastrados + o campo
   digitável que vai ao documento (ver static/js/contact_list.js).
   ══════════════════════════════════════════════════════════════════════════ */
/* O select do Bootstrap dentro de um .input-group cresce até caber a maior opção
   e espremeria o campo digitável; travamos a largura para que o valor real (um
   e-mail inteiro) fique sempre legível. */
.input-group .form-select.contact-picker {
    flex: 0 0 auto;
    width: auto;
    max-width: 38%;
    font-size: 0.78rem;
    padding-right: 1.6rem;
}
@media (max-width: 575.98px) {
    /* Em tela estreita o trio (dropdown + campo + ×) não cabe lado a lado:
       o dropdown ocupa a primeira linha inteira e o campo vem abaixo. */
    .input-group .form-select.contact-picker {
        flex: 1 0 100%;
        max-width: 100%;
        border-radius: 0.25rem 0.25rem 0 0;
    }
}

/* ============================================================================
   LANGUAGES (system.languages) — UI translation catalog
   ----------------------------------------------------------------------------
   The grid reuses .table-scroll / .freeze from the FROZEN CELLS section, so the
   key column stays visible while the language columns scroll sideways — the
   whole point being that you always know which key you are editing.
   ========================================================================== */

/* Controls ride inside the .nav-tabs <ul>, which lives in the sticky page
   header — anything hung outside it would scroll away while the tabs stayed
   put. ms-auto equivalent, without depending on a utility class. */
.lang-nav-tools {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-left: auto;
    padding-bottom: 6px;
    flex-wrap: wrap;
}

.lang-flag {
    font-size: 18px;
    line-height: 1;
}

/* --- key column ---------------------------------------------------------- */

.lang-grid .lang-col-key {
    min-width: 260px;
    max-width: 360px;
}

.lang-grid .lang-col-key code {
    word-break: break-all;
}

.lang-grid .lang-col-usage {
    width: 72px;
}

.lang-usage-badge {
    cursor: help;
}

/* --- editable cell ------------------------------------------------------- */

.lang-cell {
    min-width: 220px;
}

/* Save feedback lives on the border so the row height never shifts while you
   tab through dozens of cells. */
.lang-cell.is-saved {
    border-color: var(--success-color, #198754);
    box-shadow: 0 0 0 .15rem rgba(25, 135, 84, .18);
}

.lang-cell.is-failed {
    border-color: var(--danger-color, #dc3545);
    box-shadow: 0 0 0 .15rem rgba(220, 53, 69, .20);
}

/* --- forms --------------------------------------------------------------- */

/* Add/update language form.

   Each field is its own column-stack (label / input / hint). The stacks are
   top-aligned and the label height is fixed, so every input lands on the same
   line however long the hint under it runs — the previous version aligned to
   the BOTTOM, which staircased the inputs whenever one hint wrapped. */
.lang-form-row {
    display: flex;
    gap: 16px;
    flex-wrap: wrap;
    align-items: start;
}

.lang-form-row + .lang-form-row {
    margin-top: 16px;
}

.lang-form-row--actions {
    align-items: center;
    gap: 20px;
}

.lang-form-field {
    display: flex;
    flex-direction: column;
    flex: 1 1 200px;
    min-width: 170px;
}

.lang-form-field--narrow {
    flex: 0 0 120px;
    min-width: 120px;
}

.lang-form-field .form-label {
    min-height: 21px;
    margin-bottom: 4px;
}

.lang-form-field small {
    margin-top: 4px;
    line-height: 1.3;
}

.lang-check {
    display: flex;
    align-items: center;
    gap: 6px;
    margin: 0;
    white-space: nowrap;
}

.lang-form-inline {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 6px;
}

.lang-form-inline .form-control {
    max-width: 320px;
}

/* --- side-by-side preview ------------------------------------------------ */

.lang-preview-controls {
    display: flex;
    gap: 8px;
    align-items: center;
    flex-wrap: wrap;
    margin-bottom: 12px;
}

.lang-preview-controls .form-select {
    width: auto;
    min-width: 180px;
}

.lang-preview-panes {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 12px;
}

.lang-preview-frame {
    width: 100%;
    height: 560px;
    border: 1px solid var(--border-color, #dee2e6);
    border-radius: 6px;
    background: #fff;
}

@media (max-width: 992px) {
    .lang-preview-panes {
        grid-template-columns: 1fr;
    }
}

/* --- dark mode ----------------------------------------------------------- */

body.dark-mode .lang-preview-frame {
    background: #1e1e1e;
    border-color: #3a3a3a;
}

body.dark-mode .lang-grid .lang-col-key code {
    color: #9ecbff;
}

/* --- language toggle in the user menu ------------------------------------ */

/* The toggle itself is an <a>, so padding, colour, hover and dark mode all come
   from .user-menu-dropdown a. Only the flag and the label need rules here. */

.lang-cycle-form {
    display: none;
}

#languageMenuItem {
    display: flex;
    align-items: center;
    gap: 8px;
}

.lang-flag-svg {
    border-radius: 2px;
    box-shadow: 0 0 0 1px rgba(0, 0, 0, .18);
    flex: 0 0 auto;
    vertical-align: middle;
}

.lang-menu-name {
    line-height: 1;
}

/* Unknown language: the country letters, styled on purpose rather than left as
   a broken glyph. */
.lang-flag-fallback {
    display: inline-block;
    min-width: 18px;
    padding: 1px 4px;
    border-radius: 2px;
    background: var(--light-bg);
    color: var(--dark-text);
    font-size: 10px;
    font-weight: 700;
    letter-spacing: .5px;
    text-align: center;
}

body.dark-mode .lang-flag-svg {
    box-shadow: 0 0 0 1px rgba(255, 255, 255, .28);
}

body.dark-mode .lang-flag-fallback {
    background: #404040;
    color: #e8e8e8;
}


/* Separator key button in Menu Management follows the sort button's dark theme. */
body.dark-mode .group-key-btn { background: var(--dm-elevated, #404040); border-color: var(--dm-border, #505050); color: var(--dm-text, #e0e0e0); }
body.dark-mode .group-key-btn:hover { background: rgba(0,103,181,0.25); border-color: var(--primary-color, #0067b5); }

/* Editing pair: the default language is fixed on the left, the target picked on
   the right. Keeps the grid to two columns however many languages exist. */
.lang-nav-tools .form-select {
    width: auto;
    min-width: 150px;
}

.lang-editing-fixed {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 2px 8px;
    border-radius: 4px;
    background: var(--light-bg);
    font-size: 13px;
    white-space: nowrap;
}

body.dark-mode .lang-editing-fixed {
    background: #404040;
    color: #e8e8e8;
}

/* ============================================================================
   ATRIBUTO hidden — fundação
   ----------------------------------------------------------------------------
   O `hidden` do HTML esconde pela folha do navegador (display:none), que perde
   para QUALQUER display declarado depois — e as classes de layout do Bootstrap
   (.row = flex, .form-group…) declaram. Sem isto, `el.hidden = true` no JS não
   esconde nada em elemento com classe de layout. Vale para a tela inteira: quem
   alterna visibilidade por JS não precisa mais escolher entre `hidden` e
   `d-none`.
   ========================================================================== */
[hidden] {
    display: none !important;
}


/* ============================================================================
   TRIP DASHBOARD — quadro "Detailed Statistics" (dashboards.trip)
   ----------------------------------------------------------------------------
   São duas tabelas lado a lado dentro de um card estreito (col-lg-4). Sem
   `table-layout: fixed` a tabela cresce pela largura do RÓTULO mais longo e
   ignora os 45/55% declarados nas células — o valor em R$ era empurrado para
   fora do card. Com ela, a coluna respeita a proporção e o rótulo quebra.
   ========================================================================== */
.trip-breakdown .table {
    table-layout: fixed;
}

.trip-breakdown .table td {
    padding-left: 2px;
    padding-right: 2px;
    overflow-wrap: anywhere;   /* rótulo longo quebra em vez de estourar */
}

/* A `.row` do Bootstrap tem margem negativa (gutter) e fica mais larga que o
   corpo do card, que corta o excedente — some com o gutter aqui, onde as duas
   colunas já são estreitas. */
.trip-breakdown > .row {
    margin-left: 0;
    margin-right: 0;
}

.trip-breakdown > .row > [class^="col"] {
    padding-left: 4px;
    padding-right: 4px;
}

/* No celular a coluna fica mais estreita ainda; 0.7rem mantém rótulo e valor na
   mesma linha sem empilhar as duas tabelas. */
body.device-mobile .trip-breakdown .table,
body.device-tablet .trip-breakdown .table {
    font-size: 0.7rem !important;
}

/* ============================================================================
   MODAL DE VISUALIZAÇÃO COMPARTILHADO (#viewModal / .js-view-modal)
   ----------------------------------------------------------------------------
   O corpo do modal recebe a tela só-leitura inteira, buscada com ?modal=1. O
   fragmento chega com o layout de PÁGINA (page-header-sticky, content-area,
   card), que aqui dentro não serve: o modal já tem cabeçalho e já rola. Estas
   regras neutralizam esse enquadramento e deixam só o conteúdo.
   ============================================================================ */

/* O bloco mobile lá de cima (`@media (max-width:768px)`) trava .modal-content em
   90dvh com !important, para os modais legados. Aqui isso brigaria com o
   modal-fullscreen-md-down do Bootstrap e deixaria a tela cheia pela metade —
   o seletor por id ganha em especificidade e devolve o controle ao Bootstrap.
   100% (e não `none`) porque quem limita a altura é o .modal-dialog: com `none`
   um registro longo esticaria o modal para fora da viewport. */
#viewModal .modal-content {
    max-height: 100% !important;
}

/* ⚠️ Nada de `width`/`margin` em `#viewModal .modal-dialog`: o seletor por id
   vence .modal-fullscreen-md-down (uma classe) e o mobile deixa de ser tela
   cheia sem nenhum aviso. Foi exatamente o que aconteceu na primeira versão. */

.view-modal-body {
    padding: 0;
    overflow-y: auto;
    overscroll-behavior: contain;   /* rolar o modal não arrasta a lista atrás */
    -webkit-overflow-scrolling: touch;
}

/* O cabeçalho da página e a barra Back/Edit do rodapé são duplicatas do que o
   modal já oferece — o template as marca e elas somem só aqui dentro. */
.view-modal-body .page-header-sticky,
.view-modal-body .view-page-actions {
    display: none !important;
}

/* .content-area é o frame de scroll da página (flex + overflow próprio). Dentro
   do modal ele viraria um segundo scroller aninhado. */
.view-modal-body .content-area {
    flex: none;
    display: block;
    overflow: visible;
    padding: 18px;
    background: transparent;
    height: auto;
}

/* O card desenha a "folha" da página; dentro do modal a folha já é o próprio
   modal, então fica sem sombra, sem borda e sem duplicar o respiro. */
.view-modal-body .card {
    background: transparent;
    box-shadow: none;
    border: 0;
    border-radius: 0;
    padding: 0;
    margin: 0;
}

.view-modal-body .card > .card-body {
    padding: 0;
}

/* .table-responsive limita a altura pela viewport (100dvh menos topbar e header
   da página). Dentro do modal não há topbar nenhum, e a conta sobra: quem rola
   é o corpo do modal. */
.view-modal-body .table-responsive {
    max-height: none;
}

/* Estados de carregamento e de falha, no lugar do conteúdo. */
.view-modal-status {
    padding: 36px 18px;
    text-align: center;
    color: #6c757d;
}

.view-modal-status-error {
    color: var(--danger-color, #dc3545);
}

/* No celular o fullscreen encosta o conteúdo nas bordas — 12px devolve a
   margem de leitura sem gastar a largura que os campos precisam. */
@media (max-width: 767.98px) {
    /* O bloco legado põe 8px de padding em .modal para os modais caseiros; aqui
       ele deixaria uma moldura em volta da "tela cheia". */
    #viewModal {
        padding: 0;
    }

    .view-modal-body .content-area {
        padding: 12px;
    }

    /* Rodapé de tela cheia: os três botões em coluna evitam o corte do último
       quando o idioma alonga os rótulos. */
    #viewModal .modal-footer {
        flex-wrap: wrap;
    }

    #viewModal .modal-footer .btn {
        flex: 1 1 auto;
    }
}

/* Modo escuro: o corpo injetado herda os tokens do sistema, mas .card e
   .content-area trazem fundo claro fixo do CSS de página. */
body.dark-mode .view-modal-body .content-area,
body.dark-mode .view-modal-body .card {
    background: transparent;
    color: var(--dm-text, #e0e0e0);
}

body.dark-mode .view-modal-status {
    color: var(--dm-text-muted, #b0b0b0);
}

/* O título da seção era o único do bloco .detail-* sem versão escura (strong e
   span já têm, mais acima): ficava em #0067b5 sobre #2d2d2d, ≈3:1, abaixo do
   mínimo para texto normal. Vale também para o modal de detalhes do cliente,
   que usa as mesmas classes. */
body.dark-mode .detail-section h3 {
    color: var(--primary-light, #3385c6);
    border-bottom-color: var(--primary-light, #3385c6);
}

/* ============================================================================
   LOGO DE CLIENTE / DISTRIBUIDOR
   ----------------------------------------------------------------------------
   Eram seis `style=` inline com TRÊS tamanhos diferentes para a mesma coisa:
   36×90 nas listas da Gestão, 66×66 em General Info e 100×200 nas fichas e
   formulários. Duas classes, dois tamanhos, e o logo passa a ocupar o mesmo
   espaço em toda tela — inclusive as que ainda vierem.

   `object-fit: contain` é o que preserva a proporção de logos que chegam em
   qualquer formato: sem ele, um logo largo e baixo esticaria dentro da caixa.
   ============================================================================ */

/* Miniatura em célula de tabela. */
.entity-logo-thumb {
    max-height: 32px;
    max-width: 80px;
    width: auto;
    height: auto;
    object-fit: contain;
    display: block;
    margin: 0 auto;
}

/* Ficha e formulário — a moldura separa o logo do fundo do card quando a
   imagem tem fundo branco ou transparente. */
.entity-logo {
    max-height: 72px;
    max-width: 160px;
    width: auto;
    height: auto;
    object-fit: contain;
    display: block;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    padding: 5px;
}

body.dark-mode .entity-logo {
    border-color: var(--dm-border, #505050);
}

/* No celular a coluna do card é estreita; o logo cede espaço primeiro. */
@media (max-width: 767.98px) {
    .entity-logo {
        max-height: 56px;
        max-width: 120px;
    }
}

/* ==========================================================================
   Preview de PowerPoint (customers.context › View)
   --------------------------------------------------------------------------
   O deck é montado no servidor e desenhado como HTML: cada slide é um
   .ppt-slide com os shapes posicionados em absoluto. Todas as medidas que o
   Python emite estão em `cqw` (percentual da largura do container), então o
   `container-type: inline-size` aqui é o que faz o slide inteiro — inclusive o
   corpo de letra — escalar junto com a largura disponível, sem JS.
   O slide é uma folha: mantém fundo claro mesmo no modo escuro.
   ========================================================================== */
.ppt-preview {
    display: flex;
    flex-direction: column;
    gap: 18px;
}

.ppt-slide {
    container-type: inline-size;
    position: relative;
    width: 100%;
    overflow: hidden;
    color: #000;
    background: #fff;
    border: 1px solid #dee2e6;
    border-radius: 4px;
    box-shadow: 0 1px 4px rgba(0, 0, 0, .12);
}

body.dark-mode .ppt-slide {
    border-color: var(--dm-border, #505050);
}

.ppt-shape {
    position: absolute;
    box-sizing: border-box;
}

/* O SVG desenha o custGeom já no sistema de coordenadas do shape; deixar o
   overflow visível evita que o traço do contorno seja cortado pela borda. */
.ppt-geom {
    position: absolute;
    inset: 0;
    width: 100%;
    height: 100%;
    overflow: visible;
}

/* Imagem do slide: entra por classe (uma regra por imagem, gerada no servidor)
   e não por src, para o mesmo PNG não se repetir slide a slide. PowerPoint
   estica a imagem na caixa do shape — daí o size 100% 100%. */
.ppt-pic {
    background-size: 100% 100%;
}

.ppt-shape[class*="ppt-img-"] {
    background-repeat: no-repeat;
}

/* Só carrega os <pattern> dos preenchimentos com imagem; nada a desenhar. */
.ppt-defs {
    position: absolute;
    width: 0;
    height: 0;
    overflow: hidden;
}

/* white-space explícito: as regras globais de tabela/página deixam `nowrap` no
   caminho, e aí o texto do slide era CORTADO em vez de quebrar como no
   PowerPoint. O `pre` que o servidor põe inline (bodyPr wrap="none") vence esta
   regra, então continua valendo onde o slide realmente não quebra linha. */
.ppt-text {
    overflow: hidden;
    white-space: normal;
}

.ppt-text p {
    margin: 0;
    line-height: 1.22;
    overflow-wrap: break-word;
}

.ppt-table {
    width: 100%;
    height: 100%;
    border-collapse: collapse;
    table-layout: fixed;
}

/* Célula de slide não é célula de tabela do sistema: sem herdar padding, cor
   nem borda das regras de .table. */
.ppt-table td {
    padding: 0.4cqw 0.6cqw;
    vertical-align: top;
    border: 0;
    background: none;
    color: #000;
    white-space: normal;
}

.ppt-table td p {
    margin: 0;
    line-height: 1.2;
    overflow-wrap: break-word;
}

/* Gráfico/OLE, que o preview não desenha: caixa neutra em vez de buraco. */
.ppt-unsupported {
    display: flex;
    align-items: center;
    justify-content: center;
    background: #f1f3f5;
    border: 1px dashed #adb5bd;
    color: #868e96;
    font-size: 2cqw;
}

/* Barra de navegação do preview (contador + miniaturas), grudada no topo do
   scroller — o do modal ou o da página, conforme onde a tela foi aberta. O fundo
   é opaco de propósito: os slides passam POR BAIXO dela. */
.ppt-preview-nav {
    position: sticky;
    top: 0;
    z-index: 3;
    background: #fff;
    padding-top: 4px;
    margin-bottom: 10px;
}

body.dark-mode .ppt-preview-nav {
    background: var(--dm-card, #2d2d2d);
}

.ppt-preview-bar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;
    margin-bottom: 14px;
}

.ppt-thumbs {
    display: flex;
    gap: 8px;
    overflow-x: auto;
    padding-bottom: 4px;
}

.ppt-thumb {
    flex: 0 0 auto;
    width: 84px;
    padding: 0;
    border: 1px solid #dee2e6;
    border-radius: 3px;
    background: #fff;
    color: #495057;
    font-size: 12px;
    line-height: 1.6;
    cursor: pointer;
}

.ppt-thumb:hover,
.ppt-thumb.active {
    border-color: var(--primary-color, #0067b5);
    color: var(--primary-color, #0067b5);
}

body.dark-mode .ppt-thumb {
    background: var(--dm-card, #2d2d2d);
    border-color: var(--dm-border, #505050);
    color: var(--dm-text, #e0e0e0);
}

/* ⚠️ A regra escura acima vem DEPOIS e tem mais especificidade que `.ppt-thumb.active`:
   sem esta, o botão do slide em foco fica indistinguível dos outros no dark mode.
   O azul claro é o mesmo de `.detail-section h3` — o primário puro não tem
   contraste suficiente sobre o fundo escuro do card. */
body.dark-mode .ppt-thumb:hover,
body.dark-mode .ppt-thumb.active {
    border-color: var(--primary-light, #3385c6);
    color: var(--primary-light, #3385c6);
}

/* ============================================================================
   Tools › Logo — preview do badge da WebGUI da ONT
   ============================================================================
   O palco imita o header do aparelho, que usa images/bg.jpg (CLARO): é contra
   esse fundo que o badge precisa ser lido, e é por isso que o palco NÃO segue o
   dark mode — trocá-lo por um fundo escuro mostraria uma composição que o
   usuário nunca vai ver na ONT, e arte branca sem fundo pareceria correta ali e
   sumiria no aparelho. Só a moldura acompanha o tema.

   O badge é sempre 150×35 (o slot do header). `image-rendering: pixelated` no
   zoom mantém honesto o que se está julgando: serrilhado que aparecer aqui é
   serrilhado que existe no PNG.                                              */
.ont-preview-stage {
    display: flex;
    align-items: center;
    justify-content: center;
    min-height: 90px;
    padding: 16px;
    background: #eef1f5;
    border: 1px solid var(--border-color, #dee2e6);
    border-radius: 6px;
}

body.dark-mode .ont-preview-stage {
    border-color: var(--dm-border, #505050);
}

.ont-preview-badge {
    width: 150px;
    height: 35px;
    /* Dobro do tamanho real: 150×35 é pequeno demais para julgar enquadramento
       numa tela de desktop. O tamanho REAL é o que vai no arquivo.            */
    transform: scale(2);
    transform-origin: center;
    image-rendering: pixelated;
}

.ont-preview-empty {
    color: var(--secondary-color, #6c757d);
    font-size: 13px;
}

/* Contadores do limite de 4096. Verde/vermelho é o sinal principal da tela:
   acima do limite a GUI da ONT ignora a logo em silêncio.                    */
.ont-stat {
    font-variant-numeric: tabular-nums;
    font-weight: 600;
}

.ont-stat-ok  { color: var(--success-color, #28a745); }
.ont-stat-bad { color: var(--danger-color, #dc3545); }
