@charset "UTF-8";
/**
 * theme.css - Identidade visual do Chromo-X (camada de tema do sistema).
 *
 * ORDEM DE CARREGAMENTO (ver views/layout/header.php) - é ela que decide quem
 * vence quem, então não troque a sequência:
 *   1. vendor/bootstrap/bootstrap.min.css  - Bootstrap 5.3.x
 *   2. css/sb-admin.css                    - layout do template SB Admin
 *   3. css/theme.css                       - ESTE arquivo, sempre por último
 *
 * Nada disso usa @layer, e não deve usar: os utilitários do Bootstrap
 * (.bg-dark, .text-*, .d-none...) são !important por natureza, e a cascata de
 * camadas INVERTE a ordem para declarações !important (camada mais cedo vence,
 * e código sem camada é o mais fraco de todos). Ou seja, com o Bootstrap numa
 * camada, nenhum override nosso de utilitário funcionaria; e com só o template
 * numa camada, o Bootstrap passa a vencer o template. Testado das duas formas.
 *
 * Na prática: aqui vence o template por vir depois; e onde é preciso vencer um
 * utilitário !important do Bootstrap, o !important é obrigatório - está
 * comentado caso a caso abaixo.
 *
 * Tudo que é cor, raio, sombra ou medida estrutural sai dos tokens da seção 1.
 * Para um reskin (ou um tema por cliente), mexa nos tokens, não nas regras.
 */

/* ==========================================================================
   1. TOKENS

   São TRÊS blocos, nesta ordem:
     1a. invariantes  - forma, medida e tipografia, iguais nos dois temas;
     1b. tema claro   - em `:root` e `[data-bs-theme="light"]`;
     1c. tema escuro  - em `[data-bs-theme="dark"]`.

   O atributo vai no <html>, escrito pelo PHP a cada página (ver
   App\Helpers\Theme e views/layout/header.php) - é o MESMO atributo que o
   Bootstrap 5.3 usa para repintar card, modal, dropdown, input e tabela, então
   trocá-lo vira tema completo, e não só a nossa camada.

   Os seletores `[data-bs-theme="..."]` têm a mesma especificidade de `:root`,
   e é a ORDEM (escuro depois de claro) que decide - igual ao próprio
   Bootstrap. Não use `:root[data-bs-theme=...]`: subir a especificidade aqui
   impediria "ilhas" de tema (um bloco claro dentro de uma página escura,
   marcando o atributo num elemento interno).

   REGRA AO CRIAR TOKEN DE COR NOVO: todo token definido em 1b precisa de par
   em 1c. Um token só no claro não "some" no escuro - ele continua valendo, e
   vira uma cor clara perdida no meio da tela escura.
   ========================================================================== */

/* --- 1a. Invariantes ----------------------------------------------------- */
:root {
    /* --- Forma ------------------------------------------------------------- */
    --cx-radius-sm: 0.5rem;
    --cx-radius: 0.75rem;
    --cx-radius-lg: 1rem;
    --cx-radius-card: 14px;
    --cx-radius-btn: 8px;
    --cx-radius-btn-sm: 7px;
    --cx-radius-badge: 6px;

    /* --- Estrutura ---------------------------------------------------------
       Consumido pelo sb-admin.css (que já nasce parametrizado). O padrão do
       template era 235px, que quebrava a linha do item "Horário de
       Funcionamento", o mais longo do menu. */
    --cx-sidebar-width: 268px;
    /* Altura da barra do topo - consumida pelo sb-admin.css (offset do conteúdo
       e do menu) e por âncoras que precisam descontar a barra fixa. */
    --cx-topbar-height: 64px;

    /* --- Tipografia --------------------------------------------------------
       Geist (variável, pesos 400-700), servida pelo próprio sistema - ver o
       @font-face logo abaixo deste bloco. A stack nativa do Bootstrap continua
       como reserva. Para trocar a fonte do sistema inteiro, basta apontar este
       token para a família nova e ajustar o @font-face. */
    --cx-font-sans: "Geist", system-ui, -apple-system, "Segoe UI", Roboto, "Helvetica Neue",
        "Noto Sans", "Liberation Sans", Arial, sans-serif, "Apple Color Emoji",
        "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji";
}

/* Geist - SIL Open Font License 1.1 (public/fonts/geist/OFL.txt). Arquivos
   woff2 do Google Fonts (subconjuntos latin e latin-ext, que cobrem todos os
   acentos do português), hospedados aqui: sem dependência de CDN e sem
   repassar o IP do lojista a terceiros. font-display: swap mostra o texto na
   fonte reserva até a Geist chegar, em vez de deixá-lo invisível. */
@font-face {
    font-family: "Geist";
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url("../fonts/geist/geist-latin-ext.woff2") format("woff2");
    unicode-range: U+0100-02BA, U+02BD-02C5, U+02C7-02CC, U+02CE-02D7, U+02DD-02FF, U+0304, U+0308, U+0329, U+1D00-1DBF, U+1E00-1E9F, U+1EF2-1EFF, U+2020, U+20A0-20AB, U+20AD-20C4, U+2113, U+2C60-2C7F, U+A720-A7FF;
}
@font-face {
    font-family: "Geist";
    font-style: normal;
    font-weight: 400 700;
    font-display: swap;
    src: url("../fonts/geist/geist-latin.woff2") format("woff2");
    unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+0304, U+0308, U+0329, U+2000-206F, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
}

/* --- 1b. TEMA CLARO -------------------------------------------------------
   Também é o tema de quem nunca escolheu nada (o `:root` aqui), e o que o
   servidor renderiza enquanto o modo "automático" não foi resolvido pelo
   navegador.
   ------------------------------------------------------------------------- */
:root,
[data-bs-theme="light"] {
    /* --- Marca -------------------------------------------------------------
       Derivado do laranja do "X" da logo (public/img/logo_chromo.svg, hoje
       #f66917). NÃO é a cor literal do X, e não deve virar: o acento pinta o
       fundo do .btn-primary (texto branco por cima), e o laranja da arte só
       alcança 3.0:1 sobre branco - abaixo do mínimo AA de 4.5:1. O que se
       preserva aqui é o matiz e a saturação da marca, com a luminosidade
       baixada até o contraste passar (este tom: 4.5:1 com branco por cima).
       Ao reexportar a logo com outro laranja, gire matiz/saturação destes
       tokens na mesma medida e mantenha a luminosidade de cada um - é ela que
       segura a hierarquia de contraste entre eles. */
    --cx-accent: #c2552a;
    --cx-accent-rgb: 194, 85, 42;
    --cx-accent-hover: #9e4422;
    --cx-accent-active: #883b1f;

    /* O acento usado como TEXTO (links, preços, botão outline) é um token
       separado do acento usado como FUNDO acima, e essa separação é o que
       permite o tema escuro: lá o fundo do botão continua precisando de um
       laranja escuro (para o texto branco por cima passar em contraste) ao
       mesmo tempo em que o link precisa de um laranja CLARO (para passar sobre
       a página escura). São requisitos opostos, impossíveis num token só.
       No tema claro os dois coincidem - por isso ninguém tinha notado. */
    --cx-accent-text: #c2552a;
    --cx-accent-text-rgb: 194, 85, 42;

    /* Trio de apoio do acento, usado pelas classes -subtle do Bootstrap 5.3
       (badges de status, destaques): fundo bem claro, borda média e um tom
       escuro o bastante para texto pequeno passar em contraste sobre o fundo.
       O -emphasis também é a cor de link sob o mouse. */
    --cx-accent-bg-subtle: #faeee8;
    --cx-accent-border-subtle: #e8c9b9;
    --cx-accent-emphasis: #692f17;
    --cx-accent-emphasis-rgb: 105, 47, 23;

    /* Roxo do status "Saiu para Entrega" (não é uma cor do Bootstrap; o
       identificador "delivery-purple" vem de OrderController::statusColumnsConfig()).
       Tem o mesmo par fundo/texto do acento, pelo mesmo motivo. */
    --cx-purple: #7a58b9;
    --cx-purple-text: #7a58b9;
    --cx-purple-hover: #6a4aa3;
    --cx-purple-active: #5c3f8f;
    --cx-purple-bg-subtle: #eee9f7;
    --cx-purple-border-subtle: #cec0e6;

    /* --- Superfícies e texto ---------------------------------------------- */
    --cx-bg: #f4f6fb;          /* fundo da página, atrás dos cards brancos */
    --cx-surface: #ffffff;     /* cards, modais, tabelas */
    --cx-border: #dee2e6;
    --cx-text: #212529;
    --cx-text-muted: #6c757d;

    /* Casca do sistema: barra do topo + menu lateral.
       No tema claro ela é BRANCA e a página é o cinza levíssimo acima - ou
       seja, a casca é mais clara que o conteúdo, e o que separa os dois é a
       --cx-shell-border. Antes a casca era escura nos dois casos, que é o
       "meio claro, meio escuro" que motivou este trabalho. */
    --cx-shell-bg: #ffffff;
    --cx-shell-bg-alt: #f4f6fb;     /* rodapé do menu lateral */
    --cx-shell-border: #e2e6ef;
    --cx-shell-text: #4a5160;       /* item de menu em repouso */
    --cx-shell-text-strong: #16181d; /* item sob o mouse, nome do usuário */
    --cx-shell-text-dim: #9aa1b1;   /* títulos de seção, ícones, cadeado de plano */
    --cx-shell-hover-bg: rgba(18, 20, 31, 0.05);
    --cx-shell-active-bg: rgba(var(--cx-accent-rgb), 0.12);
    /* Fundo do badge "loja aberta/fechada", que precisa se destacar da casca
       (na barra branca um badge branco desapareceria). */
    --cx-shell-badge-bg: #f4f6fb;

    /* --- Tabelas ----------------------------------------------------------- */
    --cx-table-head-bg: #f6f7fa;
    --cx-table-head-color: #6c757d;
    --cx-table-head-border: #e6e8ee;
    --cx-table-stripe-bg: #ffffff;      /* sem zebrado: igual à superfície */
    --cx-table-hover-bg: #f6f7fa;

    /* Superfície um degrau acima do card (pílula de quantidade, contadores). */
    --cx-surface-raised: #f1f3f5;

    /* Sombra é token de tema, e não invariante: no escuro uma sombra preta
       sobre fundo preto não existe - ver o par em 1c. */
    --cx-shadow-sm: 0 1px 2px rgba(15, 23, 42, 0.04), 0 6px 20px rgba(15, 23, 42, 0.06);
}

