:root {
  /* ── Brand ──────────────────────────────────────────────────────────────
     Contrast ratios measured against every surface this token appears on.
     Update the annotation whenever the value changes.                      */
  --color-brand-0:       #3D5C0A;   /* dark green  — primary brand — 6.82:1 on s-0 · 7.68:1 on white */
  --color-brand-1:       #486B13;   /* mid green   — links/interactive — 5.50:1 on s-0 · 6.20:1 on white */
  --color-brand-2:       #A8C96B;   /* light green — tint/highlight (use on dark bg only) */

  /* ── Surfaces ────────────────────────────────────────────────────────── */
  --color-surface-0:     #F0F2EE;   /* page base — deepest layer    */
  --color-surface-1:     #F7F8F5;   /* cards, panels                */
  --color-surface-2:     #FFFFFF;   /* elevated elements, overlays  */
  --color-surface-header: #EDF6FA;  /* soft blue wash — header only */

  /* ── Content ─────────────────────────────────────────────────────────── */
  --color-content:       #1A2B1A;   /* near-black — 13.3:1 on s-0 · 14.0:1 on s-1 · 14.9:1 on white (AAA) */
  --color-content-muted: #4A5E50;   /* muted — 6.20:1 on s-0 · 6.55:1 on s-1 · 6.98:1 on white (AA) */

  --color-separators:    #D4DAD0;   /* decorative only — not used for text */

  /* ── Accents ─────────────────────────────────────────────────────────── */
  --color-accent-0:      #5B7FA6;   /* corporate blue — verify contrast before use as text */
  --color-accent-1:      #8A6D3B;   /* warm amber     — verify contrast before use as text */
  --color-accent-2:      #C0392B;   /* error red      — 5.44:1 on white · 4.68:1 on #FCEAE8 badge bg */
  --color-accent-4:      #7F55CC;   /* purple (from Cintia palette) — 5.15:1 on white (AA) */
  --color-accent-5:      #B85A00;   /* burnt orange (from Cintia palette) — 4.67:1 on white (AA) */

  /* ── Badge token pairs ────────────────────────────────────────────────
     Each badge must declare both text and bg tokens as a pair.
     Contrast ratio for the pair is annotated on the bg token.             */
  --color-badge-ok-text:      #3D5C0A;   /* = brand-0 */
  --color-badge-ok-bg:        #C2DB88;   /* 5.04:1 with ok-text */
  --color-badge-pending-text: #5C5044;   /* warm dark brown */
  --color-badge-pending-bg:   #EDE8DF;   /* 6.41:1 with pending-text */

  /* Gradients — kept but subdued */
  --gradient-0: linear-gradient(135deg, #3D5C0A 0%, #5A8018 100%);
  --gradient-1: linear-gradient(135deg, #5B7FA6 0%, #3D5C7A 100%);
  --gradient-2: linear-gradient(135deg, #D6CFBF 0%, #8A7E6E 100%);
}

/********************************* CSS Reset **********************************/
*, *::before, *::after { margin: 0; padding: 0; box-sizing: border-box; }

article, aside, details, figcaption, figure,
footer, header, hgroup, menu, nav, section { display: block; }

/********************************* Base ***************************************/
body {
  font-family: "Inter", "Helvetica Neue", "Arial", system-ui, -apple-system, sans-serif;
  font-size: 16px;
  line-height: 1.6;
  background: var(--color-surface-0);
  color: var(--color-content);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

a        { color: var(--color-brand-1); text-decoration: none; }
a:hover  { color: var(--color-brand-0); text-decoration: underline; }
ul       { list-style: none; }

/* <time> elements always render dates/times — tabular-nums keeps digits stable
   when multiple timestamps appear in a list or table. */
time { font-variant-numeric: tabular-nums; }

dl {
  display: grid;
  grid-template-columns: max-content auto;
  gap: 2px 20px;
  margin-bottom: 16px;
}

dt {
  grid-column-start: 1;
  font-size: 0.8rem;
  font-weight: 600;
  letter-spacing: 0.05em;
  text-transform: uppercase;
  color: var(--color-content-muted);
  padding-block: 7px;
  white-space: nowrap;
}

dd {
  grid-column-start: 2;
  font-size: 0.9375rem;
  line-height: 1.5;
  color: var(--color-content);
  padding-block: 7px;
  border-bottom: 1px solid var(--color-separators);
  /* NOTE: tabular-nums NOT applied globally here — dd content is often plain text
     (names, descriptions). Add font-variant-numeric: tabular-nums in the
     page-specific stylesheet for <dl> blocks that show amounts or dates, e.g.:
       dl:has(dt:first-child) dd { font-variant-numeric: tabular-nums; }
     or target the specific dl by id/context selector. */
}

dt:last-of-type + dd { border-bottom: none; }

/********************************* Layout *************************************/
/* Default: nav collapsed to icon-only strip (48px).                         */
/* .nav_open class widens it — toggled by JS on load and on resize.          */
body {
  /* One source of truth for the header's height: the grid row, the sticky
     offsets and the mobile dropdown position all read it, so the two-row
     header below 900px (see Responsive) moves them together. */
  --header-height: 60px;

  display: grid;
  grid-template-columns: 48px 1fr;
  grid-template-rows: var(--header-height) 1fr;
  grid-template-areas:
    "nav header"
    "nav main";
  min-height: 100vh;
  transition: grid-template-columns 0.25s ease;
}

body.nav_open {
  grid-template-columns: 220px 1fr;
}

/********************************* Header *************************************/
body > header {
  grid-area: header;
  display: flex;
  align-items: center;
  gap: 16px;
  padding: 0 24px 0 16px;
  background: var(--color-surface-header);
  border-bottom: 1px solid #C8D8E2;  /* blue-tinted separator — matches surface-header */
  overflow: visible;
  position: relative;
  z-index: 10;
  transition: border-bottom-color 0.18s ease;

  /* A grid item defaults to min-width: auto, i.e. "never smaller than my
     content". The header and <main> share the second grid column, so the
     widest unbreakable thing in EITHER of them silently became the minimum
     width of BOTH — a long header subtitle was pushing report content off the
     right edge of the viewport. Opting both out of auto is what stops the next
     wide element from doing the same. */
  min-width: 0;
}

/* Page title — left-aligned, takes up remaining space */
body > header hgroup {
  flex: 1;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: 12px;
  overflow: hidden;
}

body > header hgroup h1 {
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.01em;
  color: var(--color-content);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

body > header hgroup p {
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--color-content-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;

  /* The subtitle must be allowed to vanish rather than to push. It is the one
     header element whose length is unbounded — "Safra 2025/26 · comparado com
     2024/25" — and with min-width: auto it set the header's minimum width,
     which the grid then handed to <main> as well (the two share a column). The
     result was a page that scrolled sideways at 1265px because of its own
     subtitle. Truncation here plus min-width: 0 below is the actual fix; the
     breakpoint ladder further down is only the presentation.
     See doc/design/interface-system.org §3.1. */
  min-width: 0;
  flex-shrink: 1;
}

/****************************** Activity switcher *****************************/
/* Same <details> pill as the period filter (shared .hdr_pill), with its own    */
/* compact dropdown panel of option links. Positioned like the period panel —   */
/* absolute against body > header (top = header height) — but right-aligned and  */
/* only as wide as its options, rather than full-bleed.                          */
#activity_filter {
  flex-shrink: 0;
  position: relative;
}

#activity_filter[open] > summary {
  border-color: var(--color-brand-0);
  color: var(--color-brand-0);
}

#activity_filter[open] > summary .pf_chevron {
  transform: rotate(180deg);
  opacity: 0.9;
}

#activity_filter_panel {
  right: 0;
  min-width: 216px;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.hdr_panel_label {
  padding: 6px 12px 4px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-content-muted);
}

.af_option {
  display: block;
  padding: 7px 12px;
  border-radius: 7px;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--color-content);
  white-space: nowrap;
  text-decoration: none;
}

.af_option:hover {
  background: color-mix(in srgb, var(--color-brand-2) 18%, transparent);
  color: var(--color-brand-0);
}

.af_active {
  background: color-mix(in srgb, var(--color-brand-2) 30%, transparent);
  color: var(--color-brand-0);
  font-weight: 700;
}

/********************************* Period filter ******************************/
/* Everything lives inside <details id="period_filter"> in body > header.    */
/* <summary> is the pill.  The panel uses position:fixed so it escapes the   */
/* header grid row and paints above all other content.                       */

