/*
=====================================================
PHD Brasil Design System — base tokens & primitives
Framework/project-agnostic. Responsiveness gaps present in the reference
implementation this was extracted from are closed here (see SKILL.md
"Gaps fixed" for the list).

Load order: this file first, then page-specific CSS.
Pairs with reference/base-template.html.
=====================================================
*/

:root {
    /* NOTE: CSS custom properties cannot be interpolated into
       @media (max-width: ...) queries — the values below are
       the canonical breakpoint scale, but media queries in this
       file (and any project using it) must repeat the literal
       px values. Keep them in sync with this table if you add more. */
    /* --bp-sm: 480px */
    /* --bp-md: 768px */
    /* --bp-lg: 1024px */

    --font-family: 'Public Sans', sans-serif;

    /* Accent — PHD brand green. This is brand-locked: it is the same hex in
       dark and light mode (see the light-theme block below, which
       deliberately does NOT redefine these two). Never let a "light theme"
       reinterpretation swap this for a different hue. */
    --primary-btn-color: #00BCB5;
    --primary-btn-color-hover: #19e8dd;

    /* Surfaces */
    --primary-background: #e4e4e4;
    --primary-background-blur: rgba(255, 255, 255, 0.6);
    --background-hover: #e0e0e0;
    --background-blur: blur(6px);
    --primary-input-color: #eef0f1;
    --border-color: #d0d0d0;
    --row-hover-bg: rgba(0, 0, 0, 0.05);
    --bg-image: url('../img/background-light.webp');
    --input-color-scheme: light; /* native form-control rendering (date/month pickers, scrollbars) */

    /* Text */
    --primary-font-color: #1c1c1c;
    --no-contrast-font-color: #5f5f5f;
    /* Tom mais escuro da marca, usado só onde o acento é TEXTO: o hex cru da
       marca tem ~2,4:1 sobre branco e reprova no WCAG AA. O
       --primary-btn-color (preenchimento de botão, ícones) mantém o hex
       exato da marca. */
    --constrat-font-color: #00796B;

    /* Semânticas. Só existem 3 matizes de propósito: danger (ruim), warning
       (atenção), info (neutro). "Bom/positivo" reaproveita o
       --primary-btn-color (o verde da marca já significa isso — ver
       chart-palette.md) em vez de uma 4a cor inventada. Não adicionar cor
       ad hoc além dessas quatro quando precisar de uma escala de status. */
    --danger-color: #c80839;
    --danger-color-hover: #ed1144;
    --warning-color: #d97706;
    --warning-color-hover: #f59e0b;
    --error-text-color: #c62828;
    --info-color: #007bff;
    --info-color-hover: #0056b3;

    /* Véu sobre a foto de fundo (ver body{} abaixo): uma foto crua atrás dos
       cartões de vidro compete com eles por atenção. Entra como cor chapada
       sob o background-image, nunca como filter (filter borraria/escureceria
       o conteúdo em primeiro plano junto). */
    --bg-overlay: rgba(255, 255, 255, 0.7);

    /* Shape */
    --btn-input-height: 32px;
    /* Botão compacto de ação de linha: mesma altura do .btn-icon, para que uma
       coluna de ações fique igual tenha ela ícone ou rótulo de texto. */
    --btn-sm-height: 28px;
    --btn-border-radius: 8px;
    --input-border-radius: 8px;
    --card-border-radius: 16px;

    /* Type scale */
    --label-font-weight: 500;
    --label-font-size: small;
    --h2-font-size: x-large;
    --h2-font-weight: 600;
    --btn-font-size: small;
    /* 12px, o mesmo dos botões de filtro — a keyword `small` (~13px) é medida
       contra o root, não contra a tabela, e sai maior que a linha onde vive. */
    --btn-sm-font-size: 12px;
    --btn-font-weight: 600;

    /* Spacing scale (gap fix: reference implementation hardcoded 8/16/32px inline) */
    --space-1: 4px;
    --space-2: 8px;
    --space-3: 12px;
    --space-4: 16px;
    --space-5: 24px;
    --space-6: 32px;

    --dashboard-gap: var(--space-5);

    /* Layout */
    --sidebar-width: 220px;
    --content-max-width: 1600px; /* gap fix: cap content width on ultrawide screens */

    /* Elevação. Os cartões de vidro tinham borda + blur e nenhuma sombra, então
       nada parecia "levantado" da foto de fundo. Três degraus: cartão em
       repouso, cartão elevado/hover e flutuante (mantido por paridade, ainda
       sem uso). Sombras suaves de propósito — sombra pesada sobre superfície
       clara lê como borrão sujo, não como elevação. */
    --shadow-sm: 0 1px 3px rgba(0, 0, 0, 0.08);
    --shadow-md: 0 8px 24px rgba(0, 0, 0, 0.10), 0 2px 6px rgba(0, 0, 0, 0.06);
    --shadow-lg: 0 16px 40px rgba(0, 0, 0, 0.14), 0 4px 12px rgba(0, 0, 0, 0.08);

    color-scheme: light;
}