/* --- 1c. TEMA ESCURO ------------------------------------------------------
   Hierarquia de profundidade (do fundo para a frente): página #101019 ->
   casca #14141f -> card #1c1c2a -> cabeçalho de tabela #26263a. A casca
   mantém exatamente o tom que a barra e o menu já tinham antes deste trabalho.
   ------------------------------------------------------------------------- */
[data-bs-theme="dark"] {
    /* Fundo de botão: o MESMO laranja do tema claro, de propósito. É a cor da
       marca e é a única luminosidade da família que ainda passa em contraste
       com o texto branco por cima (4.5:1); clareá-la para "combinar melhor"
       com o fundo escuro reprovaria o botão primário inteiro. */
    --cx-accent: #c2552a;
    --cx-accent-rgb: 194, 85, 42;
    --cx-accent-hover: #d3612f;   /* invertido: no escuro o hover CLAREIA */
    --cx-accent-active: #e06b36;

    /* Já o acento como texto precisa ir na direção oposta: este tom dá 6.5:1
       sobre o card escuro, enquanto o #c2552a acima daria 1.4:1 (ilegível). */
    --cx-accent-text: #f0844a;
    --cx-accent-text-rgb: 240, 132, 74;

    --cx-accent-bg-subtle: #35201a;
    --cx-accent-border-subtle: #6b3a24;
    --cx-accent-emphasis: #f6a77c;
    --cx-accent-emphasis-rgb: 246, 167, 124;

    --cx-purple: #7a58b9;         /* fundo de botão, texto branco por cima */
    --cx-purple-text: #a888e0;    /* 5.8:1 sobre o card escuro */
    --cx-purple-hover: #8b68cc;
    --cx-purple-active: #9a78d8;
    --cx-purple-bg-subtle: #272040;
    --cx-purple-border-subtle: #4b3a70;

    --cx-bg: #101019;
    --cx-surface: #1c1c2a;
    --cx-border: #32324a;
    --cx-text: #e6e8ee;
    --cx-text-muted: #a0a6b5;     /* 6.9:1 sobre o card */

    --cx-shell-bg: #14141f;
    --cx-shell-bg-alt: #1c1c2b;
    --cx-shell-border: #262637;
    --cx-shell-text: rgba(255, 255, 255, 0.62);
    --cx-shell-text-strong: #ffffff;
    --cx-shell-text-dim: rgba(255, 255, 255, 0.38);
    --cx-shell-hover-bg: rgba(255, 255, 255, 0.06);
    --cx-shell-active-bg: rgba(var(--cx-accent-rgb), 0.22);
    --cx-shell-badge-bg: #1c1c2b;

    --cx-table-head-bg: #222232;
    --cx-table-head-color: #a0a6b5;
    --cx-table-head-border: #32324a;
    --cx-table-stripe-bg: #1c1c2a;      /* sem zebrado: igual à superfície */
    --cx-table-hover-bg: #252536;

    --cx-surface-raised: #26263a;

    /* Sombra preta some no escuro: o que dá relevo aqui é o preto mais opaco
       somado à borda do card (ver seção 4). */
    --cx-shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.4), 0 6px 20px rgba(0, 0, 0, 0.45);
}

/* ==========================================================================
   2. LIGAÇÃO DOS TOKENS COM O BOOTSTRAP
   Os componentes do Bootstrap leem estas variáveis internamente, então trocar
   aqui já repinta badge, link, foco e afins no sistema todo. O que NÃO passa
   por variável (botões, checkbox) vem compilado com a cor fixa e precisa de
   override próprio - seção 5.
   ========================================================================== */
:root {
    --bs-primary: var(--cx-accent);
    --bs-primary-rgb: var(--cx-accent-rgb);
    --bs-primary-bg-subtle: var(--cx-accent-bg-subtle);
    --bs-primary-border-subtle: var(--cx-accent-border-subtle);
    --bs-primary-text-emphasis: var(--cx-accent-emphasis);

    /* Link é texto, então sai de --cx-accent-text (e não de --cx-accent): no
       tema escuro os dois são cores diferentes - ver a seção 1b.
       As variantes -rgb não são enfeite nem redundância: no Bootstrap 5.3 o
       seletor `a` é `color: rgba(var(--bs-link-color-rgb), ...)` (para dar
       conta do utilitário .link-opacity-*), e o :hover troca justamente
       --bs-link-color-rgb por --bs-link-hover-color-rgb. Sem as duas, todo
       link do sistema volta ao azul do framework - as versões sem -rgb
       sozinhas não pintam nada. */
    --bs-link-color: var(--cx-accent-text);
    --bs-link-color-rgb: var(--cx-accent-text-rgb);
    --bs-link-hover-color: var(--cx-accent-emphasis);
    --bs-link-hover-color-rgb: var(--cx-accent-emphasis-rgb);

    --bs-body-font-family: var(--cx-font-sans);

    --bs-border-radius: var(--cx-radius);
    --bs-border-radius-sm: var(--cx-radius-sm);
    --bs-border-radius-lg: var(--cx-radius-lg);

    /* Superfícies e texto dos componentes do Bootstrap 5.3 (card, modal,
       dropdown, input, list-group, paginação) - todos leem --bs-body-bg.
       É por isso que o fundo CINZA da página não pode entrar aqui: ele iria
       junto para todas essas superfícies (ver o comentário do `body` abaixo).
       No tema claro estes três valores já eram os do Bootstrap; declará-los
       importa para o escuro, onde o padrão do framework (#212529) não bate
       com a nossa paleta. */
    --bs-body-bg: var(--cx-surface);
    --bs-body-color: var(--cx-text);
    --bs-border-color: var(--cx-border);
    /* .text-muted e afins (403 usos nas views) leem esta variável - é o que
       faz o texto de apoio acompanhar o tema sem tocar em nenhuma view. */
    --bs-secondary-color: var(--cx-text-muted);
}


/* Fundo levemente acinzentado atrás dos cards brancos, para dar profundidade.
   Aplicado direto no body, e NÃO via --bs-body-bg: no Bootstrap 5.3 essa
   variável também é a cor de fundo de card, modal, dropdown, input e afins
   (no 5.2 esses componentes tinham o branco compilado) - trocá-la deixaria
   todas as superfícies do sistema acinzentadas junto com a página. */
body {
    background-color: var(--cx-bg);
}

/* ==========================================================================
   3. LAYOUT (barra do topo, menu lateral, rodapé)

   A marcação do HTML continua dizendo `.sb-sidenav-dark` / `.navbar-dark` nos
   DOIS temas, e é de propósito: são só os nomes das classes do template SB
   Admin, e trocá-las por `.sb-sidenav-light` no tema claro exigiria PHP
   condicional espalhado pelo header e faria o tema depender de duas fontes de
   verdade (a classe no HTML e o data-bs-theme no <html>). Em vez disso, cada
   cor fixa que o template pendurou nessas classes é neutralizada aqui por um
   token --cx-shell-*, e quem decide tudo é o atributo no <html>.

   Por isso este bloco repete seletores que "parecem" já existir no
   sb-admin.css: lá eles são rgba(255,255,255,…) cravados, pensados só para
   casca escura.
   ========================================================================== */

/* .bg-dark é utilitário do Bootstrap (!important) e está no HTML da <nav> do
   topo - daí o !important aqui, senão a barra fica num tom diferente do menu. */
.sb-topnav.navbar-dark.bg-dark,
.sb-sidenav-dark {
    background-color: var(--cx-shell-bg) !important;
    color: var(--cx-shell-text);
}

/* Fio que separa a casca do conteúdo. No tema escuro ele quase não aparece
   (as duas superfícies já são escuras); no claro é ele que impede a barra
   branca de se fundir com o card branco logo abaixo. */
.sb-topnav.navbar-dark.bg-dark {
    border-bottom: 1px solid var(--cx-shell-border);
}

#layoutSidenav_nav .sb-sidenav-dark {
    border-right: 1px solid var(--cx-shell-border);
}

.sb-sidenav-dark .sb-sidenav-footer {
    background-color: var(--cx-shell-bg-alt);
    color: var(--cx-shell-text);
}

/* Títulos de seção, ícones e setas: o template crava rgba(255,255,255,0.25),
   invisível sobre barra branca. */
.sb-sidenav-dark .sb-sidenav-menu .sb-sidenav-menu-heading,
.sb-sidenav-dark .sb-sidenav-menu .nav-link .sb-nav-link-icon,
.sb-sidenav-dark .sb-sidenav-menu .nav-link .sb-sidenav-collapse-arrow {
    color: var(--cx-shell-text-dim);
}

.sb-sidenav-dark .sb-sidenav-menu .nav-link {
    color: var(--cx-shell-text);
}

/* O `.nav` no meio do seletor não é enfeite: sem ele esta regra tem uma classe
   a menos que a do template (`.sb-sidenav .sb-sidenav-menu .nav .nav-link`) e
   perde o padding vertical para ela, deixando os itens mais altos que o
   previsto no tema. */
.sb-sidenav-dark .sb-sidenav-menu .nav .nav-link {
    border-radius: 10px;
    margin: 1px 0.9rem;
    padding: 0.55rem 0.75rem;
    font-size: 0.9375rem;
    font-weight: 500;
    transition: background-color 0.15s ease, color 0.15s ease;
}

/* Ícone numa largura fixa: os do Font Awesome têm larguras diferentes, e sem
   isso o texto de cada item começava numa coluna diferente. */
.sb-sidenav-dark .sb-sidenav-menu .nav .nav-link .sb-nav-link-icon {
    width: 1.25rem;
    margin-right: 0.75rem;
    font-size: 1.05rem;
    display: inline-flex;
    justify-content: center;
    flex: none;
}

/* Título de seção ("PEDIDOS", "CADASTROS"): menor e com menos respiro que o
   do template (1.75rem em cima) - o menu ficava "esticado". O recuo à
   esquerda (0.9rem do item + 0.75rem do padding) alinha com o ícone. */