/* ── <details> container ──────────────────────────────────────────────────── */
/* No position:relative here — the panel's containing block must be the      */
/* header (body > header, which has position:relative), not the <details>.   */
/* This lets position:absolute on the panel escape the ::details-content     */
/* containment slot that modern browsers use internally.                      */
#period_filter {
  flex-shrink: 0;
  position: relative;
}

/* ── Summary / pill (shared by the period and activity switchers) ─────────── */
.hdr_pill {
  display: flex;
  align-items: center;
  gap: 6px;
  padding: 5px 14px;
  border: 1.5px solid #C8D8E2;  /* blue-tinted border — matches surface-header */
  border-radius: 99px;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-content-muted);
  cursor: pointer;
  list-style: none;
  user-select: none;
  white-space: nowrap;
  background: var(--color-surface-2);  /* white pill floats on the blue header surface */
  transition: border-color 0.15s, color 0.15s, background 0.15s,
              border-radius 0.15s;
}

.hdr_pill::-webkit-details-marker { display: none; }

.hdr_pill .icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  opacity: 0.75;
}

/* The value carries the weight; the label in front of it stays quiet. */
.hdr_pill .pf_dates,
.hdr_pill .hdr_pill_value {
  display: flex;
  align-items: center;
  gap: 5px;
  font-weight: 700;
  color: var(--color-content);
}

.hdr_pill_prefix { font-weight: 500; }

.hdr_pill .pf_dates span { opacity: 0.5; }

.hdr_pill .pf_chevron {
  font-size: 0.7rem;
  display: inline-block;
  opacity: 0.6;
  transition: transform 0.2s ease;
}

.hdr_pill:hover {
  border-color: var(--color-brand-1);
  color: var(--color-brand-1);
  background: var(--color-surface-2);
}

/* Open state — still a pill. The panel below is its own floating card, so
   there is no seam to join and no reason to square the corners off.          */
#period_filter[open] summary,
#activity_filter[open] > summary {
  position: relative;
  z-index: 9999;
  border-color: var(--color-brand-0);
  background: var(--color-surface-2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-brand-0) 14%, transparent);
}

#period_filter[open] summary .icon,
#activity_filter[open] > summary .icon { opacity: 1; }

/* A period the farmer chose, rather than the one the app defaulted to. Same
   treatment as the activity pill — tinted ground, coloured label, border — in
   brand green rather than amber, since this one frames the page and hides
   nothing. The × being there at all carries the state too, so it never rides
   on colour alone.
   #3D5C0A on a 10% tint of itself over white — 6.9:1 ✅ AAA                  */
.hdr_pill[data-state="chosen"] {
  border-color: var(--color-brand-0);
  background: color-mix(in srgb, var(--color-brand-0) 10%, var(--color-surface-2));
  color: var(--color-brand-0);
}

.hdr_pill[data-state="chosen"] .pf_dates { color: var(--color-brand-0); }

/* The activity switcher while it is filtering: loud, because it hides rows.
   #8A6D3B on a 10% tint of itself — ≥4.5:1 ✅ AA                              */
.hdr_pill[data-state="filtering"] {
  border-color: var(--color-accent-1);
  background: color-mix(in srgb, var(--color-accent-1) 10%, var(--color-surface-2));
  color: var(--color-accent-1);
}

.hdr_pill[data-state="filtering"] .hdr_pill_value { color: var(--color-accent-1); }
.hdr_pill[data-state="filtering"] .icon { opacity: 1; }

/* Shared by both pills; the colour comes from whichever state the pill is in,
   so the control is one thing and its severity is not. */
.pill_x {
  display: inline-grid;
  place-items: center;
  width: 17px;
  height: 17px;
  border-radius: 50%;
  background: color-mix(in srgb, currentColor 18%, transparent);
  font-size: 0.72rem;
  line-height: 1;
  text-decoration: none;
}

.pill_x:hover { text-decoration: none; }

.hdr_pill[data-state="chosen"] .pill_x    { color: var(--color-brand-0); }
.hdr_pill[data-state="filtering"] .pill_x { color: var(--color-accent-1); }

/* Filled on hover, with the glyph knocked out. Set per state rather than via
   currentColor: filling with currentColor and then lightening the glyph would
   lighten the fill along with it. */
.hdr_pill[data-state="chosen"] .pill_x:hover {
  background: var(--color-brand-0);
  color: var(--color-surface-2);
}

.hdr_pill[data-state="filtering"] .pill_x:hover {
  background: var(--color-accent-1);
  color: var(--color-surface-2);
}

#period_filter[open] summary .pf_chevron {
  transform: rotate(180deg);
  opacity: 0.9;
}

/* ── Dropdown panel ───────────────────────────────────────────────────────── */
/* position:absolute escapes the details content slot.  top:60px is a fixed  */
/* value (= header height) because top:100% would resolve against the        */
/* ::details-content containing block, not the header.                       */
/* left:0 + right:0 spans the full header width.  The header has             */
/* overflow:visible and z-index:10, so the panel paints above <main>.        */
/* Both dropdowns are the same object: a card that floats clear of the page
   rather than a band welded to the header. Two shadows — a wide soft one for
   the lift and a tight dark one for the edge — so the card reads as separate
   from whatever is behind it instead of blending into the surface below.     */
.hdr_panel {
  position: absolute;
  top: calc(100% + 9px);
  z-index: 9998;
  background: var(--color-surface-2);
  border: 1px solid var(--color-separators);
  border-radius: 12px;
  box-shadow:
    0 18px 40px -12px rgba(23, 43, 23, 0.28),
    0 2px 6px rgba(23, 43, 23, 0.08);
}

#period_filter_panel {
  right: 0;
  width: min(680px, calc(100vw - 32px));
  padding: 14px;
}

/* The pill's dot, in words. Only present when there is a choice to explain, so
   it never becomes furniture the farmer reads past. */
.pf_memory {
  display: flex;
  align-items: flex-start;
  gap: 9px;
  margin-bottom: 10px;
  padding: 11px 13px;
  border-radius: 9px;
  background: color-mix(in srgb, var(--color-brand-2) 16%, var(--color-surface-2));
  border: 1px solid color-mix(in srgb, var(--color-brand-0) 22%, transparent);
  font-size: 0.8125rem;
  line-height: 1.45;
  color: var(--color-content);
}

.pf_memory .icon {
  width: 16px;
  height: 16px;
  flex-shrink: 0;
  margin-top: 2px;
  color: var(--color-brand-0);
}

/* :not(.icon) matters — icon_tag renders a <span class="icon">, so a bare
   `span` selector hands the icon flex-grow and it shoves the text across. */
.pf_memory > span:not(.icon) { flex: 1; min-width: 0; }

.pf_memory a {
  font-weight: 700;
  white-space: nowrap;
}

/* Sections become visible groups: each picker sits on its own tinted block, so
   "Período" and "Comparar com" read as two things instead of one long form.  */
.pf_group {
  padding: 12px;
  border-radius: 9px;
  background: var(--color-surface-1);
  border: 1px solid var(--color-separators);
}

.pf_group + .pf_group { margin-top: 10px; }

/* ── Dimension switch + single-selection options ──────────────────────────── */
/* A row of underline tabs (Safra / Ano / Mês) reveals ONE picker at a time.   */
/* Only one option chip is filled green, so the active selection is unambiguous.*/
.pf_tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 12px;
  border-bottom: 1px solid var(--color-separators);
}

.pf_tab {
  padding: 6px 12px 8px;
  font-size: 0.8125rem;
  font-weight: 600;
  color: var(--color-content-muted);
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}

.pf_tab input[type=radio] { position: absolute; opacity: 0; pointer-events: none; }

.pf_tab:hover { color: var(--color-brand-1); }

.pf_tab:has(input:checked) {
  color: var(--color-brand-0);
  border-bottom-color: var(--color-brand-0);
}

/* Only the active dimension's picker is shown. */
.pf_dim { display: none; }
.pf_dim.pf_dim_active { display: block; }

.pf_chips {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}

/* Scoped under #period_filter_panel so these out-specify the generic
   `form button` / `form button:last-child` (primary-button) rules below — the
   chips are <button>s inside the filter form and must NOT inherit that green
   fill (otherwise the last chip in each row renders as if selected). */
#period_filter_panel .pf_chip {
  display: inline-flex;
  align-items: center;
  padding: 5px 16px;
  border-radius: 99px;
  border: 1.5px solid var(--color-separators);
  font-family: inherit;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--color-content-muted);
  background: var(--color-surface-2);
  cursor: pointer;
  white-space: nowrap;
  text-decoration: none;
  transition: border-color 0.12s, background 0.12s, color 0.12s;
}