/*
=====================================================
Tema único (claro)
=====================================================
Este app é tema claro apenas — os tokens acima são a paleta inteira. Não há
@media (prefers-color-scheme) nem [data-theme] aqui: qualquer cor nova entra
direto no :root acima, sempre como var(--token), nunca como hex solto num
componente (foi assim que a .sidebar-label ficou ilegível uma vez).

O design system de origem (.claude/skills/design-system/) continua com os
dois temas — quem for reaproveitá-lo em outro projeto pega os dois de lá.
*/

/*
=====================================================
Utility Classes
=====================================================
*/

.primary-background {
    background: var(--primary-background-blur);
    border-radius: var(--card-border-radius);
    border: 1px solid var(--border-color);
    backdrop-filter: var(--background-blur);
    /* gap fix: glass cards had zero shadow — no depth cue that they float
       above the background photo. transition is shared so any :hover state
       added on top (interactive cards, buttons) animates instead of snapping. */
    box-shadow: var(--shadow-md);
    transition: box-shadow 0.25s ease, transform 0.25s ease, border-color 0.25s ease;
}

.flex-center-space-between {
    display: flex;
    flex-direction: column;
    justify-content: space-evenly;
    align-items: center;
}

/*
=====================================================
Base
=====================================================
*/

* {
    font-family: var(--font-family);
    box-sizing: border-box;
    margin: 0;
    padding: 0;
}

img {
    display: block;
    margin: auto;
    max-width: 100%;
    width: 100%;
}

body {
    font-family: var(--font-family);
    color: var(--primary-font-color);
    /* --bg-image aponta para background-light.webp (ver :root acima).
       gap fix: the raw photo alone (no scrim) reads as loud/busy behind
       the glass cards — especially in light mode, where a bright photo
       fights the cards for attention instead of sitting quietly behind
       them. --bg-overlay is a flat color layered as its own background
       image (never a filter — filter would blur/dim the cards too, not
       just the photo behind them). */
    background-image: linear-gradient(var(--bg-overlay), var(--bg-overlay)), var(--bg-image);
    background-size: cover;
    background-position: center;
    background-attachment: fixed;
    min-height: 100vh;
}

h2 {
    font-size: var(--h2-font-size);
    font-weight: var(--h2-font-weight);
    letter-spacing: -0.01em;
}

/* Card/section-level heading — one step down from the page h2 (e.g. the
   title above a table inside a card). Not used for the page title itself. */
h3 {
    font-size: 16px;
    font-weight: 600;
    margin-bottom: var(--space-3);
}

/* gap fix: no visible focus state existed anywhere except filters */
:focus-visible {
    outline: 2px solid var(--primary-btn-color);
    outline-offset: 2px;
}

/*
=====================================================
App shell: sidebar + content
=====================================================
*/

.authenticated-layout {
    display: flex;
    min-height: 100vh;
}

#sidebar {
    width: var(--sidebar-width);
    height: 100vh;
    background: var(--primary-background-blur);
    backdrop-filter: var(--background-blur);
    display: flex;
    flex-direction: column;
    padding: var(--space-4);
    box-sizing: border-box;
    position: sticky;
    top: 0;
    border-right: 1px solid var(--border-color);
    overflow-y: auto;
    transition: transform 0.2s ease;
    z-index: 100;
}

#sidebar header {
    padding-block: var(--space-2);
    width: 100%;
    display: flex;
    justify-content: center;
}

/* gap fix: logo-phd.svg's "PHD" letterforms were baked in as solid white
   fill, so they disappeared on a light background. The paths now use
   fill="currentColor" and the mark follows --primary-font-color here, so
   it stays legible in both themes without a second logo asset. */
.brand-lockup {
    display: flex;
    align-items: center;
    gap: var(--space-2);
}