.sb-sidenav .sb-sidenav-menu .nav .sb-sidenav-menu-heading {
    padding: 0.85rem 1.65rem 0.35rem;
    font-size: 0.68rem;
    letter-spacing: 0.09em;
    font-weight: 600;
}
.sb-sidenav .sb-sidenav-menu .nav .sb-sidenav-menu-heading:first-child {
    padding-top: 1.1rem;
}

.sb-sidenav-dark .sb-sidenav-menu .nav .nav-link:hover {
    background-color: var(--cx-shell-hover-bg);
    color: var(--cx-shell-text-strong);
}

/* Item do menu correspondente à tela aberta (classe aplicada em header.php). */
.sb-sidenav-dark .nav .nav-link.menu-ativo {
    color: var(--cx-shell-text-strong);
    font-weight: 600;
    background-color: var(--cx-shell-active-bg);
}

/* Cadeado dos itens bloqueados pelo plano (style inline nas views chama
   rgba(255,255,255,.5), que some na casca clara - daí o !important). */
.sb-sidenav-dark .nav .nav-link .fa-lock {
    color: var(--cx-shell-text-dim) !important;
}

/* --- Barra do topo ------------------------------------------------------- */

/* .navbar-dark deixa o texto claro cravado; no tema claro isso precisa
   acompanhar a casca. Vale para o nome do usuário (que vem com .text-white no
   HTML, daí o !important) e para o botão do menu sanduíche. */
.sb-topnav.navbar-dark .navbar-nav .nav-link,
.sb-topnav.navbar-dark .navbar-nav .nav-link.text-white {
    color: var(--cx-shell-text) !important;
}

.sb-topnav.navbar-dark .navbar-nav .nav-link:hover,
.sb-topnav.navbar-dark .navbar-nav .nav-link.text-white:hover,
.sb-topnav.navbar-dark .navbar-nav .show > .nav-link {
    color: var(--cx-shell-text-strong) !important;
}

.sb-topnav.navbar-dark #sidebarToggle {
    color: var(--cx-shell-text);
}

.sb-topnav.navbar-dark #sidebarToggle:hover {
    color: var(--cx-shell-text-strong);
}

/* Botões de ícone da barra (☰, sino, usuário): área de toque de 38px com
   fundo no hover, como no restante dos botões de ícone do sistema. */
.sb-topnav #sidebarToggle,
.sb-topnav #navbar-order-alerts-toggle {
    width: 38px;
    height: 38px;
    padding: 0 !important;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border-radius: 10px;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.sb-topnav #navbarDropdown {
    height: 38px;
    display: inline-flex;
    align-items: center;
    gap: 0.35rem;
    padding: 0 0.6rem !important;
    border-radius: 10px;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.sb-topnav #sidebarToggle:hover,
.sb-topnav #navbar-order-alerts-toggle:hover,
.sb-topnav #navbarDropdown:hover,
.sb-topnav .show > #navbarDropdown,
.sb-topnav .show > #navbar-order-alerts-toggle {
    background-color: var(--cx-shell-hover-bg);
}

/* Seletor de tema, dentro do menu do usuário: a opção vigente é o botão
   .active do grupo, na cor da marca em vez do cinza padrão do
   .btn-outline-secondary. Precisa da especificidade do #theme-switcher para
   vencer a regra .btn-check:checked+.btn,.btn.active do próprio Bootstrap. */
#theme-switcher .js-theme-option.active {
    background-color: var(--cx-accent);
    border-color: var(--cx-accent);
    color: #fff;
}

/* Badge "loja aberta/fechada": vem com .bg-white .text-dark no HTML, o que na
   barra branca do tema claro deixa um retângulo branco invisível sobre branco.
   Passa a usar a superfície de contraste da casca nos dois temas. */
.sb-topnav #navbar-store-status-badge {
    background-color: var(--cx-shell-badge-bg) !important;
    color: var(--cx-shell-text-strong) !important;
    border-color: var(--cx-shell-border) !important;
}

.sb-sidenav-dark .nav .nav-link.menu-ativo .sb-nav-link-icon i,
.sb-sidenav-dark .nav .nav-link.menu-ativo .sb-nav-link-icon svg {
    color: var(--cx-accent-text);
}


/* O ícone de usuário (sem fundo colorido) parecia menor que o badge de loja
   aberta/fechada ao lado. O kit do Font Awesome troca o <i> por <svg> em
   runtime e renomeou o ícone na v6, por isso as duas classes. */
#navbarDropdown .fa-user-circle,
#navbarDropdown .fa-circle-user {
    font-size: 1.4rem;
    vertical-align: middle;
}

/* ==========================================================================
   4. CARDS E SUPERFÍCIES
   ========================================================================== */
.card {
    border-radius: var(--cx-radius-card);
    /* O Bootstrap arredonda header/footer a partir do raio ORIGINAL do card,
       não do valor sobrescrito acima - sem estas duas o header do card fica
       com a quina reta "vazando" por dentro da quina curva. */
    --bs-card-border-radius: var(--cx-radius-card);
    --bs-card-inner-border-radius: calc(var(--cx-radius-card) - 1px);
}

/* Card com borda visível (ex.: grupo de opcionais em Produtos): iguala a cor
   da borda interna do header à da borda externa (.border), que por padrão são
   ligeiramente diferentes e deixavam uma emenda visível no canto. */
.card.border {
    --bs-card-border-color: var(--cx-border);
}

.shadow-sm {
    /* .shadow-sm é utilitário do Bootstrap (!important). */
    box-shadow: var(--cx-shadow-sm) !important;
}

/* ==========================================================================
   5. CONTROLES (botões, badges, formulários, paginação)
   O Bootstrap compila a cor "primary" desses componentes como valor fixo em
   vez de reaproveitar var(--bs-primary), por isso a troca precisa ser repetida
   aqui - agora ao menos vinda dos tokens.
   ========================================================================== */
.btn {
    border-radius: var(--cx-radius-btn);
}

.btn-sm {
    border-radius: var(--cx-radius-btn-sm);
}

.btn-primary {
    --bs-btn-bg: var(--cx-accent);
    --bs-btn-border-color: var(--cx-accent);
    --bs-btn-hover-bg: var(--cx-accent-hover);
    --bs-btn-hover-border-color: var(--cx-accent-hover);
    --bs-btn-active-bg: var(--cx-accent-hover);
    --bs-btn-active-border-color: var(--cx-accent-active);
    --bs-btn-disabled-bg: var(--cx-accent);
    --bs-btn-disabled-border-color: var(--cx-accent);
    --bs-btn-focus-shadow-rgb: var(--cx-accent-rgb);
}

.btn-outline-primary {
    /* Texto sobre o fundo da página (o botão só ganha fundo no hover), então
       vem de --cx-accent-text - ver seção 1b. */
    --bs-btn-color: var(--cx-accent-text);
    --bs-btn-border-color: var(--cx-accent);
    --bs-btn-hover-bg: var(--cx-accent);
    --bs-btn-hover-border-color: var(--cx-accent);
    --bs-btn-active-bg: var(--cx-accent);
    --bs-btn-active-border-color: var(--cx-accent);
    --bs-btn-disabled-color: var(--cx-accent-text);
    --bs-btn-disabled-border-color: var(--cx-accent);
    --bs-btn-focus-shadow-rgb: var(--cx-accent-rgb);
}

.badge {
    border-radius: var(--cx-radius-badge);
    font-weight: 600;
}

/* Status "Saiu para Entrega": cor fora da paleta do Bootstrap, montada aqui no
   mesmo formato das nativas (texto + fundo/borda suaves) para os badges de
   status ficarem todos com o mesmo peso visual nas listagens. */
.text-delivery-purple {
    /* --cx-purple-text, e não --cx-purple: o primeiro é a versão legível sobre
       o fundo do tema, o segundo é o fundo do botão roxo. */
    color: var(--cx-purple-text) !important; /* espelha o .text-* do Bootstrap, que é !important */
}

.bg-delivery-purple-subtle {
    background-color: var(--cx-purple-bg-subtle) !important;
}

.border-delivery-purple-subtle {
    border-color: var(--cx-purple-border-subtle) !important;
}

.btn-delivery-purple {
    --bs-btn-color: #fff;
    --bs-btn-bg: var(--cx-purple);
    --bs-btn-border-color: var(--cx-purple);
    --bs-btn-hover-color: #fff;
    --bs-btn-hover-bg: var(--cx-purple-hover);
    --bs-btn-hover-border-color: var(--cx-purple-hover);
    --bs-btn-active-color: #fff;
    --bs-btn-active-bg: var(--cx-purple-active);
    --bs-btn-active-border-color: var(--cx-purple-active);
    --bs-btn-disabled-color: #fff;
    --bs-btn-disabled-bg: var(--cx-purple);
    --bs-btn-disabled-border-color: var(--cx-purple);
    --bs-btn-focus-shadow-rgb: 122, 88, 185;
}

/* Checkboxes, radios e switches. */
.form-check-input:checked,
.form-check-input[type="checkbox"]:indeterminate {
    background-color: var(--cx-accent);
    border-color: var(--cx-accent);
}

.form-check-input:focus {
    border-color: var(--cx-accent);
    box-shadow: 0 0 0 0.25rem rgba(var(--cx-accent-rgb), 0.25);
}

/* Bolinha do switch em foco - o Bootstrap 5.3 expõe a imagem como variável,
   então basta trocar a cor do SVG. O :not(:checked) continua obrigatório
   (mesmo no 5.3): a regra :checked do Bootstrap (bolinha branca) e esta aqui
   têm a mesma especificidade, e como o theme.css carrega depois do Bootstrap
   ela sempre vence - sem o :not(:checked), ligar o switch enquanto ele
   mantém o foco (o caso comum, logo após o clique) pintava a bolinha de
   laranja sobre o fundo já laranja do estado ligado, fazendo-a sumir. */
.form-switch .form-check-input:focus:not(:checked) {
    --bs-form-switch-bg: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='-4 -4 8 8'%3e%3ccircle r='3' fill='%23c2552a'/%3e%3c/svg%3e");
}

.form-control:focus,
.form-select:focus {
    border-color: var(--cx-accent-border-subtle);
    box-shadow: 0 0 0 0.25rem rgba(var(--cx-accent-rgb), 0.2);
}