#period_filter_panel .pf_chip:hover {
  border-color: var(--color-brand-1);
  color: var(--color-brand-1);
  background: var(--color-surface-2);
  text-decoration: none;
}

#period_filter_panel .pf_chip.pf_active {
  background: var(--color-brand-0);
  border-color: var(--color-brand-0);
  color: #fff;
  font-weight: 600;
}

#period_filter_panel .pf_chip.pf_active:hover {
  background: var(--color-brand-1);
  border-color: var(--color-brand-1);
}

.pf_month {
  display: flex;
  align-items: center;
  gap: 6px;
}

.pf_month select {
  padding: 5px 8px;
  border-radius: 6px;
  border: 1.5px solid var(--color-separators);
  background: var(--color-surface-2);
  color: var(--color-content);
  font: inherit;
  font-size: 0.8125rem;
}

.pf_section_label {
  display: block;
  margin-bottom: 8px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-content-muted);
}

/* Opt-in comparison toggle — reveals a second picker. */
.pf_compare_toggle {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 10px 0;
  padding: 9px 12px;
  border-radius: 9px;
  background: var(--color-surface-1);
  border: 1px solid var(--color-separators);
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--color-content);
  cursor: pointer;
}

.pf_compare_toggle:hover { border-color: var(--color-brand-1); }

.pf_hint {
  margin: 4px 0;
  font-size: 0.8rem;
  color: var(--color-accent-1);
}

/* Actions read as the card's footer: a rule the full width of the card, with
   the destructive-ish "Limpar" pushed away from the two buttons.             */
.pf_foot {
  display: flex;
  align-items: center;
  gap: 8px;
  margin: 12px -14px -14px;
  padding: 12px 14px;
  border-top: 1px solid var(--color-separators);
  background: var(--color-surface-1);
  border-radius: 0 0 11px 11px;
}

/* Scoped so they out-specify the generic form-button rules. */
#period_filter_panel .pf_apply {
  padding: 6px 18px;
  border-radius: 6px;
  border: 1.5px solid var(--color-brand-0);
  background: var(--color-brand-0);
  color: #fff;
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
}

#period_filter_panel .pf_apply:hover {
  background: var(--color-brand-1);
  border-color: var(--color-brand-1);
}

#period_filter_panel .pf_apply:disabled {
  opacity: 0.45;
  cursor: not-allowed;
}

#period_filter_panel .pf_close {
  padding: 6px 16px;
  border-radius: 6px;
  border: 1.5px solid var(--color-separators);
  background: var(--color-surface-2);
  color: var(--color-content-muted);
  font: inherit;
  font-size: 0.8125rem;
  font-weight: 600;
  cursor: pointer;
}

#period_filter_panel .pf_close:hover {
  border-color: var(--color-brand-1);
  color: var(--color-brand-1);
}

.pf_clear {
  margin-left: auto;
  font-size: 0.8125rem;
  color: var(--color-content-muted);
  text-decoration: none;
}

.pf_clear:hover {
  color: var(--color-accent-2);
  text-decoration: underline;
}

/* Hide the hidden period inputs — visually irrelevant */
#period_filter_panel input[type=hidden] { display: none; }

/* ── Mobile (≤768px): pill collapses to calendar icon ────────────────────── */
@media (max-width: 768px) {
  .hdr_pill {
    padding: 5px 10px;
    gap: 5px;
  }

  /* The value stays — the window you are looking at is the one thing the pill
     must always say. It is the label in front of it that goes. */
  .hdr_pill_prefix { display: none; }

  /* Anchored to the header rather than to the pill: at this width the card is
     wider than the control that opens it, so hanging it off the pill's edge
     pushes it off-screen.

     Dropping the pills' own positioning context is what makes `top: 100%`
     resolve against body > header instead — so the panel follows the header's
     real bottom edge however many rows it wrapped to, which a fixed pixel
     offset could not do once the header stopped being exactly 60px tall. */
  #period_filter,
  #activity_filter { position: static; }

  .hdr_panel {
    position: absolute;
    top: calc(100% + 6px);
    left: 12px;
    right: 12px;
    width: auto;
  }

  #period_filter_panel { width: auto; }
}

/********************************* Sidebar nav ********************************/
body > nav:not(#mobile_actions) {
  grid-area: nav;
  background: var(--color-brand-0);
  border-right: none;
  display: flex;
  flex-direction: column;

  /* Stick the sidebar to the viewport so it never scrolls away.
     height: 100vh + overflow-y: auto lets the nav scroll independently
     if its own content is taller than the screen (unlikely but safe). */
  position: sticky;
  top: 0;
  height: 100vh;
  overflow-y: auto;
}

/* Logo link */
body > nav > a {
  display: flex;
  align-items: center;
  gap: 10px;
  height: 60px;
  padding: 0 20px;
  background: rgba(0, 0, 0, 0.15);
  color: var(--color-surface-2);
  font-size: 1.05rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  white-space: nowrap;
  overflow: hidden;
  border-bottom: 1px solid rgba(255,255,255,0.08);
}

body > nav > a:hover {
  background: rgba(0, 0, 0, 0.2);
  color: var(--color-surface-2);
}

body > nav > a .logo {
  flex-shrink: 0;
  width: 36px;
  height: 36px;
  object-fit: contain;
}

body.nav_open > nav > a .logo {
  margin-left: -6px;
}

/* Nav sections */
body > nav section {
  padding: 14px 0 2px;
}

/* Section label row — flex so toggle button sits inline */
body > nav section h2 {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 4px 14px;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  /* 0.76 alpha = 5.23:1 contrast on brand-0 sidebar — passes AA */
  color: rgba(255,255,255,0.76);
  white-space: nowrap;
  overflow: hidden;
}

/* Toggle button inside first section's <h2> */
body > nav section h2 button {
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  background: rgba(255,255,255,0.1);
  border: none;
  border-radius: 4px;
  color: rgba(255,255,255,0.7);
  cursor: pointer;
  line-height: 1;
  transition: background 0.15s;
}

body > nav section h2 button:hover { background: rgba(255,255,255,0.18); }

/* Rotate the chevron-right icon 180° when the nav is open (= chevron-left) */
body > nav section h2 button .icon {
  --icon-size: 0.9rem;
  transition: transform 0.2s ease;
}
body.nav_open nav section h2 button .icon { transform: rotate(180deg); }
body.nav_open nav section h2                  { justify-content: space-between; }
body.nav_open nav section h2 button           { order: 1; }

/* Nav links — white at full opacity = 7.68:1 on sidebar bg; meets AAA */
body > nav section ul li a {
  display: flex;
  align-items: center;
  gap: 10px;
  padding: 9px 18px;
  font-size: 0.9rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.88);
  border-left: 3px solid transparent;
  transition: background 0.12s, color 0.12s;
  white-space: nowrap;
  overflow: hidden;
  line-height: 1.4;
}

body > nav section ul li a .icon {
  flex-shrink: 0;
  --icon-size: 1rem;
  width: 20px;
}

/* The rail is a fixed 220px, so a label must give way rather than shove the
   badge or the "Breve" tag past the edge — min-width: 0 is what lets a flex
   item shrink below its content at all. Ellipsis over clipping: a cut-off word
   with no marker reads as a rendering bug. */
body > nav section ul li a .label:not(.nav_soon) {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* Collapsed state — remove labels from flow so justify-content: center
   works on the icon alone. Vertical spacing (padding) is kept intact. */
body:not(.nav_open) > nav .label { display: none; }

body:not(.nav_open) > nav > a              { justify-content: center; padding: 0; }
body:not(.nav_open) > nav section h2       { justify-content: center; padding: 4px 0; }
body:not(.nav_open) > nav section ul li a  { justify-content: center; padding: 8px 0; }

/* ── Collapsed: no ghost rows ────────────────────────────────────────────
   Hiding .label emptied every section heading but left it holding the
   section's padding plus its own — a ~28px band of nothing between each icon
   group, taller than the 32px items it was separating. Five sections, five
   bands, and the first one read as a gap under the logo.

   The Visão heading is the exception and must survive: it carries the collapse
   toggle, which is the only way back out of this state. So the rule keys on
   "has a button", not on the section's position. */
body:not(.nav_open) > nav section h2:not(:has(button)) { display: none; }

/* With the labels gone, grouping is carried by a hairline instead of by a
   word. 0.12 alpha on brand-0 — decorative only, never used behind text. */
body:not(.nav_open) > nav section + section {
  border-top: 1px solid rgba(255,255,255,0.12);
  padding-top: 10px;
}

/* The quick-action strip draws the same hairline as every other group, and it
   needs it most when collapsed: unpinned (§3.2.9) it now follows Fazenda
   directly instead of floating at the bottom of the rail, so without a line
   the verbs run straight on from the places. One element, one border-top —
   the rule that used to suppress it here was guarding against a doubling that
   cannot happen. */

body > nav section ul li a:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--color-surface-2);
  text-decoration: none;
}