.brand-lockup .brand-mark {
    display: block;
    width: 72px;
    height: auto;
    color: var(--primary-font-color);
}

/* logo-phd-nome.png: dark-theme-only asset (baked-in white text). Keep this
   rule only if a project still needs the raster wordmark on a page that is
   never shown in light mode (e.g. a fixed-dark login screen). */
#logo-sidebar {
    width: 80px;
}

/* gap fix: era `color: #f7f7f7` fixo — quase branco, legível só sobre a
   sidebar escura e praticamente invisível no tema claro. Token temático
   resolve nos dois temas (mesmo tratamento de rótulo discreto usado em
   .forms-container label). */
.sidebar-label {
    font-size: 12px;
    color: var(--no-contrast-font-color);
}

.sidebar-content {
    display: flex;
    flex: 1;
    flex-direction: column;
    justify-content: space-between;
    width: 100%;
    margin-top: var(--space-2);
}

.sidebar-group {
    display: flex;
    flex-direction: column;
    gap: 3px;
    height: fit-content;
    border-top: 1px solid var(--border-color);
    padding-top: var(--space-2);
}

.sidebar-group:first-child {
    border-top: none;
    padding-top: 0;
}

.sidebar-button {
    display: flex;
    align-items: center;
    gap: var(--space-2);
    padding: var(--space-2);
    font-size: 12px;
    color: var(--no-contrast-font-color);
    border-radius: 6px;
    text-decoration: none; /* gap fix: this is an <a> for the nav items — no rule reset the default underline */
    transition: background-color 0.15s ease, color 0.15s ease;
}

.sidebar-button:hover,
.sidebar-button:active {
    background: var(--background-hover);
    color: var(--primary-font-color);
}

/* Current page in the nav — set via aria-current="page" on the link
   (templates compare request.resolver_match.url_name), not a manual class,
   so it can never drift out of sync with the actual route. */
.sidebar-button[aria-current="page"] {
    background: color-mix(in srgb, var(--primary-btn-color) 14%, transparent);
    color: var(--primary-btn-color);
    font-weight: 600;
}

.main-content {
    flex: 1;
    min-width: 0; /* gap fix: flex children need this or they refuse to shrink below content size */
    overflow-y: auto;
}

/* gap fix: sidebar toggle, hidden above --bp-md */
.sidebar-toggle {
    display: none;
}

/* gap fix: off-canvas sidebar under 768px (reference implementation had no mobile behavior at all) */
@media (max-width: 768px) {
    .sidebar-toggle {
        display: flex;
        align-items: center;
        justify-content: center;
        position: sticky;
        top: var(--space-3);
        left: var(--space-3);
        width: 40px;
        height: 40px;
        margin: var(--space-3);
        border: 1px solid var(--border-color);
        border-radius: var(--btn-border-radius);
        background: var(--primary-background-blur);
        backdrop-filter: var(--background-blur);
        color: var(--primary-font-color);
        z-index: 200;
    }

    #sidebar {
        position: fixed;
        left: 0;
        transform: translateX(-100%);
        box-shadow: 4px 0 24px rgba(0, 0, 0, 0.4);
    }

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

/*
=====================================================
Page containers
=====================================================
*/

.dashboard-container,
.new-form-page,
.new-user-page {
    width: 100%;
    max-width: var(--content-max-width); /* gap fix: was uncapped, stretched edge-to-edge on ultrawide */
    margin-inline: auto;
    padding: var(--space-6);
    display: flex;
    flex-direction: column;
    gap: var(--dashboard-gap);
}

.dashboard-container h2 {
    text-align: left;
    color: var(--primary-font-color);
}

@media (max-width: 480px) {
    .dashboard-container,
    .new-form-page,
    .new-user-page {
        padding: var(--space-4);
    }
}

/*
=====================================================
Cards & metrics
=====================================================
*/

#cards-section {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dashboard-gap);
}

/* gap fix: this had no display/padding/alignment at all — content just
   block-stacked flush against the card edges with no breathing room, and
   `width: 200px` combined with `flex: 1` (which sets flex-basis: 0%) was
   dead weight — flex-basis overrides width, so the number did nothing.
   flex-basis is set explicitly below instead, so the intent (~200px
   default, shrink/grow from there) actually takes effect. */
.metrics-card {
    flex: 1 1 180px;
    min-height: 150px;
    display: flex;
    flex-direction: column;
    align-items: center;
    justify-content: center;
    gap: var(--space-1);
    padding: var(--space-4);
    text-align: center;
}