/* Campos preenchidos pelo navegador (e-mail/senha salvos).
   O Chrome pinta o fundo do autofill por conta própria, e como esse fundo é
   decidido pelo `color-scheme` da página, no tema escuro ele vira um
   amarelo-oliva que não combina com nada. Não adianta declarar
   `background-color`: a cor do autofill é aplicada num nível que o CSS da
   página não sobrescreve.
   O caminho que funciona é preencher o campo inteiro com uma SOMBRA INTERNA
   opaca (que o navegador respeita) e trocar a cor do texto por
   -webkit-text-fill-color, já que `color` também é ignorado nesse estado.
   A transição de 9999s é o truque conhecido para o Chrome não reanimar a cor
   dele por cima logo depois de pintar.
   Vale nos dois temas, saindo dos tokens: no claro o campo fica branco como os
   demais, em vez do azul-claro que o Chrome usa. */
input:-webkit-autofill,
input:-webkit-autofill:hover,
input:-webkit-autofill:focus,
input:-webkit-autofill:active,
textarea:-webkit-autofill,
select:-webkit-autofill {
    -webkit-box-shadow: 0 0 0 1000px var(--cx-surface) inset !important;
    box-shadow: 0 0 0 1000px var(--cx-surface) inset !important;
    -webkit-text-fill-color: var(--cx-text) !important;
    caret-color: var(--cx-text);
    transition: background-color 9999s ease-in-out 0s;
}

/* Paginação das listagens (DataTables usa a paginação do Bootstrap). */
.pagination {
    --bs-pagination-active-bg: var(--cx-accent);
    --bs-pagination-active-border-color: var(--cx-accent);
    --bs-pagination-focus-box-shadow: 0 0 0 0.25rem rgba(var(--cx-accent-rgb), 0.25);
    margin-top: 10px;
}

/* ==========================================================================
   6. TABELAS / DATATABLES
   ========================================================================== */

/* As tabelas ocupam sempre a largura do container. */
table.dataTable,
.dataTables_wrapper table.dataTable {
    width: 100% !important;   /* o DataTables grava a largura calculada no atributo style */
    max-width: 100% !important;
    margin: 0 auto !important;
    border-collapse: collapse !important; /* vence o border-collapse:separate !important do CSS do DataTables */
}

.table-responsive {
    width: 100%;
    overflow-x: auto;
    -webkit-overflow-scrolling: touch;
}

/* Cabeçalho: pintado pelas variáveis do próprio Bootstrap, em vez de
   background-color forçado - o thead das views vem com .table-light, que só
   define essas mesmas variáveis, e esta regra é mais específica. */
table.dataTable > thead,
.table > thead.table-light {
    --bs-table-bg: var(--cx-table-head-bg);
    --bs-table-color: var(--cx-table-head-color);
}

table.dataTable > thead > tr > th,
.table > thead.table-light > tr > th {
    font-weight: 600;
    border-bottom: 1px solid var(--cx-table-head-border);
}

/* Zebra e hover: o CSS do DataTables pinta as linhas com box-shadow inset
   próprio (rgba fixo) e anula o zebrado nativo do Bootstrap, então o jeito de
   clarear a faixa é pelo mesmo mecanismo. Sem !important: os seletores abaixo
   terminam em td/th (mais específicos que o `> *` do DataTables). */
table.dataTable.table-striped > tbody > tr.odd > td,
table.dataTable.table-striped > tbody > tr.odd > th,
table.dataTable.table-striped > tbody > tr:nth-of-type(odd) > td,
table.dataTable.table-striped > tbody > tr:nth-of-type(odd) > th {
    background-color: var(--cx-table-stripe-bg);
    box-shadow: inset 0 0 0 9999px var(--cx-table-stripe-bg);
}

table.dataTable.table-striped > tbody > tr.even > td,
table.dataTable.table-striped > tbody > tr.even > th,
table.dataTable.table-striped > tbody > tr:nth-of-type(even) > td,
table.dataTable.table-striped > tbody > tr:nth-of-type(even) > th {
    background-color: var(--cx-surface);
    box-shadow: inset 0 0 0 9999px var(--cx-surface);
}

table.dataTable.table-hover > tbody > tr:hover > td,
table.dataTable.table-hover > tbody > tr:hover > th {
    background-color: var(--cx-table-hover-bg);
    box-shadow: inset 0 0 0 9999px var(--cx-table-hover-bg);
}

/* Coluna do checkbox de seleção em massa, estreita e centralizada.
   Antes isto valia para a primeira coluna de QUALQUER DataTable, e as tabelas
   sem checkbox precisavam ser listadas uma a uma numa lista de exclusão aqui
   dentro - quem criasse uma listagem nova só descobria o problema vendo a
   primeira coluna esmagada. Agora a regra se aplica sozinha, e só onde existe
   checkbox no corpo da tabela. */
table.dataTable:has(tbody .form-check-input) > thead > tr > th:nth-child(1),
table.dataTable:has(tbody .form-check-input) > tbody > tr > td:nth-child(1) {
    width: 38px !important;   /* o DataTables grava width inline nas colunas */
    min-width: 38px !important;
    max-width: 38px !important;
    padding: 10px 0 !important; /* vence o padding utilitário aplicado nas células */
    text-align: center !important;
    vertical-align: middle !important;
}

table.dataTable .form-check-input {
    margin: 0 auto;
    display: block;
    float: none;
}

/* Coluna com a largura exata do conteúdo. */
.table-fit {
    width: 1% !important;
    white-space: nowrap !important;
}

/* Rótulo do cabeçalho alinhado à ESQUERDA nas colunas centralizadas e
   estreitas de status/tipo (table-fit + text-center: Status, Tipo, NFC-e...)
   - antes ficava centralizado, herdado do .text-center do Bootstrap. O
   ícone de ordenação (▲▼) continua à DIREITA do rótulo, sem override: é o
   padrão do dataTables.bootstrap5.css usado no resto do sistema, só o texto
   muda de alinhamento. text-align precisa de !important pra vencer o
   .text-center do Bootstrap (que também usa !important) - a especificidade
   extra das 3 classes já decide o empate a nosso favor. */
table.dataTable > thead > tr > th.table-fit.text-center.sorting,
table.dataTable > thead > tr > th.table-fit.text-center.sorting_asc,
table.dataTable > thead > tr > th.table-fit.text-center.sorting_desc {
    text-align: left !important;
}

.dataTables_wrapper label {
    margin-bottom: 10px;
}

/* ==========================================================================
   7. PAINÉIS DE STATUS (Preparação e Delivery)
   ========================================================================== */

/* Modo tela cheia: esconde menu, topo e rodapé para aproveitar a tela inteira
   em monitores de cozinha/balcão. Ver public/js/status-fullscreen.js. */
body.status-maximized .sb-topnav,
body.status-maximized #layoutSidenav_nav,
body.status-maximized footer {
    display: none !important; /* vence o display utilitário das próprias barras */
}

/* #layoutSidenav como parte do seletor (não só body.status-maximized) para
   igualar a especificidade de ".sb-nav-fixed #layoutSidenav #layoutSidenav_content"
   (2 IDs) - sem isso essa regra sempre perdia pra base, que reserva
   padding-left/top pro menu mesmo com ele escondido, deixando uma faixa de
   espaço morto à esquerda/em cima no modo maximizado. */
body.status-maximized #layoutSidenav #layoutSidenav_content {
    top: 0;
    min-height: 100vh;
}

/* O padding-left compensa a largura do menu só no desktop (onde ele fica
   sempre reservado no layout). No celular o menu já está fora da tela por
   transform + margin-left negativa, e essa mesma margem negativa soma com o
   padding-left para alinhar o conteúdo à borda esquerda - zerar o padding ali
   quebra a conta e empurra o conteúdo para fora da tela. */
@media (min-width: 992px) {
    body.status-maximized #layoutSidenav #layoutSidenav_content {
        padding-left: 0;
    }
}

body.status-maximized #prep-status-board .card-body,
body.status-maximized #status-board .card-body {
    max-height: calc(100vh - 210px) !important; /* vence o max-height inline do board */
}

/* Colunas do Kanban sempre lado a lado, com rolagem horizontal em vez de
   empilhar: a sequência Novo -> Em Preparo -> Pronto precisa ser lida da
   esquerda para a direita, inclusive no celular. */
.status-board-row {
    display: flex;
    flex-wrap: nowrap;
    gap: 1rem;
    overflow-x: auto;
    padding-bottom: 0.5rem;
}

.status-board-col {
    flex: 1 1 240px;
    min-width: 240px;
}

/* ==========================================================================
   8. SELETOR DE QUANTIDADE EM PILL (-/+)
   Mesmo modelo visual do cardápio digital (public/css/menu.css
   .menu-modal-qty-pill), reaproveitado nas telas de adicionar produto ao
   pedido (Mesas e Pedidos) para manter o padrão de interação em todo o sistema.
   ========================================================================== */
.qty-pill {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    background: var(--cx-surface-raised);
    border-radius: 999px;
    padding: 0.35rem 0.7rem;
    flex: 0 0 auto;
}

.qty-pill-btn {
    background: none;
    border: none;
    padding: 0;
    color: var(--cx-text);
    font-size: 0.8rem;
    display: flex;
    align-items: center;
    justify-content: center;
}

.qty-pill input {
    background: transparent;
    border: none;
    padding: 0;
    width: 22px;
    text-align: center;
    font-weight: 700;
    font-size: 0.9rem;
    color: var(--cx-text);
}

/* Variante compacta, para o seletor de quantidade de cada complemento
   (allow_quantity='S') - cabe ao lado do checkbox. */
.qty-pill-sm {
    gap: 0.4rem;
    padding: 0.2rem 0.5rem;
}

.qty-pill-sm .qty-pill-btn {
    font-size: 0.7rem;
    width: 18px;
    height: 18px;
}

.qty-pill-sm input {
    width: 16px;
    font-size: 0.8rem;
}

/* ==========================================================================
   8b. FOTO DO ITEM ADICIONAL (formulário de produto)
   Quadradinho clicável na linha de cada adicional: sem foto mostra o ícone de
   câmera, com foto vira a miniatura. O clique abre o modal único de galeria
   (escolher entre as imagens já cadastradas no sistema ou enviar do
   computador) - ver public/js/product-form.js::setupImageGalleryModal(). Não
   é mais um <label> nativo envolvendo o <input type="file">: o input só é
   disparado de dentro do modal, na opção "Enviar do computador".
   ========================================================================== */