body > nav section ul li.active a {
  border-left-color: var(--color-accent-0);  /* blue active bar — contrasts with green sidebar */
  background: rgba(255, 255, 255, 0.12);
  color: var(--color-surface-2);
  font-weight: 600;
}

/* No ::after bleed needed since the sidebar is now fully dark */
body > nav section ul li.active {
  position: relative;
}

/* ── Nav badges ─────────────────────────────────────────────────────── */
.nav_badge {
  margin-left: auto;
  font-size: 0.8rem;
  font-weight: 700;
  min-width: 1.8em;
  padding: 3px 2px;
  border-radius: 99px;
  line-height: 1.4;
  text-align: center;
  /* amber — a queue to work through ("a conferir"), not an alarm.
     White on #8A6D3B = 4.85:1 — passes AA. */
  background: var(--color-accent-1);
  color: #fff;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 1px 4px rgba(0,0,0,0.25);
}

.nav_badge[data-type="error"] {
  background: var(--color-accent-2);  /* red — reserved for real alerts */
}

/* ── Collapsed: the count survives, re-anchored to the icon ──────────────
   This badge used to be hidden outright when the rail collapsed — that is,
   on exactly the narrow screens where a farmer is most likely to be triaging
   notas on a phone. IA §5 makes the visible "a conferir" count the whole
   justification for placing Notas *under* Despesas instead of beside it, so
   hiding it threw away the reason for the arrangement.

   Collapsed, it becomes the standard corner chip on the icon. It still reads
   at 20px, and the helper renders "9+" past nine so it never grows wider than
   the icon it is pinned to.
   White on --color-accent-1 (#8A6D3B) = 4.85:1 ✅ AA;
   white on --color-accent-2 (#C0392B) = 4.83:1 ✅ AA for the error variant. */
body:not(.nav_open) > nav section ul li a { position: relative; }

body:not(.nav_open) > nav .nav_badge {
  position: absolute;
  top: 2px;
  right: 6px;
  margin-left: 0;
  min-width: 16px;
  padding: 0 4px;
  font-size: 0.6rem;
  line-height: 16px;
  border-radius: 99px;
  /* A 1px ring in the rail's own green keeps the chip readable where it
     overlaps the icon's strokes. */
  box-shadow: 0 0 0 1.5px var(--color-brand-0);
}

/* ── Forthcoming areas ───────────────────────────────────────────────────
   An item the IA has placed but no feature backs yet (see §6b, and the pages
   under /upcoming). It is a real link — it leads to the page that explains the
   area — so it must read as "not yet", never as "broken" and never as
   "available".

   Dimming alone would fail: at 0.62 alpha this text is 3.4:1 on the rail, and
   colour is not allowed to be the only carrier of state anyway. So the state
   is carried by the "Breve" tag, and the label itself stays at the 0.88 alpha
   every other item uses (7.68:1 ✅ AAA) — only the icon is attenuated. */
body > nav li.future a .icon { opacity: 0.7; }

.nav_soon {
  flex-shrink: 0;
  margin-left: auto;
  padding: 1px 6px;
  border-radius: 4px;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  /* #EDE8DF on the rail's #3D5C0A = 9.9:1 ✅ AAA at this weight; the fill is
     a 0.14 white wash so the tag reads as a quiet aside, not a second badge. */
  background: rgba(255,255,255,0.14);
  color: var(--color-badge-pending-bg);
}

/* An active forthcoming item is still active: you are standing on its page. */
body > nav li.future.active a .icon { opacity: 1; }

/* ── The foot: Configurações, below a real gap ───────────────────────────
   interface-system.org §3.2.8. The owner's complaint was that the settings
   part of the menu carries as much visual weight as the work, and the answer
   is emphatically NOT to make it smaller or greyer: every design system that
   addresses this (Material, Shopify, Notion, Polar, Column) keeps settings at
   the same size, weight and colour and separates it by position and space —
   because muting reads as "disabled", and Setup is not less important, it is
   less frequent. Only position encodes frequency honestly.

   So there is no tonal rule here at all. There is a gap, and the section sits
   after it, last in the scroll. That is also the one encoding that survives
   the rail collapsing to 48px, where labels and section headings are gone:
   destinations at the top, then a hole, then the things you set once, then the
   verbs — which are always present, so they belong at the end rather than
   between two kinds of destination (IA §6b draws exactly this order).

   A real gap and NOT another hairline — using the same separator for "next
   section" and "different kind of thing" is what made the boundary invisible
   in the first place. */
body > nav #nav_setup {
  margin-top: 30px;
  padding-top: 4px;
}

/* …including collapsed, where `section + section` would otherwise put the
   ordinary grouping hairline right back on top of it. */
body:not(.nav_open) > nav section + #nav_setup {
  border-top: none;
  padding-top: 4px;
}

/* ── Conta and Sair, one row ─────────────────────────────────────────────
   Two full nav items for account chrome left Configurações as six items of
   two kinds. Compressed to a single row it is four items of one kind, which
   is half of why the group reads as coherent now (§3.2.8 #4).

   This is the one place tone is allowed to do the work, and Polar is the
   precedent: full weight for Settings itself, a genuinely quieter foot for
   true utility links. It is still held to AA — see the colour below. */
body > nav ul.nav_account {
  display: flex;
  align-items: center;
  gap: 4px;
  margin-top: 6px;
  padding: 7px 15px 2px;
  border-top: 1px solid rgba(255,255,255,0.1);
}

body > nav ul.nav_account > li {
  display: flex;
  align-items: center;
  gap: 4px;
  min-width: 0;
}

/* A decorative divider, not a word: it separates two links that a screen
   reader already reads as two list items. */
body > nav ul.nav_account > li + li::before {
  content: "·";
  color: rgba(255,255,255,0.35);
  font-size: 0.8rem;
  line-height: 1;
}

body > nav ul.nav_account li a,
body > nav ul.nav_account li.nav_session button {
  gap: 6px;
  padding: 3px 6px;
  border-left-width: 0;
  border-radius: 4px;
  font-size: 0.78rem;
  font-weight: 600;
  /* 0.72 alpha on the rail's #3D5C0A = 4.87:1 ✅ AA at this size. The obvious
     shortcut — stacking opacity: .72 on the inherited 0.88 — lands at 4.18:1
     and fails, so the alpha is stated once here instead. */
  color: rgba(255,255,255,0.72);
  width: auto;
}

body > nav ul.nav_account li a:hover,
body > nav ul.nav_account li.nav_session button:hover {
  background: rgba(255,255,255,0.08);
  color: var(--color-surface-2);
}

/* Standing on /account still marks the row — quieter chrome, not a dead end.
   The left bar the other items use has no room in a horizontal row, so the
   fill carries it alone here. */
body > nav ul.nav_account li.active a {
  background: rgba(255,255,255,0.14);
  color: var(--color-surface-2);
}

body > nav ul.nav_account .icon {
  flex-shrink: 0;
  --icon-size: 0.85rem;
  width: var(--icon-size);
}

/* Collapsed: the row has no width to be a row, so it stacks — and the divider
   goes with the labels, since it was only ever separating them. */
body:not(.nav_open) > nav ul.nav_account {
  flex-direction: column;
  align-items: stretch;
  gap: 0;
  padding: 7px 0 2px;
}

body:not(.nav_open) > nav ul.nav_account > li + li::before { display: none; }

body:not(.nav_open) > nav ul.nav_account li a,
body:not(.nav_open) > nav ul.nav_account li.nav_session button {
  justify-content: center;
  padding: 6px 0;
  border-radius: 0;
}

/* ── Sair, in the rail beside Conta ──────────────────────────────────────
   Moved out of the header (interface-system.org §3.1): it is account chrome,
   used at most once per session, and it was costing permanent header width on
   every page. It stays a real form so logout works without JS, styled to sit
   flush with the nav links above it. */
.nav_session form.button_to { margin: 0; }

.nav_session button {
  display: flex;
  align-items: center;
  gap: 10px;
  width: 100%;
  padding: 9px 18px;
  border: 0;
  border-left: 3px solid transparent;
  background: none;
  font-family: inherit;
  font-size: 0.9rem;
  font-weight: 600;
  line-height: 1.4;
  text-align: left;
  color: rgba(255, 255, 255, 0.88);
  white-space: nowrap;
  overflow: hidden;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}