/* Interactive variant: only cards that are actually links behave like
   something you can click — a plain stat card (div, e.g. the dashboard's
   criticidade blocks) never gets a pointer/lift, that would fake
   affordance that isn't there. */
a.metrics-card {
    text-decoration: none;
    color: inherit;
    cursor: pointer;
}

a.metrics-card:hover {
    transform: translateY(-4px);
    box-shadow: var(--shadow-lg);
    border-color: var(--primary-btn-color);
}

a.metrics-card:active {
    transform: translateY(-1px);
    box-shadow: var(--shadow-md);
}

.metric-value {
    font-size: 44px;
}

.metric-value-sm {
    font-size: 20px;
    font-weight: 600;
    word-break: break-word;
    text-align: center;
    line-height: 1.2;
}

.metric-label {
    color: var(--no-contrast-font-color);
}

/*
=====================================================
Status badge
=====================================================
Generic small pill for an ordinal/severity/status value (open/pending/ok,
critical/warning/info, etc). Uses only the semantic tokens above — don't
reach for the Chart.js categorical palette (chart-palette.md) for this;
that palette is for telling apart unrelated series, not for a status that
has an actual "worse → better" order.
*/

.status-badge {
    display: inline-flex;
    align-items: center;
    padding: 2px var(--space-2);
    border-radius: 999px;
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
}

.status-badge--danger {
    color: var(--danger-color);
    background: color-mix(in srgb, var(--danger-color) 15%, transparent);
}

.status-badge--warning {
    color: var(--warning-color);
    background: color-mix(in srgb, var(--warning-color) 15%, transparent);
}

.status-badge--info {
    color: var(--info-color);
    background: color-mix(in srgb, var(--info-color) 15%, transparent);
}

.status-badge--success {
    color: var(--primary-btn-color);
    background: color-mix(in srgb, var(--primary-btn-color) 15%, transparent);
}

/*
=====================================================
Charts
=====================================================
*/

#graphics-section {
    display: flex;
    flex-wrap: wrap;
    gap: var(--dashboard-gap);
}

/* gap fix: no flex-basis of its own — inside #graphics-section's flex row
   it fell back to sizing off its content (the <canvas> Chart.js drops in),
   which Chart.js can't size sanely before its parent already has a width.
   flex: 1 1 320px gives 2-3 evenly-sized charts per row on desktop and
   collapses to one per row under --bp-md without needing the -width-70/30
   modifiers below (still available when a project wants an explicit
   70/30 split instead of even columns). */
.charts-wrapper {
    display: flex;
    flex-direction: column;
    flex: 1 1 320px;
    gap: 10px;
    padding: 20px 16px;
    height: 300px;
    min-width: 0;
    position: relative;
}

.nota-projecao {
    flex: none;
    font-size: 12px;
    color: var(--no-contrast-font-color);
}

/* Container do gráfico. O ECharts mede o elemento na hora do init e desenha
   com a altura que encontrar — um div sem altura vira um gráfico de 0px.
   `min-height: 0` impede que o conteúdo force o flex item a crescer. */
.chart-box {
    flex: 1;
    min-height: 0;
    width: 100%;
}

/* Gráfico que precisa da linha inteira — barras horizontais com rótulo de
   texto são o caso típico: numa coluna de ~320px o rótulo come a área de
   plotagem e sobra uma tira para a barra. */
.charts-wrapper-full { flex: 1 1 100%; }

.charts-wrapper-width-70 { flex: 7 1 0; }
.charts-wrapper-width-30 { flex: 3 1 0; }

@media (max-width: 768px) {
    #graphics-section {
        flex-direction: column;
    }
    .charts-wrapper-width-70,
    .charts-wrapper-width-30 {
        width: 100%;
        height: 300px;
        flex: none;
    }
}

/*
=====================================================
Tables
=====================================================
*/

#table-section {
    display: flex;
    flex-direction: row;
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: var(--dashboard-gap);
}

/* gap fix: reference implementation forced two 300px-min tables side by side with no
   wrap rule, guaranteeing horizontal page overflow under ~650px */
@media (max-width: 768px) {
    #table-section {
        flex-direction: column;
    }
    #production-table,
    #errors-table {
        width: 100%;
    }
}

#production-table,
#errors-table {
    flex: 1;
    min-width: 0; /* gap fix: was 300px, now the table's own overflow-x handles narrow content */
    width: 50%;
}