/* Foto do adicional sempre QUADRADA (pedido do usuário, 2026-10-08): antes
   o wrapper tinha largura fixa de 64px e a altura vinha do stretch do flex -
   com mais linhas de campo ao lado (preço do sistema + preço do iFood) a foto
   virava um retângulo alto e estreito. align-self:flex-start tira o stretch;
   aspect-ratio mantém altura = largura. */
.complement-photo-wrap {
    width: 104px;
    aspect-ratio: 1 / 1;
    align-self: flex-start;
}

/* Caixa de preço por canal dentro do card do adicional (sistema / iFood):
   rótulo + "R$ [cheio]" + "R$ [promo]" numa linha, quebrando se o card
   ficar estreito (grade de 240px no tablet, ver .complement-grid). */
.complement-price-box {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.25rem 0.4rem;
    padding: 0.25rem 0.5rem;
    border: 1px solid var(--bs-border-color);
    border-radius: var(--bs-border-radius);
    background-color: var(--bs-body-bg);
}

.complement-price-box-label {
    font-size: 0.8rem;
    font-weight: 700;
    min-width: 2.6rem;
}

.complement-price-box-currency {
    font-size: 0.8rem;
    color: var(--bs-secondary-color);
}

.complement-price-box .form-control {
    flex: 1 1 0;
    min-width: 0;
}

/* Card estreito (grade de 240px no tablet/celular): a medida é a do PRÓPRIO
   card (container query), não a da tela - no desktop largo o card também
   pode ficar estreito se couberem muitas colunas. Foto menor, nome numa
   linha só dele e sem o "R$" repetido, pra os dois preços caberem lado a
   lado. */
.complement-item-row {
    container-type: inline-size;
}

@container (max-width: 440px) {
    .complement-photo-wrap {
        width: 64px;
    }

    .complement-item-head {
        flex-wrap: wrap;
    }

    .complement-item-head .item-name-input {
        flex-basis: 100% !important;
    }

    .complement-price-box {
        flex-wrap: nowrap;
    }

    .complement-price-box-currency {
        display: none;
    }
}

/* Celular, formulário de produto: margens em camadas (card do formulário,
   card do grupo de adicionais, moldura da grade) comiam ~100px dos 390px da
   tela e espremiam os campos de preço dos adicionais (achado do usuário,
   2026-10-08). Só nesta tela - o padding da página em si continua igual. */
@media (max-width: 575.98px) {
    .card-body:has(> #superProductForm) {
        padding: 0.75rem !important;
    }

    #complements-pane .complement-group-card > .card-body {
        padding: 0.5rem !important;
    }

    #complements-pane .items-list-container {
        margin: -0.25rem;
    }
}

/* Card de celular: as caixas de preço ao lado da foto ficavam com campos
   minúsculos (achado do usuário, 2026-10-08). Aqui elas descem pra largura
   inteira do card, abaixo de foto + nome: o corpo vira grid e a coluna de
   campos some da árvore de layout (display: contents) pra seus filhos
   ocuparem as células do grid. !important porque d-flex do Bootstrap também
   usa !important. */
@container (max-width: 360px) {
    .complement-item-body {
        display: grid !important;
        grid-template-columns: auto 64px minmax(0, 1fr);
        column-gap: 0.5rem;
        row-gap: 0.35rem;
        padding: 0.4rem !important;
    }

    .complement-item-fields {
        display: contents !important;
    }

    .complement-item-body > .item-drag-handle {
        grid-column: 1;
        grid-row: 1 / span 3;
        align-self: center;
    }

    .complement-item-body > .complement-photo-wrap {
        grid-column: 2;
        grid-row: 1;
    }

    .complement-item-head {
        grid-column: 3;
        grid-row: 1;
        align-self: center;
    }

    .complement-price-box {
        grid-column: 2 / 4;
        padding: 0.25rem 0.4rem;
    }

    /* Na largura inteira cabe o "R$" de novo. */
    .complement-price-box-currency {
        display: inline;
    }
}

.complement-photo-slot {
    /* Preenche o wrapper quadrado (.complement-photo-wrap, logo acima). */
    width: 100%;
    height: 100%;
    cursor: pointer;
    overflow: hidden;
    flex-shrink: 0;
    transition: border-color 0.15s ease, color 0.15s ease;
}

.complement-photo-slot:hover {
    border-color: var(--cx-accent) !important;
    color: var(--cx-accent-text);
}

/* "Categorias Vinculadas" (views/products/form.php, aba Geral): no máximo
   --category-rows (até 7, calculado no PHP) itens por coluna; passou disso,
   o grid abre uma nova coluna ao lado. As colunas dividem a largura por
   igual, com mínimo de 120px - se não couberem, a caixa rola na horizontal.
   padding-top de 2rem deixa o 1º item à mesma distância do topo que o 1º
   switch de "Configurações do Produto" (p-3 + gutter g-3). */
.product-categories-grid {
    display: grid;
    grid-auto-flow: column;
    grid-template-rows: repeat(var(--category-rows, 7), auto);
    grid-auto-columns: minmax(120px, 1fr);
    column-gap: 1rem;
    row-gap: 0.5rem;
    align-content: start;
    padding-top: 2rem !important;
    overflow-x: auto;
}

/* Celular: sem o limite por coluna - todas as categorias numa coluna só,
   em vez de colunas que não cabem e fazem a caixa rolar pro lado. */
@media (max-width: 767.98px) {
    .product-categories-grid {
        grid-auto-flow: row;
        grid-template-rows: none;
        grid-template-columns: 1fr;
        overflow-x: visible;
    }
}

/* Foto do produto (views/products/form.php, aba Geral). Em tela grande a
   caixa fica lado a lado com "Categorias Vinculadas" e estica até a altura
   dela - a foto (ou o card "Add Foto") acompanha essa altura, quadrada, com
   a mesma folga em cima, embaixo e à esquerda. O min-height segura um
   tamanho decente quando há poucas categorias; o max-height evita que uma
   lista longa de categorias estique a foto além da largura da caixa (aí ela
   fica no topo, com 320px). */
@media (min-width: 992px) {
    #gallery-preview-container {
        padding: 1rem !important;
    }

    #gallery-preview-container > .original-media-card,
    #gallery-preview-container > .local-new-card,
    #gallery-preview-container > .reused-photo-card,
    #gallery-preview-container > #trigger-upload-card {
        flex: 0 0 auto;
        width: auto;
        height: 100%;
        min-height: 200px;
        max-height: 320px;
        aspect-ratio: 1 / 1;
        margin-top: 0;
        padding: 0;
    }

    #gallery-preview-container > * > .ratio {
        height: 100%;
    }
}

/* Visualizador de foto em tamanho real (ver public/js/main.js). A imagem
   nunca é ampliada além do tamanho natural - só reduzida pra caber na tela. */
.photo-viewer-img {
    max-width: 100%;
    max-height: calc(100vh - 120px);
    object-fit: contain;
    border-radius: 6px;
}

img[data-photo-zoom],
.complement-photo-slot.has-photo {
    cursor: zoom-in;
}

.complement-photo-slot img {
    width: 100%;
    height: 100%;
    /* cover e não contain: o slot é pequeno e quadrado, e uma foto de produto
       em contain viraria uma tarja fina no meio de duas faixas vazias. */
    object-fit: cover;
}

/* Grade de adicionais (.items-list-container) - quantas colunas cabem, ATÉ
   largura de tablet, é decidido pelo próprio grid (`auto-fit`/`minmax`), não
   por um número de colunas fixo por breakpoint. Motivo (2026-08-13): um
   valor de largura fixa por card (testado com 480px e depois 370px) sempre
   acerta uma faixa de telas e erra outra - um iPad 11" real tem só ~100px de
   diferença de área útil entre retrato (sidebar recolhida, ~672-686px) e
   paisagem (sidebar fixa, ~767-781px, ver breakpoint `min-width:992px` do
   sb-admin.css), e o usuário queria 2 colunas no retrato e 3 na paisagem -
   intervalo estreito demais pra acertar via largura fixa em pixel.
   `minmax(240px, 1fr)` deixa o navegador calcular sozinho: 1 no celular, 2 no
   retrato do tablet, 3 na paisagem do tablet. `grid-auto-rows: minmax(88px,
   auto)` substitui o hack antigo de repetir `min-height:88px` no item E no
   tile "Inserir Item Adicional" pra eles baterem quando um deles sobra
   sozinho numa linha - no grid, toda linha (completa ou não) já nasce com o
   mesmo piso de altura.

   Acima de 1400px (desktop de verdade - ver o comentário do media query logo
   abaixo pra saber por que não é 1200px) o `auto-fit` com piso de 240px
   deixava de fazer sentido: numa tela de ~1920px ele cabia 6+ colunas de
   ~250px, esmagando o card (linha de preço/promo que cabia lado a lado em
   480px virava 2 linhas espremidas). */
.complement-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    grid-auto-rows: minmax(88px, auto);
    gap: 0.5rem;
}

@media (min-width: 1400px) {
    /* Corrigido de "repeat(2, 480px)" fixo (achado que estava errado, ver
       comentário acima) pra um `auto-fit` com piso de 480px - o usuário
       confirmou que 3 colunas cabiam bem no monitor real dele, só não podiam
       ficar mais ESTREITAS que 480px (era isso que "esmagava" o card, não a
       quantidade de colunas em si). Com piso 480px o card nunca fica menor
       que o tamanho "largo" original - a quantidade de colunas (2, 3, 4...)
       vira consequência de quanto espaço o monitor realmente tem, sem número
       fixo chutado.

       O limite deste media query (era 1200px) foi corrigido pra 1400px depois
       de um bug real: um Galaxy Tab A11+ (1920x1080 físico, DPR~1.5 -> viewport
       CSS de paisagem ~1280px) caía nesta faixa e o piso de 480px SUBESTIMAVA
       o espaço disponível - a barra lateral fixa (265px) + o padding da página
       (~148px) comem ~413px de QUALQUER viewport (medido: viewport 1280 vira
       container 867px), e 867px não cabe nem 2 colunas de 480+gap (precisa de
       968px) - resultado: colapsava pra 1 coluna só, pior que o piso de 240px
       que vinha usando antes. 1400px é o menor valor onde o piso de 480px já
       garante pelo menos 2 colunas de verdade (968px de conteúdo + a folga de
       413px = 1381px, arredondado com margem). Abaixo disso, mesmo que a tela
       já não seja mais tablet, o piso de 240px continua sendo a escolha mais
       segura - ele nunca deixa de caber pelo menos 2 colunas por conta própria. */
    .complement-grid {
        grid-template-columns: repeat(auto-fit, minmax(480px, 1fr));
    }
}