.nav_session button:hover {
  background: rgba(255, 255, 255, 0.08);
  color: var(--color-surface-2);
}

.nav_session button .icon { flex-shrink: 0; --icon-size: 1rem; width: 20px; }

body:not(.nav_open) .nav_session button { justify-content: center; padding: 8px 0; }

/********************************* Main ***************************************/
main {
  grid-area: main;
  padding: 16px;
  overflow-y: auto;

  /* See body > header: both grid items in this column must opt out of
     min-width: auto, or a single wide descendant makes the whole page scroll
     sideways. Wide content earns its own overflow container (a table wrapper),
     it does not get to widen the column. */
  min-width: 0;
  scroll-behavior: smooth;
  background: var(--color-surface-0);

  /* Stack every direct child — sections, kpi rows, content grids, etc. —
     with consistent vertical spacing. New boxes get the gap for free. */
  display: flex;
  flex-direction: column;
  gap: 16px;
}

/* Any wrapper element inside main that directly contains sections (e.g. the
   left column of a two-panel layout) inherits the same vertical rhythm.
   Specificity 0,0,3 — safely overridden by any class-based layout rule. */
main :is(div, article):has(> section) {
  display: flex;
  flex-direction: column;
  gap: 16px;
}



/********************************* Flash **************************************/
#flash {
  border: none;
  background: transparent;
  border-radius: 0;
}

#flash p {
  position: relative;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  margin: 0 0 8px;
  padding: 13px 44px 13px 16px;
  border-radius: 6px;
  border: 1px solid;
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.5;
}

#flash p .icon {
  flex-shrink: 0;
  --icon-size: 1.1rem;
}

/* Alert — red */
#flash_alert {
  background: #FDF3F2;
  border-color: var(--color-accent-2);
  color: var(--color-accent-2);
}



/* Notice — green */
#flash_notice {
  background: #F2F9E8;
  border-color: var(--color-brand-0);
  color: var(--color-brand-0);
}



/* Close button */
#flash p button {
  position: absolute;
  top: 12px;
  right: 12px;
  border: none;
  background: transparent;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.5;
  color: inherit;
  padding: 2px 4px;
  border-radius: 3px;
}

#flash p button:hover { opacity: 1; background: rgba(0,0,0,0.06); }

/* JS error toast — injected by application.js on unhandled JS errors */
#js_error_toast {
  position: fixed;
  bottom: 24px;
  right: 24px;
  z-index: 9000;
  max-width: 380px;
  display: flex;
  align-items: flex-start;
  gap: 10px;
  padding: 13px 44px 13px 16px;
  border-radius: 6px;
  border: 1px solid var(--color-accent-2);
  background: #FDF3F2;
  color: var(--color-accent-2);   /* #C0392B — 5.44:1 on #FDF3F2 ✓ */
  font-size: 0.9375rem;
  font-weight: 500;
  line-height: 1.5;
  box-shadow: 0 4px 16px rgba(0,0,0,0.12);
}
#js_error_toast p { margin: 0; }
#js_error_toast button {
  position: absolute;
  top: 12px;
  right: 12px;
  border: none;
  background: transparent;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  opacity: 0.5;
  color: inherit;
  padding: 2px 4px;
  border-radius: 3px;
}
#js_error_toast button:hover { opacity: 1; background: rgba(0,0,0,0.06); }

@media (max-width: 480px) {
  #js_error_toast {
    bottom: 80px;   /* clear the mobile_actions bar */
    right: 12px;
    left: 12px;
    max-width: none;
  }
}

/********************************* Forms **************************************/
form {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

fieldset {
  border: 1px solid var(--color-separators);
  border-radius: 6px;
  padding: 20px 20px 12px;
  display: flex;
  flex-direction: column;
  gap: 12px;
}

legend {
  padding: 0 6px;
  font-size: 0.8rem;
  font-weight: 700;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-content-muted);
}

label {
  display: block;
  font-size: 0.875rem;
  font-weight: 600;
  line-height: 1.4;
  color: var(--color-content);
}

input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]),
select,
textarea {
  width: 100%;
  padding: 8px 12px;
  background: var(--color-surface-2);
  border: 1px solid var(--color-separators);
  border-radius: 5px;
  font-size: 0.9rem;
  font-family: inherit;
  color: var(--color-content);
  transition: border-color 0.15s, box-shadow 0.15s;
}

/* Tabular nums for date/time/number inputs — digits always align in columns.
   NOTE: currency and quantity text inputs (type="text" with inputmode="numeric")
   are NOT covered here — add font-variant-numeric: tabular-nums directly in the
   page-specific stylesheet or via an inline style on the field, alongside
   text-align: right when the field is inside a table row. */
input[type="number"],
input[type="date"],
input[type="time"],
input[type="datetime-local"],
input[type="month"],
input[type="week"] {
  font-variant-numeric: tabular-nums;
}

input:not([type="submit"]):not([type="checkbox"]):not([type="radio"]):not([type="file"]):focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--color-brand-1);
  box-shadow: 0 0 0 3px rgba(90, 128, 24, 0.14);
}

input::placeholder,
textarea::placeholder { color: var(--color-content-muted); opacity: 1; }

textarea { resize: vertical; min-height: 90px; }

select { cursor: pointer; }

/* Password toggler — eye icon floated inside the right edge of the input.
   The JS wraps input[data-toggle] + span.password_toggler in div.input_wrapper. */
.input_wrapper {
  position: relative;
  display: block;
}

.input_wrapper input[data-toggle] {
  padding-right: 44px; /* leave room for the icon */
}

.password_toggler {
  position: absolute;
  right: 0;
  top: 0;
  bottom: 0;
  width: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  font-size: 1rem;
  color: var(--color-content-muted);
  user-select: none;
  transition: color 0.15s;
}

.password_toggler:hover { color: var(--color-content); }

/* "Slash" overlay drawn on top of the eye when the password is visible.
   The span is already position:absolute, so ::after is placed relative to it. */
.password_toggler.visible {
  color: var(--color-content);
}

.password_toggler.visible::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 10%;
  width: 2px;
  height: 80%;
  background: currentColor;
  border-radius: 1px;
  transform: translateX(-50%) rotate(45deg);
  pointer-events: none;
}

/* Inline checkbox / radio rows */
label:has(input[type="checkbox"]),
label:has(input[type="radio"]) {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  font-weight: 400;
  cursor: pointer;
}

input[type="checkbox"],
input[type="radio"] {
  width: 16px;
  height: 16px;
  cursor: pointer;
  accent-color: var(--color-brand-1);
  flex-shrink: 0;
}

/* All form buttons — secondary style by default */
form input[type="submit"],
form button {
  align-self: flex-start;
  padding: 9px 22px;
  border-radius: 5px;
  font-size: 0.9375rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  background: transparent;
  border: 1.5px solid var(--color-brand-0);
  color: var(--color-brand-0);
}

form input[type="submit"]:hover,
form button:hover {
  background: color-mix(in srgb, var(--color-brand-2) 30%, transparent);
}

/* Last button — primary style */
form input[type="submit"]:last-child,
form button:last-child {
  background: var(--color-brand-0);
  border-color: var(--color-brand-0);
  color: var(--color-surface-2);
}

form input[type="submit"]:last-child:hover,
form button:last-child:hover {
  background: var(--color-brand-1);
  border-color: var(--color-brand-1);
}

/* Hint text beneath a field */
label small {
  display: block;
  margin-top: 4px;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--color-content-muted);
}

/* Required field indicator — auto-added by InputTagsReflectValidationFromModel */
label.required::after {
  content: ' *';
  color: var(--color-accent-2);
  font-weight: 700;
}

/* Base-error summary — rendered by FormHelperWithBaseErrors BEFORE the <form> tag.
   Only errors.add(:base, …) appear here. */
#error_explanation {
  background: color-mix(in srgb, var(--color-accent-2) 8%, var(--color-surface-2));
  border: 1px solid var(--color-accent-2);
  border-radius: 6px;
  padding: 12px 16px;
  margin-bottom: 16px;
}

#error_explanation ul {
  display: flex;
  flex-direction: column;
  gap: 4px;
}

#error_explanation li.error {
  display: flex;
  align-items: baseline;
  gap: 6px;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--color-accent-2);
}

#error_explanation li.error::before {
  content: '•';
  flex-shrink: 0;
  font-size: 1rem;
  line-height: 1;
}

/* Field-level errors — custom field_error_proc wraps each errored input in
   .field_with_errors and appends a <span class="error_message"> below it.
   Labels are returned unchanged (not wrapped), so use :has() to colour them. */