.table-shell {
    width: 100%;
    overflow-x: auto;
    border-collapse: collapse;
    border-radius: var(--card-border-radius); /* clips thead's flat corners to match the card it sits in */
}

table {
    width: 100%;
    border-collapse: collapse;
    text-align: left;
    font-size: 13px;
}

/* gap fix: header had 0 vertical padding (`0 12px`) — rows read as cramped
   and flat. Uppercase + letter-spacing + muted color reads as a column
   label rather than body text, the usual modern-dashboard header treatment. */
thead {
    background: color-mix(in srgb, var(--primary-font-color) 5%, transparent);
}

thead th {
    padding: var(--space-3) var(--space-4);
    font-size: 11px;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.04em;
    color: var(--no-contrast-font-color);
    border-bottom: 1px solid var(--border-color);
}

tbody td {
    padding: var(--space-3) var(--space-4);
    font-size: 14px;
}

tbody tr {
    border-bottom: 1px solid var(--border-color);
    transition: background-color 0.15s ease;
}

tbody tr:last-child {
    border-bottom: none;
}

/* Subtle zebra: mixes with the current text color, so it's a faint light
   wash in dark theme and a faint dark wash in light theme automatically —
   no separate per-theme value needed. */
tbody tr:nth-child(even) {
    background: color-mix(in srgb, var(--primary-font-color) 3%, transparent);
}

tbody tr:hover {
    background: var(--row-hover-bg);
}

.no-table-data {
    font-size: 14px;
    color: var(--no-contrast-font-color);
    padding: var(--space-4);
    text-align: center;
}

/* Cabeçalho de coluna ordenável: sem estas regras o <a> dentro do <th>
   sairia sublinhado e na cor de âncora do navegador, destoando do resto do
   cabeçalho (mesma armadilha de sempre — um link só vira "não-link" com
   text-decoration e color explícitos). O ícone de direção acompanha o
   texto via inline-flex, e a coluna ativa ganha contraste maior. */
thead th a {
    display: inline-flex;
    align-items: center;
    gap: var(--space-1);
    color: inherit;
    text-decoration: none;
    cursor: pointer;
}

thead th a:hover {
    color: var(--primary-font-color);
}

thead th[aria-sort] a {
    color: var(--primary-font-color);
}

/* Linha emoldurada pelas bordas do cartão: primeira coluna encostada à
   esquerda, última à direita. Com tudo alinhado à esquerda, o conteúdo da
   última coluna flutua no meio de uma fatia larga e a linha parece
   terminar antes da borda. O `:not(.no-table-data)` preserva o estado
   vazio, que é centralizado de propósito. */
thead th:last-child,
tbody td:last-child:not(.no-table-data) {
    text-align: right;
}

/* Coluna de ações (botões de linha): `width: 1%` é o truque clássico do
   layout automático de tabela para a coluna encolher até o conteúdo — toda
   a largura sobrando vai para as colunas de dados, em vez de a coluna de
   ações herdar uma fatia larga. */
th.col-actions,
td.col-actions {
    width: 1%;
    white-space: nowrap;
}

/*
=====================================================
Forms & inputs
=====================================================
*/

input, select, textarea, button {
    font-family: var(--font-family);
}

/* Formulário centralizado: sem um teto de largura, um form de 2 campos
   esticava de ponta a ponta do container da página. A largura segue a
   proporção do cartão de login — coluna única, campo de ponta a ponta. */
.forms-container {
    width: 100%;
    max-width: 520px;
    margin-inline: auto;
}

/* Layout padrão de formulário: uma coluna, cada campo embaixo do anterior,
   igual à tela de login. É o oposto de .form-grid (colunas auto-fit): um
   formulário lido de cima para baixo, sem o olho ter que pular entre
   colunas, e sem campo espremido em coluna estreita. */
.form-stack {
    display: flex;
    flex-direction: column;
}

/* gap fix: `input` aqui PRECISA excluir checkbox/radio. Esta regra dá
   width:100% + height:var(--btn-input-height) — correto para um campo de
   texto, mas transforma um checkbox numa caixa gigante esticada (o tamanho
   é a identidade visual do controle). Pelo mesmo motivo o `padding-inline`
   é explícito: o reset global `* { padding: 0 }` mata o padding nativo do
   input, então sem isto o texto digitado encosta na borda. */