/* ==========================================================================
   9. MENSAGENS DE SUCESSO/ERRO
   A página recarrega já com a caixa visível no topo, sem espaço para rolar até
   ela - a animação de entrada é o que chama atenção para a mensagem.
   ========================================================================== */
@keyframes flashMessageEnter {
    0% {
        opacity: 0;
        transform: translateY(-12px);
        box-shadow: 0 0 0 0 rgba(0, 0, 0, 0.18);
    }
    60% {
        opacity: 1;
        transform: translateY(0);
    }
    100% {
        opacity: 1;
        transform: translateY(0);
        box-shadow: 0 0 0 14px rgba(0, 0, 0, 0);
    }
}

.js-flash-message-enter {
    animation: flashMessageEnter 0.7s ease-out;
}

/* ==========================================================================
   10. AJUSTES DE TELA PEQUENA
   ========================================================================== */
@media (max-width: 767.98px) {
    /* A .navbar-brand herda a largura fixa do menu lateral (pensada para o
       desktop), o que faz a logo ocupar boa parte da barra numa tela estreita. */
    .sb-topnav .navbar-brand {
        width: auto;
        max-width: 35%;
    }
}

@media (max-width: 768px) {
    /* O texto de apoio abaixo do título das listagens não pode competir por
       espaço com o botão "Novo X" ao lado. 50vw (e não 50%) porque a referência
       é a largura da TELA, não a do container flex estreito ao lado do botão. */
    .listing-subtitle {
        max-width: 50vw;
    }

    /* No celular, o `.container-fluid.px-4` do header.php (24px de cada lado)
       soma com o `.card-body.p-4` (outro 24px de cada lado) que envolve quase
       toda tela de cadastro/edição - 96px jogados fora numa tela de ~390px é
       ~25% da largura útil perdida só em padding duplicado. Reduz os dois pra
       12px cada (48px total) - ainda dá respiro sem desperdiçar tanto espaço
       que faria falta pra formulários com campo lado a lado (ex.: linha de
       combo com select+botão, ver [[project_complement_grid_layout]]).
       !important necessário: são utilitários do Bootstrap (`.px-4`/`.p-4`),
       que nesta build já vêm com !important próprio (confirmado no
       bootstrap.min.css) - uma regra sem !important aqui não teria efeito. */
    .container-fluid.px-4 {
        padding-left: 12px !important;
        padding-right: 12px !important;
    }

    .card-body.p-4 {
        padding-left: 12px !important;
        padding-right: 12px !important;
    }

    /* Botões que escondem o texto no celular (só ícone): centraliza o ícone e
       trava a altura, senão cada glifo do Font Awesome estica o botão de um
       jeito diferente. */
    .btn:has(> .d-none.d-md-inline) {
        display: inline-flex;
        align-items: center;
        justify-content: center;
        height: 31px;
    }

    /* --- Listagens no celular ---------------------------------------------
       A tabela rola na horizontal por arraste, sem travar a rolagem vertical
       da página (senão o rodapé nunca é alcançado no celular): o wrapper é
       quem rola, e para isso a tabela precisa poder crescer além da largura
       da tela. Este bloco vivia repetido, idêntico, num <style> dentro de
       cada uma das 14 telas de listagem - agora vale para qualquer DataTable,
       inclusive as que forem criadas depois.
       Os !important daqui vencem a largura que o DataTables grava no atributo
       style da tabela e das colunas. */
    .dataTables_wrapper {
        overflow-x: auto;
        -webkit-overflow-scrolling: touch;
        cursor: grab;
    }

    .dataTables_wrapper.dragging {
        cursor: grabbing;
        user-select: none;
    }

    table.dataTable,
    .dataTables_wrapper table.dataTable {
        width: auto !important;
        max-width: none !important;
        white-space: nowrap;
    }

}

/* ==========================================================================
   11. DESTAQUE DE LINHA RECÉM-CLONADA (Listagem de Produtos)
   Logo após clonar um produto, a linha do clone é fixada no topo da listagem
   junto do original (ver ProductController::datatable()) - este flash amarelo
   é só o reforço visual pra achar ela sem precisar ler cada linha.
   ========================================================================== */
@keyframes productRowHighlightFade {
    0% {
        background-color: rgba(255, 193, 7, 0.35);
    }
    100% {
        background-color: transparent;
    }
}

.product-row-highlight {
    animation: productRowHighlightFade 2.5s ease-out;
    border-radius: 4px;
}

/* ==========================================================================
   12. VITRINE DE PRODUTOS DO MONTADOR DE PEDIDOS
   Cardápio ativo em seções (Destaques, categorias, "Outros produtos") do "Novo
   Pedido" e do modal "Adicionar Produto" da mesa - a marcação é gerada por
   public/js/product-picker.js, que é único pras duas telas, então o estilo
   também mora num lugar só.
   ========================================================================== */

/* Faixa de atalhos de categoria. Rola na horizontal quando o cardápio tem
   muitas categorias, com respiro em cima e embaixo pra não encostar no campo
   de filtro nem no primeiro cabeçalho de seção. */
.product-picker-nav {
    display: flex;
    flex-wrap: nowrap;
    gap: 0.4rem;
    overflow-x: auto;
    padding: 0.5rem 0.15rem 0.65rem;
    margin-bottom: 0.35rem;
    border-bottom: 1px solid var(--cx-border);
    scrollbar-width: thin;
}

.product-picker-nav::-webkit-scrollbar {
    height: 4px;
}

.product-picker-nav::-webkit-scrollbar-thumb {
    background: var(--cx-border);
    border-radius: 999px;
}

/* Pílula de categoria: fundo claro e texto do próprio tema (o cinza do
   .btn-outline-secondary destoava do resto da tela), com altura confortável
   em vez do botão achatado que o .btn-sm + px-2 produzia. */
.product-picker-chip {
    flex: 0 0 auto;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.85rem;
    border: 1px solid var(--cx-border);
    border-radius: 999px;
    background: var(--cx-surface);
    color: var(--cx-text);
    font-size: 0.78rem;
    font-weight: 600;
    line-height: 1.35;
    white-space: nowrap;
    transition: background-color 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}

.product-picker-chip:hover {
    background: var(--cx-accent-bg-subtle);
    border-color: var(--cx-accent-border-subtle);
    color: var(--cx-accent-emphasis);
}

.product-picker-chip.active {
    background: var(--cx-accent);
    border-color: var(--cx-accent);
    color: #fff;
}

.product-picker-chip .product-picker-chip-icon {
    font-size: 0.72rem;
    opacity: 0.85;
}

/* A lista rola só na vertical: a grade de cards abaixo não usa .row do
   Bootstrap justamente porque a margem negativa dele criava uma barra de
   rolagem horizontal dentro deste container. */
.product-picker-list {
    overflow-y: auto;
    overflow-x: hidden;
}

.product-picker-section + .product-picker-section {
    margin-top: 1.15rem;
}

/* Cabeçalho da seção gruda no topo enquanto a categoria está em tela - por
   isso o fundo opaco, senão os cards passariam por baixo do texto. */
.product-picker-section-header {
    position: sticky;
    top: 0;
    z-index: 2;
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0.35rem 0 0.55rem;
    background: var(--cx-surface);
}

.product-picker-section-title {
    font-size: 0.72rem;
    font-weight: 700;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    color: var(--cx-text-muted);
}

.product-picker-section-count {
    font-size: 0.68rem;
    font-weight: 600;
    color: var(--cx-text-muted);
    background: var(--cx-table-head-bg);
    border-radius: 999px;
    padding: 0.05rem 0.45rem;
}

.product-picker-section-rule {
    flex: 1 1 auto;
    height: 1px;
    background: var(--cx-border);
}

/* auto-fill + minmax: a quantidade de colunas sai da largura disponível, sem
   classe de coluna por breakpoint. A largura mínima muda no celular (2 cards
   por linha, como antes) e cresce no desktop (3 por linha, com espaço pro nome
   do produto em vez de 4 cards espremidos). */
.product-picker-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(150px, 1fr));
    gap: 0.5rem;
}

@media (min-width: 576px) {
    .product-picker-grid {
        grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
    }
}

.product-card {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    width: 100%;
    padding: 0.5rem;
    border: 1px solid var(--cx-border);
    border-radius: var(--cx-radius-sm);
    background: var(--cx-surface);
    text-align: left;
    cursor: pointer;
    transition: background-color 0.15s ease, border-color 0.15s ease;
}

.product-card:hover {
    background: var(--cx-accent-bg-subtle);
    border-color: var(--cx-accent-border-subtle);
}

.product-card.selected {
    border-color: var(--cx-accent);
    box-shadow: inset 0 0 0 1px var(--cx-accent);
}

.product-card-img {
    width: 44px;
    height: 44px;
    object-fit: cover;
    border-radius: 6px;
    flex-shrink: 0;
}

.product-card-info {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    min-width: 0;
}

.product-card-name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    font-size: 0.82rem;
    font-weight: 700;
    line-height: 1.25;
    color: var(--cx-text);
}

.product-card-meta {
    display: flex;
    align-items: baseline;
    flex-wrap: wrap;
    gap: 0.3rem;
    font-size: 0.78rem;
}

.product-card-price {
    font-weight: 700;
    color: var(--cx-accent-text);
}

.product-card-price-old {
    font-size: 0.7rem;
    color: var(--cx-text-muted);
    text-decoration: line-through;
}

.product-card-code {
    font-size: 0.7rem;
    color: var(--cx-text-muted);
}

/* ==========================================================================
   ÂNCORA DO CARD DE PEDIDOS RETIDOS DO IFOOD (views/ifood_mapping/index.php)
   O toast "Pedido do iFood retido!" e o badge do sino levam pra
   #ifood-pending-orders. Sem scroll-margin-top o salto nativo da âncora
   alinha o topo do card exatamente em y=0 - por baixo da .sb-topnav fixa
   (--cx-topbar-height, ver sb-admin.css) -, escondendo o título "N pedido(s)..." atrás
   dela. Os 16px extra são a mesma folga do padding do card.
   ========================================================================== */