label:has(+ .field_with_errors) { color: var(--color-accent-2); }

.field_with_errors input,
.field_with_errors select,
.field_with_errors textarea {
  border-color: var(--color-accent-2);
  box-shadow: 0 0 0 3px color-mix(in srgb, var(--color-accent-2) 12%, transparent);
}

span.error_message {
  display: block;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--color-accent-2);
  margin-top: 4px;
}

/********************************* Cards **************************************/
main section {
  background: var(--color-surface-2);
  border: 1px solid var(--color-separators);
  border-radius: 8px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.06);
  overflow: hidden;
}

main section > header {
  all: unset;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  background: var(--color-surface-1);
  border-bottom: 1px solid var(--color-separators);
}

main section > header h2 {
  font-size: 0.9rem;
  font-weight: 600;
  letter-spacing: 0.01em;
  color: var(--color-content);
}

main section > header a {
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-brand-1);
}

main section > header a:hover { color: var(--color-brand-0); }

/********************************* KPI row ************************************/
.kpi_row {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 16px;
}

.kpi_row article {
  background: var(--color-surface-2);
  border: 1px solid var(--color-separators);
  border-radius: 8px;
  padding: 20px 24px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.05);
}

.kpi_row .kpi_label {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--color-content-muted);
  margin-bottom: 10px;
}

.kpi_row .kpi_value {
  font-size: 1.75rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--color-content);
  font-variant-numeric: tabular-nums;
}

/* The accent cycles every three cards rather than stopping at the third: a
   strip of four or five (Despesa, the activity-cost report) used to have its
   later cards silently lose the border and stop looking like the same card. */
.kpi_row article:nth-child(3n + 1) { border-top: 3px solid var(--color-brand-0);  }  /* dark green  */
.kpi_row article:nth-child(3n + 2) { border-top: 3px solid var(--color-accent-0); }  /* blue        */
.kpi_row article:nth-child(3n)     { border-top: 3px solid var(--color-accent-1); }  /* warm amber  */

/********************************* Spend comparison panel *********************/
/* Shared by expenditures#index and reports#show (layouts/_spend_comparison).  */
/* KPI direction colours are scoped under .cmp so they never collide with the  */
/* dashboard's own (which use amber for "up").                                 */
.cmp {
  display: flex;
  flex-direction: column;
  gap: 20px;
  margin-bottom: 24px;
}

/* Plain-language verdict headline */
.cmp_verdict {
  font-size: 1.05rem;
  font-weight: 600;
  color: var(--color-content);
  padding: 12px 16px;
  border-radius: 8px;
  background: var(--color-surface-1);
  border-left: 4px solid var(--color-content-muted);
}
.cmp_verdict[data-direction="down"] { border-left-color: var(--color-brand-0); }
.cmp_verdict[data-direction="up"]   { border-left-color: var(--color-accent-2); }

/* KPI strip — 3 cards; scoped to out-specify page-level .kpi_row overrides. */
.cmp .kpi_row { grid-template-columns: repeat(3, minmax(0, 220px)); }
.cmp .kpi_label { font-size: 0.78rem; color: var(--color-content-muted); }
.cmp .kpi_delta { display: block; margin-top: 6px; font-size: 0.8rem; font-variant-numeric: tabular-nums; }
.cmp .kpi_value[data-direction="up"]   { color: var(--color-accent-2); }
.cmp .kpi_value[data-direction="down"] { color: var(--color-brand-0); }
.cmp .kpi_value[data-direction="flat"] { color: var(--color-content-muted); }
.cmp .kpi_delta[data-direction="up"]   { color: var(--color-accent-2); }
.cmp .kpi_delta[data-direction="down"] { color: var(--color-brand-0); }

/* Two-series monthly chart (pure CSS bars, same idea as the evolution report) */
.cmp_legend_row { display: flex; gap: 20px; margin-bottom: 8px; font-size: 0.82rem; color: var(--color-content); }
.cmp_legend .sw { display: inline-block; width: 22px; height: 8px; border-radius: 2px; margin-right: 6px; vertical-align: middle; }
.sw_primary { background: var(--color-brand-0); }
.sw_compare { background: repeating-linear-gradient(45deg, var(--color-content-muted) 0 3px, transparent 3px 6px); border: 1px solid var(--color-content-muted); }

.cmp_chart { display: flex; align-items: flex-end; gap: 8px; height: 160px; }
.cmp_group { flex: 1; display: flex; flex-direction: column; align-items: center; gap: 6px; height: 100%; min-width: 0; }
.cmp_bars { flex: 1; display: flex; align-items: flex-end; justify-content: center; gap: 3px; width: 100%; }
.cmp_bar { width: 42%; max-width: 22px; border-radius: 2px 2px 0 0; }
.cmp_bar_primary { background: var(--color-brand-0); }
.cmp_bar_compare { background: repeating-linear-gradient(45deg, color-mix(in srgb, var(--color-content-muted) 40%, transparent) 0 3px, transparent 3px 6px); border: 1px solid var(--color-content-muted); border-bottom: none; }
.cmp_axis { font-size: 0.65rem; color: var(--color-content-muted); }

/* "Onde mais mudou" — category movers, biggest change first */
.cmp_movers_head { font-size: 0.95rem; color: var(--color-brand-0); margin-bottom: 4px; }
.cmp_movers { width: 100%; border-collapse: collapse; }
.cmp_movers td { padding: 8px 10px; border-bottom: 1px solid var(--color-separators); font-size: 0.9rem; }
.cmp_mover_name { font-weight: 600; white-space: nowrap; }
.cmp_mover_bar { width: 40%; }
.cmp_inline_bar { display: block; height: 8px; border-radius: 4px; background: var(--color-brand-2); min-width: 2px; }
.cmp_mover_val { text-align: right; font-variant-numeric: tabular-nums; }
.cmp_mover_vs { text-align: right; color: var(--color-content-muted); font-size: 0.82rem; font-variant-numeric: tabular-nums; }
.cmp_mover_delta { text-align: right; font-weight: 600; font-variant-numeric: tabular-nums; }
.cmp_mover_delta[data-direction="up"]   { color: var(--color-accent-2); }
.cmp_mover_delta[data-direction="down"] { color: var(--color-brand-0); }

/********************************* Content grid *******************************/
.content_grid {
  display: grid;
  grid-template-columns: 1fr 420px;
  gap: 16px;
  align-items: start;
}

.content_grid > aside {
  grid-row: 1 / span 2;
  grid-column: 2;
}

#page_overrides .swatches {
  grid-template-columns: 1fr;
}

#page_overrides .swatches span {
  height: 80px;
  display: flex;
  align-items: center;
  justify-content: center;
  font-size: 0.75rem;
  text-align: center;
  padding: 0 16px;
}

/********************************* Table **************************************/
table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.875rem;
}

thead th {
  padding: 10px 20px;
  text-align: left;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--color-content-muted);
  background: var(--color-surface-1);
  border-bottom: 1px solid var(--color-separators);
}

tbody tr td {
  padding: 12px 20px;
  border-bottom: 1px solid var(--color-separators);
  color: var(--color-content);
  font-size: 0.9375rem;
  line-height: 1.5;
  /* NOTE: tabular-nums NOT applied globally — most td content is mixed text.
     In page-specific stylesheets, target numeric columns explicitly, e.g.:
       td:nth-child(2), td:nth-child(3) { font-variant-numeric: tabular-nums; text-align: right; }
     reports/show.css already does this for the breakdown table. */
}

tbody tr:last-child td { border-bottom: none; }
tbody tr:hover td      { background: var(--color-surface-0); }

/********************************* Badges *************************************/
td span {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 99px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.4;
}

/* All badge backgrounds + text pairs pass WCAG AA ≥4.5:1 */
td span[data-status="ok"]      { background: var(--color-badge-ok-bg);      color: var(--color-badge-ok-text);      }
td span[data-status="pending"] { background: var(--color-badge-pending-bg); color: var(--color-badge-pending-text); }
td span[data-status="error"]   { background: #FCEAE8;                       color: var(--color-accent-2);           }

/* Reusable badge — works anywhere (td, div, span).
   Use data-status for semantic colour; default is neutral.                   */
/* Visible to screen readers only — for a control whose purpose is already
   obvious on screen (a search field with a placeholder) but which still needs
   a real label in the accessibility tree. */
.visually_hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 99px;
  font-size: 0.8rem;
  font-weight: 600;
  line-height: 1.4;
  /* A pill that wraps stops reading as a pill: "Parcialmente registrada"
     became two stacked lines inside one rounded outline, and took the whole
     table row's height with it. */
  white-space: nowrap;
  background: var(--color-badge-pending-bg);   /* neutral default — 6.41:1 with pending-text */
  color: var(--color-badge-pending-text);
}