.forms-container select,
.forms-container textarea,
.forms-container input:not([type="checkbox"]):not([type="radio"]),
.login-form input {
    color: var(--primary-font-color);
    background-color: var(--primary-input-color);
    border: 1px solid var(--border-color);
    border-radius: var(--input-border-radius);
    text-align: left;
    font-size: medium;
    width: 100%;
    height: var(--btn-input-height);
    padding-inline: var(--space-3);
}

.forms-container textarea {
    padding-block: var(--space-2);
}

/* Checkbox/radio mantêm o tamanho intrínseco — nunca esticados à largura
   do campo. `accent-color` pinta o estado marcado com o verde da marca nos
   dois temas, sem precisar desenhar um controle customizado. */
.forms-container input[type="checkbox"],
.forms-container input[type="radio"] {
    flex: none;
    width: 18px;
    height: 18px;
    accent-color: var(--primary-btn-color);
    cursor: pointer;
}

/* gap fix: the reference implementation had no multi-field form layout at
   all — every field (each wrapped in its own <p> by e.g. Django's
   form.as_p, or an equivalent per-field wrapper in another stack) stacked
   in one full-width column, so anything past ~5 fields became a long
   skinny scroll with lots of dead horizontal space on desktop. Put this
   class on the <form> (or a wrapping element around the field wrappers)
   inside .forms-container; each field wrapper becomes a grid cell. Add
   .full-width to a field wrapper or the submit button to span every
   column (long text areas, the submit action). */
.form-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
    gap: 0 var(--space-5);
    align-items: start;
}

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

/* gap fix: this class is used on both <button> and <a> (e.g. "Novo X" list
   actions). A <button> defaults to display:inline-block with no underline,
   so width/height/text-align "just worked" there — but an <a> defaults to
   display:inline, which ignores width/height and keeps the browser's
   default underline. The result on an <a>: an unstyled underlined text
   fragment instead of a button. display:flex + text-decoration:none make
   this render identically on both elements — never rely on width+text-align
   alone to center a button's label, it only works on block-level boxes.
   `padding-inline` is explicit and unconditional for the same kind of
   reason: this class was written assuming it's always width:100% (the
   parent's width gives it visual breathing room "for free"), so it never
   needed its own horizontal padding — until a context (.list-toolbar,
   .form-actions) overrides it back to content-sized width, at which point
   a button with zero padding hugs its own label with no room at all. Never
   let a button's presence depend on the width mode it happens to run in. */
.btn-primary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    height: var(--btn-input-height);
    padding-inline: var(--space-4);
    border: none;
    border-radius: var(--btn-border-radius);
    color: white;
    background: var(--primary-btn-color);
    font-size: var(--btn-font-size);
    font-weight: var(--btn-font-weight);
    text-align: center;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
    box-shadow: 0 2px 8px color-mix(in srgb, var(--primary-btn-color) 35%, transparent);
    transition: background-color 0.2s ease, transform 0.15s ease, box-shadow 0.2s ease;
}

.btn-primary:hover {
    background-color: var(--primary-btn-color-hover);
    transform: translateY(-1px);
    box-shadow: 0 4px 14px color-mix(in srgb, var(--primary-btn-color) 45%, transparent);
}

.btn-primary:active {
    transform: translateY(0);
    box-shadow: 0 2px 8px color-mix(in srgb, var(--primary-btn-color) 35%, transparent);
}

/* Same height/type treatment as .btn-primary (var(--btn-input-height),
   uppercase, same font weight) so a "Salvar"/"Cancelar" pair reads as one
   matched set — only the fill differs (outlined/muted vs filled/brand).
   Sized to its own content (no width:100%): pair it with .btn-primary
   inside a flex row (see .form-actions), never stack it full-width. */