#ifood-pending-orders {
    scroll-margin-top: calc(var(--cx-topbar-height) + 16px);
}

/* ==========================================================================
   13. TEMA ESCURO - ACERTOS QUE OS TOKENS NÃO ALCANÇAM

   Quase todo o sistema troca de tema só pelos tokens da seção 1, porque o
   Bootstrap 5.3 repinta sozinho (sob data-bs-theme="dark") card, modal,
   dropdown, input, tabela, .text-muted e a família toda de badges -subtle.

   Sobram os utilitários de cor FIXA que o framework não tem como repintar,
   porque a cor faz parte do nome da classe. São 4, e estão espalhados por ~50
   views: .bg-white (153 usos), .text-dark (88), .bg-light (73) e .table-light
   (30). Reapontá-los aqui é o que evita reescrever essas views uma a uma - e,
   mais importante, é o que faz uma tela NOVA que use .bg-white já nascer certa
   nos dois temas.

   Os seletores levam o atributo na frente (0,2,0 de especificidade) só para
   vencer o utilitário original (0,1,0); o !important é obrigatório porque o do
   Bootstrap também é.
   ========================================================================== */

/* A logo tem o texto branco, então some numa barra clara: são duas artes, e o
   tema escolhe qual aparece (ver views/layout/header.php). Fica no CSS, e não
   no PHP, porque no modo "automático" quem decide o tema é o navegador.
   Os !important são obrigatórios: a <img> da barra de topo carrega `.d-block`
   do Bootstrap (que é `display:block !important`) para não se apoiar na linha
   de base do texto - sem !important aqui, as DUAS logos aparecem lado a lado. */
.cx-logo-for-light {
    display: none !important;
}

[data-bs-theme="light"] .cx-logo-for-dark {
    display: none !important;
}

[data-bs-theme="light"] .cx-logo-for-light {
    display: block !important;
}

/* Superfícies brancas cravadas viram a superfície do tema. .bg-white é o fundo
   de card/painel em quase toda tela de cadastro; .bg-light é o cinza de apoio
   (cabeçalhos de bloco, faixas de resumo), que aqui vira o degrau de elevação
   logo acima do card. */
[data-bs-theme="dark"] .bg-white {
    background-color: var(--cx-surface) !important;
}

[data-bs-theme="dark"] .bg-light {
    background-color: var(--cx-surface-raised) !important;
}

/* .text-dark é usado como "texto forte" (títulos, valores em destaque, badges
   .bg-warning.text-dark) - no escuro a intenção continua sendo contraste
   máximo contra o fundo, só que invertido.
   A exceção vem logo abaixo. */
[data-bs-theme="dark"] .text-dark {
    color: var(--cx-text) !important;
}

/* Exceção: texto sobre um fundo que NÃO troca de tema (o badge amarelo de
   alerta de NFC-e continua amarelo no escuro, e ali o preto é que é legível).
   A lista é EXATAMENTE a dos fundos sólidos que seguem claros no tema escuro:
   o amarelo do .bg-warning e o ciano do .bg-info. Não inclua .bg-light nem
   .bg-white aqui - esses dois são repintados de escuro logo acima, e listá-los
   deixaria texto preto sobre fundo escuro. Foi esse o erro que deixou o código
   do cupom (`badge bg-light text-dark font-monospace`) ilegível a 1.04:1. */
[data-bs-theme="dark"] .bg-warning.text-dark,
[data-bs-theme="dark"] .bg-info.text-dark {
    color: #212529 !important;
}

/* Cores de TEXTO de estado (.text-secondary 425 usos, .text-danger 163,
   .text-success 62, e as menores) são a mesma armadilha do .text-dark: a cor
   está no nome da classe e o Bootstrap não a repinta no escuro. Elas foram
   escolhidas para contrastar com FUNDO BRANCO, então sobre o card escuro caem
   para ~3:1 - o medido na tela de Produtos antes desta regra era 3.15:1 no
   .text-secondary e 3.54:1 no .text-success.

   A correção aponta cada uma para o `-text-emphasis` que o próprio Bootstrap
   já define no tema escuro (versões claras da mesma família). O que NÃO se
   pode fazer é clarear as variáveis --bs-success-rgb e companhia: elas também
   pintam os fundos sólidos (.bg-success), que ficariam claros demais para o
   texto branco por cima.

   .text-primary sai da nossa própria família, e não da azul do framework. */
[data-bs-theme="dark"] .text-secondary {
    color: var(--bs-secondary-text-emphasis) !important;
}

[data-bs-theme="dark"] .text-success {
    color: var(--bs-success-text-emphasis) !important;
}

[data-bs-theme="dark"] .text-danger {
    color: var(--bs-danger-text-emphasis) !important;
}

[data-bs-theme="dark"] .text-warning {
    color: var(--bs-warning-text-emphasis) !important;
}

[data-bs-theme="dark"] .text-info {
    color: var(--bs-info-text-emphasis) !important;
}

[data-bs-theme="dark"] .text-primary {
    color: var(--cx-accent-text) !important;
}

/* Exceção: sobre um fundo sólido que NÃO troca de tema, a cor de texto original
   é que continua legível (um .text-danger dentro de um .bg-danger, por
   exemplo, ficaria vermelho claro sobre vermelho). Restaura o branco que o
   próprio Bootstrap usaria nesses pares. */
[data-bs-theme="dark"] .bg-success .text-success,
[data-bs-theme="dark"] .bg-danger .text-danger,
[data-bs-theme="dark"] .bg-primary .text-primary,
[data-bs-theme="dark"] .bg-secondary .text-secondary {
    color: #fff !important;
}

/* .table-light no <thead> já era tratado pela seção 6 (que troca só as
   variáveis --bs-table-*); esta regra cobre os outros usos, onde a classe
   aparece numa <tr> ou <td> de destaque. */
[data-bs-theme="dark"] .table-light {
    --bs-table-color: var(--cx-table-head-color);
    --bs-table-bg: var(--cx-table-head-bg);
    --bs-table-border-color: var(--cx-table-head-border);
    --bs-table-striped-bg: var(--cx-table-stripe-bg);
    --bs-table-striped-color: var(--cx-text);
    --bs-table-active-bg: var(--cx-table-hover-bg);
    --bs-table-active-color: var(--cx-text);
    --bs-table-hover-bg: var(--cx-table-hover-bg);
    --bs-table-hover-color: var(--cx-text);
}

/* No claro é a sombra que descola o card do fundo; no escuro sombra não
   aparece, e o que faz esse papel é a borda. */
[data-bs-theme="dark"] .card {
    border-color: var(--cx-border);
}

/* --- Variantes de botão de cor fixa --------------------------------------
   Cuidado para não confundir com os utilitários acima: .btn-light é a VARIANTE
   DE BOTÃO (--bs-btn-bg: #f8f9fa cravado), não o utilitário .bg-light. O
   Bootstrap não a repinta no tema escuro, e ela é justamente o botão de ação
   (editar/excluir) de toda listagem do sistema - 75 usos em 35 views. Sem isto
   cada linha de tabela escura fica com dois retângulos brancos na ponta.

   As variáveis --bs-btn-* são lidas pela regra .btn do próprio framework, então
   basta redefini-las aqui: nenhum !important é necessário. */
[data-bs-theme="dark"] .btn-light {
    --bs-btn-color: var(--cx-text);
    --bs-btn-bg: var(--cx-surface-raised);
    --bs-btn-border-color: var(--cx-border);
    --bs-btn-hover-color: var(--cx-text);
    --bs-btn-hover-bg: var(--cx-table-hover-bg);
    --bs-btn-hover-border-color: var(--cx-text-muted);
    --bs-btn-active-color: var(--cx-text);
    --bs-btn-active-bg: var(--cx-table-hover-bg);
    --bs-btn-active-border-color: var(--cx-text-muted);
    --bs-btn-disabled-color: var(--cx-text-muted);
    --bs-btn-disabled-bg: var(--cx-surface-raised);
    --bs-btn-disabled-border-color: var(--cx-border);
}

/* .btn-outline-secondary (110 usos) tem fundo transparente, mas texto e borda
   no cinza #6c757d, que sobre a página escura fica em ~3:1 - abaixo do mínimo
   legível. Clareia os dois mantendo o fundo transparente. */
[data-bs-theme="dark"] .btn-outline-secondary {
    --bs-btn-color: var(--cx-text-muted);
    --bs-btn-border-color: var(--cx-border);
    --bs-btn-hover-color: var(--cx-text);
    --bs-btn-hover-bg: var(--cx-surface-raised);
    --bs-btn-hover-border-color: var(--cx-text-muted);
    --bs-btn-active-color: var(--cx-text);
    --bs-btn-active-bg: var(--cx-table-hover-bg);
    --bs-btn-active-border-color: var(--cx-text-muted);
    --bs-btn-disabled-color: var(--cx-text-muted);
    --bs-btn-disabled-border-color: var(--cx-border);
}

/* O DataTables vem de CDN com cores próprias para o campo de busca, o "x
   registros por página" e o rodapé de informação - nada disso passa por
   variável do Bootstrap. */
[data-bs-theme="dark"] .dataTables_wrapper .dataTables_length,
[data-bs-theme="dark"] .dataTables_wrapper .dataTables_filter,
[data-bs-theme="dark"] .dataTables_wrapper .dataTables_info,
[data-bs-theme="dark"] .dataTables_wrapper .dataTables_processing,
[data-bs-theme="dark"] .dataTables_wrapper .dataTables_paginate {
    color: var(--cx-text-muted);
}

/* O overlay que escurece o conteúdo quando o menu abre no celular já é preto
   translúcido; no tema escuro ele precisa ser mais forte para ainda separar
   as duas camadas. */
[data-bs-theme="dark"] .sb-sidenav-toggled #layoutSidenav #layoutSidenav_content:before {
    opacity: 0.7;
}

/* ==========================================================================
   14. BRILHO NO TOPO DAS TELAS
   Degradê suave na cor da marca atrás do título de toda tela do painel
   (nasceu no Dashboard). É background da própria área de conteúdo, não um
   pseudo-elemento: não cria camada nem contexto de empilhamento, então não
   interfere em modal/dropdown/tooltip. Origem no content-box para os %
   ignorarem o padding-left que reserva o espaço do menu lateral.
   Os centros/raios das elipses foram escolhidos para o degradê sumir ANTES
   das bordas da faixa (centro >= raio) - senão aparece um corte reto.
   ========================================================================== */