.badge[data-status="ok"]      { background: var(--color-badge-ok-bg);      color: var(--color-badge-ok-text);      }
.badge[data-status="pending"] { background: var(--color-badge-pending-bg); color: var(--color-badge-pending-text); }
.badge[data-status="error"]   { background: #FCEAE8;                       color: var(--color-accent-2);           }

/* Expenditure source badges — contrast verified:
   receipt: brand-ok pair  5.04:1 ✅ AA   (#3D5C0A on #C2DB88)
   manual:  brand-pending  6.41:1 ✅ AA   (#5C5044 on #EDE8DF)
   mixed:   content-muted  6.20:1 ✅ AA   (#4A5E50 on #F0F2EE)              */
.badge[data-source="receipt"] { background: var(--color-badge-ok-bg);      color: var(--color-badge-ok-text);      }
.badge[data-source="manual"]  { background: var(--color-badge-pending-bg); color: var(--color-badge-pending-text); }
.badge[data-source="mixed"]   { background: var(--color-surface-0);        color: var(--color-content-muted);      }

/* Receipt allocation status badges — contrast verified:
   full:    brand-ok pair   5.04:1 ✅ AA   (#3D5C0A on #C2DB88)
   partial: brand-pending   6.41:1 ✅ AA   (#5C5044 on #EDE8DF)
   none:    #7A4F1E/#FAF0E6 5.12:1 ✅ AA   (warm amber — signals action needed) */
.badge[data-allocation="full"]    { background: var(--color-badge-ok-bg);      color: var(--color-badge-ok-text);      }
.badge[data-allocation="partial"] { background: var(--color-badge-pending-bg); color: var(--color-badge-pending-text); }
.badge[data-allocation="none"]    { background: #FAF0E6;                        color: #7A4F1E;                         }

/********************************* Category breakdown *************************/
/* Shared between dashboard and reports. Moved here from reports/show.css     *
 * when the partial was extracted to app/views/shared/_category_breakdown.    */

/* Attenuated green for chart bars — softer than --color-brand-1 (#486B13).
   color-mix blends brand-2 (light green) with surface-0 for a muted tint
   that reads as "data" rather than "action". */
:root {
  --color-chart-bar: color-mix(in srgb, var(--color-brand-2) 55%, var(--color-surface-0));

  /* Muted warm grey-green — the "uncategorized" share, in the reports donut and
     in the shared category breakdown's "Sem categoria" row. Contrast vs white:
     2.6:1 (graphical element, not text — WCAG 1.4.11 requires 3:1 for UI
     components, acceptable here because the text alongside it meets 4.5:1). */
  --color-chart-uncategorized: #B8CCBB;
}

.category_breakdown {
  padding: 8px 20px 20px;
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.breakdown_group {
  border: none;
}

details.breakdown_group {
  border: none;
}

.breakdown_row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 8px 12px;
  border-radius: 6px;
  list-style: none;
  background:
    linear-gradient(
      to right,
      var(--color-chart-bar) var(--bar-width, 0%),
      var(--color-surface-0) var(--bar-width, 0%)
    );
}

/* Spend with no category. Hatched rather than tinted: a solid bar would read as
   one more category, and this is the absence of one. It also never expands —
   there is nothing underneath it — so it is a plain row, not a <summary>. */
.breakdown_uncategorized {
  background:
    repeating-linear-gradient(
      -45deg,
      var(--color-chart-uncategorized) 0 6px,
      var(--color-surface-0) 6px 12px
    ) left / var(--bar-width, 0%) 100% no-repeat,
    var(--color-surface-0);
}

.breakdown_uncategorized .breakdown_name {
  color: var(--color-content-muted);
}

summary.breakdown_row::-webkit-details-marker { display: none; }
summary.breakdown_row { list-style-type: none; cursor: pointer; }

.breakdown_name {
  font-size: 0.875rem;
  font-weight: 500;
  color: var(--color-content);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.breakdown_value {
  font-size: 0.875rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--color-content);
  text-align: right;
  white-space: nowrap;
  flex-shrink: 0;
  margin-left: 12px;
}

.breakdown_pct {
  font-size: 0.8125rem;
  font-weight: 400;
  color: var(--color-content-muted);
  margin-left: 4px;
}

.breakdown_children {
  padding-bottom: 8px;
}

.breakdown_child {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding: 3px 0;
}

.breakdown_child_name {
  font-size: 0.8125rem;
  color: var(--color-content-muted);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.breakdown_child_value {
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
  color: var(--color-content-muted);
  white-space: nowrap;
  flex-shrink: 0;
  margin-left: 12px;
}

.breakdown_footer {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding-top: 16px;
  border-top: 2px solid var(--color-separators);
  margin-top: 8px;
  font-size: 0.9375rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  color: var(--color-content);
}

/********************************* Link-as-button *****************************/
/* .button and .button.secondary let link_to render as a primary/secondary    *
 * button without being inside a <form>. Same sizing as form buttons.         */
.button {
  display: inline-block;
  padding: 9px 22px;
  border-radius: 5px;
  font-size: 0.9375rem;
  font-weight: 600;
  font-family: inherit;
  line-height: 1.5;
  text-decoration: none;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  background: var(--color-brand-0);
  border: 1.5px solid var(--color-brand-0);
  color: var(--color-surface-2);
}

.button:hover {
  background: var(--color-brand-1);
  border-color: var(--color-brand-1);
  color: var(--color-surface-2);
  text-decoration: none;
}

.button.secondary {
  background: transparent;
  border-color: var(--color-brand-0);
  color: var(--color-brand-0);
}

.button.secondary:hover {
  background: color-mix(in srgb, var(--color-brand-2) 30%, transparent);
  color: var(--color-brand-0);
}

/********************************* Aside panels *******************************/
aside {
  display: flex;
  flex-direction: column;
  gap: 16px;
}

aside > div:first-child {
  background: var(--color-accent-0);  /* corporate blue — distinguishes the aside from the green nav */
  border-radius: 8px;
  padding: 24px;
  box-shadow: 0 2px 8px rgba(0,0,0,0.12);
}

aside > div:first-child p:first-child {
  font-size: 0.68rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.75);  /* white-alpha label on blue — ~8:1 */
  margin-bottom: 10px;
}

aside > div:first-child p:nth-child(2) {
  font-size: 2rem;
  font-weight: 700;
  letter-spacing: -0.03em;
  color: var(--color-surface-2);  /* pure white — 3.76:1 on #5B7FA6 (large text: ≥ 3:1 AA) */
  margin-bottom: 4px;
  font-variant-numeric: tabular-nums;
}

aside > div:first-child p:last-child {
  font-size: 0.8rem;
  font-weight: 500;
  color: rgba(255, 255, 255, 0.75);  /* white-alpha subtitle on blue — ~8:1 */
}


/********************************* Button strip ********************************/
/* .actions is a flex strip of link_to / button_to actions.
   button_to wraps a <button> in a <form>; dissolve the form into the flex
   flow so it participates directly in the strip layout.                       */
/* ── A table that is wider than its card ────────────────────────────────────
   An opt-in container, never a global `table { overflow-x: auto }`.

   `main section` clips with overflow: hidden, so a table wider than the card
   does not merely look cramped — its right-hand columns become *unreachable*.
   At 375px the receipts table is 888px inside a 423px card, which put
   "Registrar despesa" permanently out of reach.

   This is a stopgap, not the answer. interface-system.org §3.4 is right that a
   farmer cannot discover a sideways scroll on a phone, and §4.2 specifies the
   real fix: a declared column priority that drops columns and restacks the row
   below 760px. Until that ships, reachable-by-scrolling beats invisible.

   tabindex makes it scrollable from the keyboard; without it a scroll
   container with no focusable child cannot be reached at all. */
.table_scroll {
  overflow-x: auto;
  overscroll-behavior-x: contain;
}

.table_scroll:focus-visible {
  outline: 2px solid var(--color-brand-1);
  outline-offset: -2px;
}

/* The row that sits directly above a list: the filters that act on that list,
   and the one action the page exists for. Page-level filters live here rather
   than in the app header, so the header's contents stop changing shape per
   page — doc/design/interface-system.org §3.1. */
.list_toolbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: 10px 16px;
  min-width: 0;
}

.actions {
  display: flex;
  gap: 10px;
  margin-bottom: 16px;
  flex-wrap: wrap;
  align-items: center;
}

/* button_to generates <form class="button_to"> — make it invisible in layout */
.actions form {
  display: contents;
}

/* Buttons inside .actions inherit a.button sizing; default = secondary style  */
.actions button {
  display: inline-block;
  padding: 9px 22px;
  border-radius: 5px;
  font-size: 0.9375rem;
  font-weight: 600;
  font-family: inherit;
  cursor: pointer;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
  background: transparent;
  border: 1.5px solid var(--color-brand-0);
  color: var(--color-brand-0);
}

.actions button:hover {
  background: color-mix(in srgb, var(--color-brand-2) 30%, transparent);
}

/********************************* Swatches ***********************************/
.swatches {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 6px;
  padding: 16px 20px;
}

.swatches span {
  display: block;
  height: 36px;
  border-radius: 4px;
  border: 1px solid var(--color-separators);
  position: relative;
}

.swatches span::after {
  content: attr(title);
  position: absolute;
  bottom: -18px;
  left: 0; right: 0;
  font-size: 0.6rem;
  color: var(--color-content-muted);
  text-align: center;
  white-space: nowrap;
}

.swatches_wrap { padding-bottom: 16px; }

/********************************* Quick actions (sidebar verb strip) *********/
/* Promote one, quiet two — interface-system.org §3.2.9.
 *
 * This strip used to render all three items as filled purple buttons at weight
 * 700 with a drop shadow, which made them the loudest objects in the rail:
 * louder than any nav item and louder than the notas badge. Two things were
 * wrong with that. Three obvious things is no obvious thing (the promote-one
 * rule the dashboard already follows), and it spent the rail's colour budget on
 * an affordance — a purple block saying "you could always do this", competing
 * with the one element that says "there is work waiting right now".
 *
 * So exactly one action is filled and the other two are ordinary rail rows.
 *
 * Not pinned with margin-top: auto either. Microsoft Fluent advises against a
 * pinned nav footer because it obscures nav content as the list grows, and our
 * rail is twelve items and growing. Unpinned, but still last: the caveat is
 * against a footer that claims the leftover height and can cover the nav, not
 * against the verbs coming last. The rule above them is the one IA §6b draws.
 */
#quick_actions {
  padding: 12px 0 4px;
  border-top: 1px solid rgba(255,255,255,0.12);
}