.btn-secondary {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    height: var(--btn-input-height);
    padding-inline: var(--space-4);
    border: 1px solid var(--border-color);
    border-radius: var(--btn-border-radius);
    background: var(--primary-input-color);
    color: var(--primary-font-color);
    font-size: var(--btn-font-size);
    font-weight: var(--btn-font-weight);
    text-align: center;
    text-transform: uppercase;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

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

/* Botões do formulário seguem o mesmo empilhamento vertical dos campos
   (padrão da tela de login): um embaixo do outro, ambos ocupando a largura
   inteira — largura idêntica sai de graça, sem depender do tamanho do
   rótulo de cada um. */
.form-actions {
    display: flex;
    flex-direction: column;
    gap: var(--space-3);
}

.form-actions > .btn-primary,
.form-actions > .btn-secondary {
    width: 100%;
}

/* Modificador de tamanho, não um botão novo: combina com .btn-secondary (e com
   .btn-danger) para a ação de linha que precisa de rótulo em texto. O
   .btn-secondary cru é o par Salvar/Cancelar de formulário — 32px e
   `--btn-font-size: small` —, e dentro de um <td> ele sai mais alto que o
   .btn-icon das outras listagens e com fonte maior que o badge de status ao
   lado. */
.btn-sm {
    height: var(--btn-sm-height);
    padding-inline: var(--space-3);
    font-size: var(--btn-sm-font-size);
}

/* Larguras iguais sem min-width chutado: `td.col-actions` já é `width: 1%` +
   `white-space: nowrap`, então a coluna encolhe até o rótulo mais largo de
   TODAS as linhas; `width: 100%` faz cada botão preencher essa mesma largura.
   Sem isto, "REABRIR TRATAMENTO" e "MARCAR COMO ABERTA" saem em caixas de
   tamanhos diferentes linha a linha (mesma armadilha do par
   SALVAR/CANCELAR — largura de conteúdo nunca alinha dois rótulos). */
td.col-actions .btn-sm {
    width: 100%;
}

/* Icon-only row action (e.g. a table's "Editar"/"Excluir") — square,
   compact, always centered by construction since there's no text to
   misalign or underline. Give the <a>/<button> an aria-label (and usually
   a `title` for the hover tooltip) since the icon alone has no accessible
   name. */
.btn-icon {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 28px;
    height: 28px;
    padding: 0;
    border: 1px solid var(--border-color);
    border-radius: var(--btn-border-radius);
    background: var(--primary-input-color);
    color: var(--primary-font-color);
    font-size: 14px;
    text-decoration: none;
    cursor: pointer;
    transition: background-color 0.2s ease, border-color 0.2s ease, color 0.2s ease;
}

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

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

.btn-secondary.btn-danger:hover,
.btn-icon.btn-danger:hover {
    border-color: var(--danger-color);
    background: color-mix(in srgb, var(--danger-color) 12%, transparent);
    color: var(--danger-color);
}

/* Row actions (e.g. "Editar"/"Excluir" in a table) sit side by side, never
   full-width like a toolbar's "Novo X" button. */
.row-actions {
    display: inline-flex;
    align-items: center;
    gap: var(--space-2);
}

.field-errors {
    color: var(--error-text-color);
    font-size: 12px;
    margin-top: 4px;
}

/*
=====================================================
Search bar
=====================================================
*/

.search-bar-input {
    position: relative;
    display: flex;
    align-items: center;
    width: 100%;
    max-width: 450px; /* gap fix: was `min-width: 450px`, overflowed any viewport under ~480px */
    height: var(--btn-input-height);
    border: 1px solid var(--border-color);
    border-radius: var(--input-border-radius);
    background-color: var(--primary-input-color);
}

.search-bar-input input {
    width: 100%;
    height: 100%;
    border: none;
    outline: none;
    padding-inline: 32px 32px;
    background-color: transparent;
    color: var(--primary-font-color);
}

.search-bar-input input::placeholder {
    color: var(--no-contrast-font-color);
}

.search-bar-input i {
    position: absolute;
    left: var(--space-3);
    color: var(--no-contrast-font-color);
    pointer-events: none;
}

/*
=====================================================
List toolbar & pagination (telas de cadastro do gestor)
=====================================================
*/

.list-toolbar {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    justify-content: space-between;
    gap: var(--space-3);
    margin-bottom: var(--space-4);
}

/* Ação de criar sempre primeiro (à esquerda), busca/filtros encostados à
   direita — mesmo arranjo em toda listagem, com ou sem filtro. `order: -1`
   põe o botão na frente sem depender da ordem do markup (no HTML ele vem
   depois do form, que precisa ficar junto dos campos que submete).
   O `width: auto` é necessário porque .btn-primary nasce width:100% para o
   seu uso normal (submit de formulário) — como item flex isso vira o
   flex-basis e o botão tomaria a linha inteira. */
.list-toolbar .btn-primary {
    order: -1;
    width: auto;
}

/* Busca + selects de filtro + ações, num form GET só (ver comentário no
   template: forms separados perdem o querystring um do outro). Cada campo
   vem envolto num <label>, mesma estrutura da barra de filtros do painel.
   `justify-content: flex-end` é o que encosta a barra na borda direita:
   como o form cresce (flex: 1) para ocupar o espaço que sobra do botão,
   sem isso os controles ficariam alinhados à esquerda dentro dele, com um
   vão vazio à direita. */
.list-filters {
    display: flex;
    flex: 1;
    flex-wrap: wrap;
    align-items: flex-end;
    justify-content: flex-end;
    gap: var(--space-3);
}

/* Com filtros, os selects têm rótulo em cima e a barra fica mais alta que o
   botão — alinhar pela base deixa botão e campos na mesma linha de apoio,
   em vez de o botão flutuar no meio da altura. */
.list-toolbar:has(.filter-select) {
    align-items: flex-end;
}

.list-filters label {
    display: flex;
    flex-direction: column;
    gap: var(--space-1);
    font-size: var(--label-font-size);
    font-weight: var(--label-font-weight);
    color: var(--no-contrast-font-color);
}

/* Dentro da barra de filtros a busca abre mão do seu max-width de 450px e
   os selects ganham teto: com o botão de criar mais 6 controles na mesma
   linha, um único campo largo (nomes de setor são compridos) já empurra
   "Limpar" para uma segunda linha. */
.list-filters .search-bar-input {
    flex: 1 1 160px;
    max-width: 240px;
}

.list-filters label {
    min-width: 0;
}

.list-filters .filter-select {
    height: var(--btn-input-height);
    max-width: 160px;
}

/* O bloco acima nunca alcançou .filter-input: numa barra de filtros o campo
   de texto/número ficava sem altura (mais baixo que os selects ao lado) e com
   a largura padrão que o browser dá a input[type=number] — cerca de 20
   caracteres para um ano de 4 dígitos, ocupando o espaço de um select inteiro
   e empurrando "Limpar" para uma segunda linha. */
.list-filters .filter-input {
    height: var(--btn-input-height);
    max-width: 160px;
}

/* Ano tem 4 dígitos: deixar o campo com a largura de um select (160px) o faria
   parecer vazio, e a largura padrão do browser (~20 caracteres) o faz parecer
   um campo de texto solto no meio dos filtros. Largura fixa, altura e raio
   iguais aos dos selects — é a altura que fazia o campo destoar da fileira. */
.list-filters .filter-input[type="number"] {
    width: 96px;
    flex: none;
}

.list-filters .btn-filter-apply,
.list-filters .btn-filter-clear {
    flex: none;
    height: var(--btn-input-height);
    padding-inline: var(--space-4);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    text-decoration: none;
}

.paginacao {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: var(--space-4);
    margin-top: var(--space-4);
    font-size: 14px;
    color: var(--primary-font-color);
}

.paginacao a {
    color: var(--primary-btn-color);
    text-decoration: none;
}

.paginacao a:hover {
    text-decoration: underline;
}

/*
=====================================================
Sidebar filters
=====================================================
*/

.filter-select,
.filter-input {
    padding: 4px;
    border: 1px solid var(--border-color);
    border-radius: var(--btn-border-radius);
    background: var(--primary-input-color);
    color: var(--primary-font-color);
    font-size: 12px;
    box-sizing: border-box;
}

.filter-select:focus,
.filter-input:focus {
    outline: none;
    border-color: var(--primary-btn-color);
    background: var(--background-hover);
}

.filter-input[type="date"],
.filter-input[type="month"] {
    color-scheme: var(--input-color-scheme);
}

.btn-filter-apply,
.btn-filter-clear {
    flex: 1;
    padding: var(--space-2);
    border: none;
    border-radius: var(--btn-border-radius);
    font-size: 12px;
    cursor: pointer;
    text-align: center;
    transition: all 0.2s;
}

.btn-filter-apply {
    background: var(--info-color);
    color: #efefef;
}
.btn-filter-apply:hover { background: var(--info-color-hover); }

.btn-filter-clear {
    background: var(--primary-input-color);
    color: var(--no-contrast-font-color);
}
.btn-filter-clear:hover {
    background: var(--background-hover);
    color: var(--primary-font-color);
}

/*
=====================================================
Login page
=====================================================
*/

.login-page {
    display: flex;
    min-height: 100vh;
    justify-content: center;
    align-items: center;
    padding: var(--space-4);
}

.login-card {
    text-align: center;
    width: 100%;
    max-width: 350px;
    padding: 20px 12px;
}

.login-card > * {
    margin-bottom: var(--space-4);
}

#phd-logo {
    width: 50px;
    height: 69px;
}