#layoutSidenav #layoutSidenav_content {
    background-image:
        radial-gradient(ellipse 26% 42% at 27% 42%, rgba(var(--cx-accent-rgb), 0.12), transparent),
        radial-gradient(ellipse 22% 38% at 64% 38%, color-mix(in srgb, var(--cx-purple) 9%, transparent), transparent);
    background-repeat: no-repeat;
    background-size: 100% 320px;
    background-position: top left;
    background-origin: content-box;
}
@media print {
    #layoutSidenav #layoutSidenav_content { background-image: none; }
}

/* ==========================================================================
   15. TÍTULO DAS TELAS
   Mesmo título do Dashboard ("Boa noite, ..."): maior que o texto, peso 600,
   letter-spacing levemente negativo e na cor do texto principal (antes era
   .text-secondary, cinza). Toda tela abre com <h2 class="page-title m-0"> -
   o aviso de mensalidade do header continua se ancorando nesse <h2>.
   ========================================================================== */
.page-title {
    font-size: clamp(1.3rem, 1.05rem + 0.8vw, 1.65rem);
    font-weight: 600;
    letter-spacing: -0.02em;
    line-height: 1.2;
    color: var(--cx-text);
}

/* ==========================================================================
   16. VALIDAÇÃO DE FORMULÁRIOS
   Visual com :user-invalid / :user-valid: o campo só fica vermelho DEPOIS que
   a pessoa mexe nele e sai (ou tenta salvar) - nunca enquanto ainda está
   digitando pela primeira vez, nem num formulário recém-aberto. A mensagem
   abaixo do campo (.cx-field-error) é criada pelo main.js ("VALIDAÇÃO NOS
   FORMULÁRIOS"). .cx-user-invalid é o mesmo estado para navegador antigo
   sem :user-invalid (o main.js aplica).
   ========================================================================== */
.form-control:user-invalid,
.form-select:user-invalid,
.form-control.cx-user-invalid,
.form-select.cx-user-invalid {
    border-color: var(--bs-danger);
    box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.12);
}
.form-control:user-invalid:focus,
.form-select:user-invalid:focus,
.form-control.cx-user-invalid:focus,
.form-select.cx-user-invalid:focus {
    border-color: var(--bs-danger);
    box-shadow: 0 0 0 0.25rem rgba(var(--bs-danger-rgb), 0.22);
}
.form-check-input:user-invalid,
.form-check-input.cx-user-invalid {
    border-color: var(--bs-danger);
}
.form-check-input:user-invalid ~ .form-check-label {
    color: var(--bs-danger-text-emphasis);
}

.cx-field-error {
    display: flex;
    align-items: flex-start;
    gap: 0.35rem;
    margin-top: 0.3rem;
    font-size: 0.8rem;
    font-weight: 500;
    line-height: 1.35;
    color: var(--bs-danger-text-emphasis);
    animation: cx-field-error-in 0.22s ease-out;
}
.cx-field-error::before {
    content: "";
    flex: none;
    width: 14px;
    height: 14px;
    margin-top: 0.05rem;
    background: currentColor;
    -webkit-mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 8v4M12 16h.01'/%3E%3C/svg%3E") center / contain no-repeat;
            mask: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='black' stroke-width='2.2' stroke-linecap='round' stroke-linejoin='round'%3E%3Ccircle cx='12' cy='12' r='10'/%3E%3Cpath d='M12 8v4M12 16h.01'/%3E%3C/svg%3E") center / contain no-repeat;
}
@keyframes cx-field-error-in {
    from { opacity: 0; transform: translateY(-3px); }
}

/* ✓ no campo OBRIGATÓRIO preenchido corretamente. Fica de fora quem já usa o
   lado direito (número com setas, data/hora com calendário, arquivo, cor) e
   textarea, em que o ícone ficaria solto no canto. */
.form-control[required]:user-valid:not(textarea):not([type="number"]):not([type="date"]):not([type="time"]):not([type="datetime-local"]):not([type="month"]):not([type="file"]):not([type="color"]):not([readonly]) {
    padding-right: calc(1.5em + 0.75rem);
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23198754' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
    background-repeat: no-repeat;
    background-position: right calc(0.375em + 0.1875rem) center;
    background-size: calc(0.75em + 0.375rem) calc(0.75em + 0.375rem);
}
[data-bs-theme="dark"] .form-control[required]:user-valid:not(textarea):not([type="number"]):not([type="date"]):not([type="time"]):not([type="datetime-local"]):not([type="month"]):not([type="file"]):not([type="color"]):not([readonly]) {
    background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%2375b798' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M20 6 9 17l-5-5'/%3E%3C/svg%3E");
}

@media (prefers-reduced-motion: reduce) {
    .cx-field-error { animation: none; }
}

/* ==========================================================================
   17. LISTAGENS (DataTables) NO VISUAL DO TEMPLATE
   As 21+ listagens usam <table class="table table-striped table-bordered ...
   datatable-transform">. Sem mexer nas views: some a grade vertical, o
   contorno fica arredondado, o cabeçalho vira rótulo pequeno em maiúsculas,
   as células ganham respiro e centralização vertical, e a paginação vira
   pílulas. O zebrado sai pelos tokens (--cx-table-stripe-bg = superfície).

   border-collapse: separate é o que permite arredondar o contorno (com
   collapse, border-radius não tem efeito); por isso as bordas passam a ser
   desenhadas célula a célula (só a de baixo) e os cantos vêm do raio das
   células das pontas.
   ========================================================================== */
table.dataTable,
.dataTables_wrapper table.dataTable {
    border-collapse: separate !important;
    border-spacing: 0;
    border: 1px solid var(--cx-border) !important;
    border-radius: 14px;
}
/* Sem a grade do .table-bordered (bordas laterais e de cima de cada célula) */
table.dataTable.table-bordered > :not(caption) > *,
table.dataTable.table-bordered > :not(caption) > * > * {
    border-width: 0;
}

/* Cabeçalho no padrão do template em TODA tabela (listagens, modais como o
   resumo do pedido no Kanban, detalhe do pedido, histórico do caixa...): rótulo
   pequeno em maiúsculas. O contorno arredondado e o resto desta seção ficam só
   nas listagens (table.dataTable). */
table.dataTable > thead > tr > th,
.table > thead > tr > th {
    padding: 0.65rem 0.9rem;
    font-size: 0.74rem;
    font-weight: 600;
    letter-spacing: 0.04em;
    text-transform: uppercase;
    white-space: nowrap;
    color: var(--cx-table-head-color);
    border-bottom: 1px solid var(--cx-table-head-border) !important;
}
/* Tabela compacta (.table-sm, comum nos modais): mesmo rótulo, menos respiro */
.table.table-sm > thead > tr > th {
    padding: 0.45rem 0.55rem;
}
table.dataTable > thead > tr > th:first-child { border-top-left-radius: 13px; }
table.dataTable > thead > tr > th:last-child  { border-top-right-radius: 13px; }

table.dataTable > tbody > tr > td {
    padding: 0.7rem 0.9rem;
    vertical-align: middle;
    border-bottom: 1px solid var(--cx-border) !important;
}
table.dataTable > tbody > tr:last-child > td { border-bottom-width: 0 !important; }
table.dataTable > tbody > tr:last-child > td:first-child { border-bottom-left-radius: 13px; }
table.dataTable > tbody > tr:last-child > td:last-child  { border-bottom-right-radius: 13px; }
table.dataTable > tbody > tr > td { transition: background-color 0.15s ease, box-shadow 0.15s ease; }

/* Linha marcada no checkbox de seleção em massa: leve tom da marca */
table.dataTable > tbody > tr:has(.form-check-input:checked) > td {
    background-color: rgba(var(--cx-accent-rgb), 0.07);
    box-shadow: inset 0 0 0 9999px rgba(var(--cx-accent-rgb), 0.07);
}

/* Setas de ordenação mais discretas; a da coluna ordenada fica na cor da marca */
table.dataTable > thead .sorting::before,
table.dataTable > thead .sorting::after,
table.dataTable > thead .sorting_asc::before,
table.dataTable > thead .sorting_asc::after,
table.dataTable > thead .sorting_desc::before,
table.dataTable > thead .sorting_desc::after {
    font-size: 0.7em;
}
table.dataTable > thead > tr > th:is(.sorting_asc, .sorting_desc) {
    color: var(--cx-accent-text);
}

/* Rodapé: informação discreta + paginação em pílulas separadas */
.dataTables_wrapper .dataTables_info {
    font-size: 0.82rem;
    color: var(--cx-text-muted);
    padding-top: 0.9rem !important;
}
.dataTables_wrapper .dataTables_paginate {
    padding-top: 0.6rem !important;
}
.dataTables_wrapper .pagination {
    gap: 0.3rem;
    margin: 0;
}
.dataTables_wrapper .pagination .page-link {
    min-width: 32px;
    height: 32px;
    padding: 0 0.65rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    border: 0;
    border-radius: 9px !important;
    background: transparent;
    color: var(--cx-text-muted);
    font-size: 0.82rem;
    font-weight: 550;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.dataTables_wrapper .pagination .page-link:hover {
    background-color: var(--cx-table-hover-bg);
    color: var(--cx-text);
}
.dataTables_wrapper .pagination .page-item.active .page-link {
    background-color: var(--cx-accent);
    color: #fff;
    box-shadow: 0 6px 14px -6px rgba(var(--cx-accent-rgb), 0.8);
}
.dataTables_wrapper .pagination .page-item.disabled .page-link {
    background: transparent;
    opacity: 0.45;
}

/* ==========================================================================
   Layout do garçom (body.cx-waiter - perfil Garçom, ver
   $waiterLayout em views/layout/header.php): sem menu lateral, então o
   conteúdo não reserva a largura dele (nem o padding do desktop, nem a
   margem negativa do celular), e o rodapé de copyright sai pra sobrar tela.
   ========================================================================== */
body.cx-waiter #layoutSidenav #layoutSidenav_content {
    padding-left: 0;
    margin-left: 0;
}

body.cx-waiter #layoutSidenav_content > footer {
    display: none;
}