#quick_actions h2 {
  /* inherits the nav section h2 rule — just override padding slightly */
  padding-bottom: 8px;
}

/* The two quiet ones inherit `body > nav section ul li a` wholesale: same
   weight, same colour, same icon column as Despesas or Relatórios. Nothing to
   declare — the absence of a rule here is the design. */
#quick_actions ul li {
  padding: 0;
}

/* The promoted one: a filled pill, inset from the rail edge so it reads as an
   object rather than as a row. */
#quick_actions ul li.qa_primary {
  padding: 2px 12px 4px;
}

#quick_actions ul li.qa_primary a {
  gap: 9px;
  padding: 9px 13px;
  border-left: none;
  border-radius: 8px;
  background: var(--color-brand-2);
  /* #1A2B1A on #A8C96B = 8.00:1 ✅ AAA. Measured, not assumed: the obvious
     pairing --color-brand-0 on --color-brand-2 is only 4.11:1 and FAILS AA at
     this size, which is exactly what these annotations exist to catch. */
  color: var(--color-content);
  font-size: 0.84rem;
  font-weight: 700;
  transition: background 0.15s;
}

#quick_actions ul li.qa_primary a:hover {
  /* Lighter, not darker: on a dark rail a lift reads as hover, and it raises
     the text contrast rather than eroding it — #B5D181 = 8.84:1 ✅ AAA. */
  background: color-mix(in srgb, var(--color-brand-2) 85%, #FFFFFF);
  color: var(--color-content);
}

#quick_actions ul li.qa_primary a .icon {
  /* The rail's 20px icon column belongs to the rows; inside a pill the icon
     sits tight against its label. An explicit width is required — the shared
     .icon rule sizes the box, and `auto` on an empty masked span is 0. */
  --icon-size: 1.1rem;
  width: var(--icon-size);
}

/* Collapsed nav — the pill keeps its shape, the rows keep the shared centring
   rule from the sidebar block above. */
body:not(.nav_open) #quick_actions ul li.qa_primary {
  padding: 2px 8px 4px;
}

body:not(.nav_open) #quick_actions ul li.qa_primary a {
  justify-content: center;
  padding: 9px 0;
}

/********************************* Mobile action bar **************************/
#mobile_actions { display: none; }

/********************************* Responsive *********************************/
/* Desktop-first: base styles target ≥1280px. Queries narrow for smaller viewports. */

/* Large desktops and below — ≤1280px */
@media (max-width: 1280px) {
}

/* ── The header ladder ──────────────────────────────────────────────────────
   Three things share this row: the title, the period pill and the activity
   pill. They shed in that order. The pills are last because the pill is what
   tells the farmer which window the numbers on screen belong to — losing it is
   worse than losing the page's own name, which the rail already shows.
   See doc/design/interface-system.org §3.1.                                  */

/* ≤1100px — the subtitle goes first. It is the longest and the least load
   bearing: it restates the frame that the pill beside it already names. */
@media (max-width: 1100px) {
  body > header hgroup p { display: none; }
}

/* ≤900px — two rows: title above, pills below, both left-aligned. This is not
   a failure state. Letting the header grow to 92px costs far less than
   truncating the one control that says what period is being shown. */
@media (max-width: 900px) {
  /* auto, not a second magic number: on a page with no pills this is one row
     of title, and on a narrow phone with a long custom range it is three. The
     row takes what the content needs and the dropdown panels follow it (see
     .hdr_panel at ≤768). */
  body { --header-height: auto; }

  body > header {
    flex-wrap: wrap;
    align-content: center;
    row-gap: 6px;
    min-height: 60px;
    padding-top: 8px;
    padding-bottom: 8px;
  }

  /* Forces the wrap: the title takes the whole first row, the pills fall to
     the second in their natural order. */
  body > header hgroup { flex: 1 0 100%; }
}

/* Laptops and below — ≤1024px */
@media (max-width: 1024px) {
  .content_grid { grid-template-columns: 1fr; }

  /* Reset explicit grid placement so the aside flows below the sections. */
  .content_grid > aside {
    grid-column: auto;
    grid-row: auto;
  }

}

/* Tablets and below — ≤768px */
@media (max-width: 768px) {
  /* The title used to be dropped outright here. That left a blank 60px band on
     every page without a period pill, and — worse — the rail is icon-only at
     this width too, so nothing on screen said what page the farmer was looking
     at. interface-system.org §3.1 asks for two rows down to the smallest
     screen rather than a title-less header; this is that.
     The subtitle stays hidden (see ≤1100px). */

  .kpi_row { grid-template-columns: 1fr; }
  .kpi_row article { padding: 14px 16px; }
  .kpi_row .kpi_value { font-size: 1.4rem; }

  .category_breakdown { padding: 4px 16px 16px; }

  /* #mobile_actions replaces the sidebar quick actions on small screens */
  #quick_actions { display: none; }

  #mobile_actions {
    display: flex;
    position: fixed;
    bottom: 0; left: 0; right: 0;
    background: var(--color-surface-2);
    border-top: 1px solid var(--color-separators);
    box-shadow: 0 -2px 8px 0 var(--color-separators);
    z-index: 100;
  }

  #mobile_actions a {
    flex: 1;
    display: flex;
    flex-direction: row;
    align-items: center;
    justify-content: center;
    gap: 6px;
    padding: 12px 4px;
    font-size: 0.8125rem;
    font-weight: 600;
    color: var(--color-content-muted);
    white-space: nowrap;
  }

  #mobile_actions a:hover,
  #mobile_actions a:first-child { color: var(--color-brand-0); }

  #mobile_actions a span { font-size: 1.1rem; line-height: 1; }

  main { padding-bottom: 80px; }
}

/* ── Category tree — shared depth indentation ──────────────────────────────── */
/* Any element with data-depth is indented by 1.25rem per level.              */
/* Used in: categories#index (.category_tree li) and ELI category checkboxes  */
/* (fieldset label) so both places stay consistent without extra classes.      */

[data-depth="0"] { padding-left: 0; }
[data-depth="1"] { padding-left: 1.25rem; }
[data-depth="2"] { padding-left: 2.5rem; }
[data-depth="3"] { padding-left: 3.75rem; }

/* Large phones and below — ≤480px */
@media (max-width: 480px) {
  .breakdown_pct { display: none; }
}

/* Small phones — ≤375px */
@media (max-width: 375px) {
}
