/* Algarve Imports Ops CRM — single dark theme (no toggle).
   Calm, low-stress palette tuned for long focus sessions:
   near-black greys (not pure black), soft off-white text (not pure white),
   a muted blue accent. */

:root {
  color-scheme: dark;
  --bg: #1c2128;          /* page background (near-black grey) */
  --panel: #22272e;       /* cards / tables */
  --panel-2: #2d333b;     /* table header, hover, inputs */
  --header: #14181d;      /* top bar */
  --border: #373e47;      /* subtle separators */
  --border-strong: #444c56;
  --text: #adbac7;        /* soft off-white body text */
  --text-strong: #cdd9e5; /* headings / emphasis */
  --muted: #768390;       /* secondary text */
  --accent: #539bf5;      /* calm blue — links */
  --accent-soft: #243b55; /* muted blue fill */
  --shadow: 0 1px 3px rgba(0, 0, 0, .4);
  /* The one content column, shared by the header bar and <main> so they cannot drift apart.
     FLUID since 2026-07-20: the 1100px cap left a dead gutter on anything wider than a laptop —
     at 1920 the tables were using 57% of the screen while their percentage columns had room to
     spread. `none` lets the column follow the viewport; --gutter is the breathing room at the
     edges. ⚠️The CASE VIEW deliberately keeps a cap (body--case, 1320px, further down): it is a
     two-column form, not a data table, and stretching a form to 1920 makes it harder to read,
     not easier. That cap is a design decision from an earlier pass and is pinned by a test. */
  --content-max: none;
  --gutter: 28px;
  /* Signed money, ONE pair for the whole CRM (estimates, finder, Histórico). Both are colours the
     palette already used - the red is the same #e5534b every error state uses, the green the same
     #57ab5a as .stat-tile.ok - so this is a rename into tokens, not a new palette. Deliberately
     the CALMER green: a saving is information, not a celebration. */
  --money-pos: #57ab5a;
  --money-neg: #e5534b;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 14px/1.45 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  color: var(--text);
  background: var(--bg);
}

header {
  /* full-bleed bar, centred inner column (see .header-inner) */
  display: flex;
  justify-content: center;
  /* ⚠️The horizontal padding moved OFF the bar and ONTO .header-inner. The bar is full-bleed and
     the inner column must be the SAME BOX as <main> — nav_layout section 2 compares their
     bounding rects, and it is also just how the eye checks alignment. Padding the bar instead
     would inset the inner column by 20px while main started at 0, i.e. exactly the "page is
     shifted" complaint that alignment rule exists to prevent. */
  padding: 10px 0;
  background: var(--header);
  color: var(--text-strong);
  border-bottom: 1px solid var(--border);
  position: sticky;
  top: 0;
  z-index: 10;
}
.header-inner {
  display: flex;
  align-items: center;      /* one line, everything vertically centred on it */
  gap: 20px;
  width: 100%;
  max-width: var(--content-max);
  padding: 0 var(--gutter);   /* same gutter as <main>, so the two columns line up exactly */
  min-width: 0;
}
/* --- Brand monogram (replaces the "Algarve Imports — Ops CRM" wordmark; doubles as Home) ---
   The tile artwork is the real brand mark (website/public/icon.svg = the Instagram tile) inlined in
   index.html — its "AI" is amber #F4A724 STROKES on a #0D0D0D tile, not type, so no webfont loads. */
header h1.brand { margin: 0; line-height: 0; flex: 0 0 auto; }
.brand-link { display: inline-flex; align-items: center; border-radius: 8px; text-decoration: none; }
.brand-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
.brand-tile { display: block; width: 30px; height: 30px; transition: transform .12s ease, filter .12s ease; }
.brand-link:hover .brand-tile { transform: translateY(-1px); filter: brightness(1.12); }
.brand-link:active .brand-tile { transform: translateY(0); }
@media (prefers-reduced-motion: reduce) { .brand-tile { transition: none; } .brand-link:hover .brand-tile { transform: none; } }
/* the wordmark the tile replaces — kept for screen readers */
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* Any scrollIntoView onto the extraction card must clear the sticky header (~54px) so its <h2>
   title isn't cut off at the top of the viewport. */
#extract-card { scroll-margin-top: 84px; }

nav { display: flex; align-items: center; gap: 6px; min-width: 0; }
nav a {
  color: var(--muted);
  text-decoration: none;
  padding: 6px 12px;
  border-radius: 6px;
  font-weight: 500;
  white-space: nowrap;      /* a label may never wrap inside its own pill */
  line-height: 20px;
}
nav a:hover { background: var(--panel-2); color: var(--text-strong); }
nav a.active { background: var(--accent-soft); color: var(--text-strong); }

/* ⚠️Nav overflow, 641-820px. A ninth tab (Procura) pushed the bar to 828px in a 768px window and the
   PAGE started scrolling horizontally. Below 640 the nav already scrolls inside itself; this adds
   ONLY that overflow behaviour for the band above it, leaving the header reflow untouched. */
@media (max-width: 820px) {
  nav { overflow-x: auto; scrollbar-width: none; }
  nav::-webkit-scrollbar { display: none; }
  nav a { flex: 0 0 auto; }
}

/* .ro-badge removed 2026-07-16: it was decoration that gated nothing (relabelled READ-ONLY ->
   READ-MOSTLY in July precisely because it implied a guarantee the code did not make), and the real
   posture is enforced by the write-allowlist + the login gate. */

main { padding: 20px var(--gutter); max-width: var(--content-max); margin: 0 auto; }

/* A table's min-content can exceed the content column (the Cases table wants ~1137px in a 1060px
   box), so it spills. It scrolls INSIDE its own box at every width; where it fits, no bar shows.
   ⚠️THE SCROLLER IS THE .table-scroll WRAPPER, NOT THE <table>. The previous form of this rule was
   `main table { display:block; overflow-x:auto }`: that scrolls, but it makes the table the scroll
   container, so the horizontal bar is drawn at the bottom edge of the entire table — 2770px below
   the fold on a 25-row Histórico at 1280, 329px at 390 — and a vertical wheel does not pan a
   horizontal scroller. The right-hand columns were therefore unreachable in practice. display:block
   also collapsed the table to shrink-to-fit, squeezing columns and ballooning row height (141px vs
   39px on Histórico at 1280), which is what made it look clipped/broken. */
.table-scroll {
  /* ⚠️NO HORIZONTAL SCROLLING, ANYWHERE — that is the whole point of this pass. The table below
     is table-layout:fixed at width:100%, so it CANNOT exceed this box at any viewport or zoom
     level; there is therefore nothing to scroll sideways and hidden clips nothing. The class
     name is kept (tests and markup reference it) but it is now purely the VERTICAL cap box. */
  overflow-x: hidden;
  overflow-y: hidden;
  -webkit-overflow-scrolling: touch;   /* iOS momentum */
  /* X contained, Y not: a horizontal fling must never reach the browser's back-navigation
     gesture, but once the capped box is scrolled to its last row the wheel SHOULD carry on to
     the page — containing both axes traps the pointer over the table and makes the operator
     move the mouse off it to keep reading. */
  overscroll-behavior-x: contain;
  overscroll-behavior-y: auto;
  border-radius: 8px;
  /* The bar is the ONLY affordance that these columns continue off-screen, so it is always
     rendered rather than left to the platform's overlay behaviour (which hides it until you
     already know to scroll — precisely the bug being fixed). */
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) var(--panel-2);
}
.table-scroll::-webkit-scrollbar { height: 10px; width: 10px; }
.table-scroll::-webkit-scrollbar-track { background: var(--panel-2); border-radius: 0 0 8px 8px; }
.table-scroll::-webkit-scrollbar-thumb {
  background: var(--border-strong);
  border-radius: 6px;
  border: 2px solid var(--panel-2);
}
.table-scroll::-webkit-scrollbar-thumb:hover { background: var(--muted); }
/* FIT-TO-WIDTH. table-layout:fixed + width:100% means the column widths are decided by the
   percentages below and NOT by the content, so the table is exactly as wide as its container at
   every viewport and every zoom level. Fit stops being something to verify and becomes structural
   — which is what lets the horizontal scrollbar go away entirely.
   The trade is that a long value is clipped, so every clipped cell carries a title (stamped by
   finishTable in app.js) — truncation with a tooltip, deliberately, instead of a scrollbar. */
main .table-scroll > table {
  display: table;
  table-layout: fixed;
  width: 100%;
  min-width: 0;
}
main .table-scroll > table th,
main .table-scroll > table td {
  max-width: none;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
/* tighter than the 9px/12px list default so ~11 columns stay legible at 1280 */
main .table-scroll > table th,
main .table-scroll > table td { padding: 6px 8px; font-size: 12.5px; }
main .table-scroll > table th { font-size: 11px; }

/* Column widths. Percentages, so they hold at any width and any zoom.
   ── Histórico ── ops sees 9 columns, a developer 11 (IP + Conta). */
/* NINE tracks summing to 100 since the Associar column was removed (2026-07-27): the 8% it held is
   spread over the columns that actually carry content — the car name and the three money columns. */
/* ⚠️6.9%, MEASURED, NOT GUESSED. At 1280 the table box is 1224px, "07/09 10:32" renders 66px wide
   and the cell padding is 8px a side — so 82px is the whole requirement and the old 10% (122px)
   left FORTY pixels of dead space between the date and the car name. 6.9% is 84.5px: the content
   plus a 2.5px cushion, which puts the gap back to ordinary cell padding. The freed 3.1% goes to
   Viatura in every one of the three width tables below. */
.tbl--hist th:nth-child(1) { width: 6.9% }  /* Date — sized to "07/09 10:32" */
.tbl--hist th:nth-child(2) { width: 23.1% } /* Car         */
.tbl--hist th:nth-child(3) { width: 7%  }   /* Fuel        */
.tbl--hist th:nth-child(4) { width: 8%  }   /* Km          */
.tbl--hist th:nth-child(5) { width: 12% }   /* Import total*/
.tbl--hist th:nth-child(6) { width: 12% }   /* Savings     */
.tbl--hist th:nth-child(7) { width: 12% }   /* PT median   */
.tbl--hist th:nth-child(8) { width: 9%  }   /* Status      */
.tbl--hist th:nth-child(9) { width: 10% }   /* Anúncio     */
/* Tuned against the real 1060px content column at 1280: Date holds "20/07 00:28", IP holds a full
   IPv4, PT median holds "sem comparáveis". Car and Conta are the two that still clip, and both are
   free text with a tooltip — the deliberate trade. */
/* ELEVEN tracks summing to 100 since the Associar column was removed (2026-07-27): its 7% went to
   the two columns that were clipping worst, Car and Conta, plus a point each to the money columns. */
.tbl--hist.has-identity th:nth-child(1)  { width: 6.9% } /* Date — sized to "07/09 10:32" */
/* ⚠️THE COLUMN THAT ACTUALLY CARRIES THE SUBJECT OF THE ROW. At 10% (122px) sixteen of twenty-five
   live rows clipped, "Mercedes-Benz V 250 2020" among them — the make alone ate the cell. 13.1%
   is 160px, which holds that row in full once HIST_MAKE_SHORT renders it as "Mercedes V 250 2020".
   A mobile.de free-text title can still be seventy characters and will still clip; that one keeps
   its tooltip, which is the deliberate trade this table has always made. */
.tbl--hist.has-identity th:nth-child(2)  { width: 13.1% } /* Car        */
.tbl--hist.has-identity th:nth-child(3)  { width: 5.5% } /* Fuel    */
.tbl--hist.has-identity th:nth-child(4)  { width: 5.5% } /* Km      */
.tbl--hist.has-identity th:nth-child(5)  { width: 10% }  /* Total   */
.tbl--hist.has-identity th:nth-child(6)  { width: 10% }  /* Savings */
.tbl--hist.has-identity th:nth-child(7)  { width: 10% }  /* PT median */
.tbl--hist.has-identity th:nth-child(8)  { width: 9%  }   /* Status  */
.tbl--hist.has-identity th:nth-child(9)  { width: 10% }  /* IP — a full IPv4 must not clip */
.tbl--hist.has-identity th:nth-child(10) { width: 10% }  /* Conta — clips, has title */
/* ⚠️9%, not 6%: this column's whole job is now to NAME the site, and at 6% "standvirtual" clipped
   to "stand…" / "mobile.de" to "mobil…" — which threw away the very thing the old Source column
   was dropped in favour of. Sized to the longest real label ("standvirtual"). */
.tbl--hist.has-identity th:nth-child(11) { width: 10% }  /* Anúncio */

/* ⚠️A FLOOR UNDER THE TWO COLUMNS WHOSE CONTENT HAS A KNOWN MINIMUM, and deliberately set BELOW
   what the percentages compute at 1280 so it never widens the table there — nav_layout_test 9g
   pins "the container cannot scroll horizontally" at that width and this must not be what breaks
   it. Below about 1240 the percentages fall under the content and the floor takes over; the
   .table-scroll box already exists for exactly that. */
main .table-scroll > table.tbl--hist th:nth-child(1) { min-width: 82px; }   /* "07/09 10:32" */
main .table-scroll > table.tbl--hist th:nth-child(2) { min-width: 150px; }  /* an abbreviated make + model + year */

/* ── Poupança (column 6 in ALL THREE Histórico width tables) ──────────────────────────────────
   ⚠️RIGHT-ALIGNED BECAUSE IT IS A MONEY COLUMN READ AGAINST ITSELF. Scanning a column of figures
   for the big one is a digit-alignment task, and left-aligned currency defeats it. Halving the
   padding to 4px buys eight pixels back for the figure without touching the column's width.
   ⚠️SCOPED TO THE TABLE MODE. At <=820px these rows become stacked cards where every cell is a
   labelled grid row, and right-aligning one of them there would just look broken. */
@media (min-width: 821px) {
  /* ⚠️:not(.tbl--hist-procura) — column 6 is Poupança on the Website tab, but the Procura tab is a
     different set of columns now (one row per run) and its column 6 is not money. Procura's own
     numeric columns are right-aligned separately, further down. */
  main .table-scroll > table.tbl--hist:not(.tbl--hist-procura) th:nth-child(6),
  main .table-scroll > table.tbl--hist:not(.tbl--hist-procura) td:nth-child(6) {
    padding-left: 4px;
    padding-right: 4px;
    text-align: right;
  }
}

/* ── Car finder ── 10 columns summing to 100%: fit is STRUCTURAL, never a scrollbar.
   Carro is the one that clips and it carries a title tooltip — the usual deliberate trade. */
.tbl--finder th:nth-child(1)  { width: 22% }  /* Carro    */
.tbl--finder th:nth-child(2)  { width: 9%  }  /* Site — sized to "standvirtual" */
.tbl--finder th:nth-child(3)  { width: 6%  }  /* Ano      */
.tbl--finder th:nth-child(4)  { width: 8%  }  /* Km       */
.tbl--finder th:nth-child(5)  { width: 11% }  /* Preço    */
.tbl--finder th:nth-child(6)  { width: 11% }  /* Total    */
.tbl--finder th:nth-child(7)  { width: 11% }  /* Poupança */
.tbl--finder th:nth-child(8)  { width: 10% }  /* ISV      */
.tbl--finder th:nth-child(9)  { width: 5%  }  /* Fotos    */
.tbl--finder th:nth-child(10) { width: 7%  }  /* acção    */
/* A skipped candidate is still information: dimmed, not hidden. */
.tbl--finder .muted-row td { color: var(--muted); }
.tbl--finder .skip-reason { font-style: italic; }
/* A negative saving means importing costs MORE than buying in PT. It must LOOK different, because
   reading it as a positive would invert the decision it exists to inform. */
.tbl--finder .neg { color: #e5534b; }

/* Criteria form: wraps instead of scrolling, and collapses to one column on a phone. */
.finder-form { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; margin: 12px 0 4px; }
.finder-field { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--muted); min-width: 0; }
.finder-field input, .finder-field select {
  background: var(--panel); color: var(--text); border: 1px solid var(--border-strong);
  border-radius: 6px; padding: 6px 8px; font: inherit; min-width: 0; width: 150px;
}
@media (max-width: 820px) {
  .finder-form { flex-direction: column; align-items: stretch; }
  .finder-field input, .finder-field select { width: 100%; }
}

/* ── Conversas ── the last-message preview takes the slack and truncates */
.tbl--conv th:nth-child(1) { width: 9%  }
.tbl--conv th:nth-child(2) { width: 19% }
.tbl--conv th:nth-child(3) { width: 19% }
.tbl--conv th:nth-child(4) { width: 6%  }
.tbl--conv th:nth-child(5) { width: 13% }
.tbl--conv th:nth-child(6) { width: 34% }

/* ── Casos ── Year is merged into Vehicle, so 12 columns incl. the ✕ action */
.tbl--cases th:nth-child(1)  { width: 14% }  /* Client  */
.tbl--cases th:nth-child(2)  { width: 17% }  /* Vehicle */
.tbl--cases th:nth-child(3)  { width: 7%  }  /* Origem  */
.tbl--cases th:nth-child(4)  { width: 9%  }  /* Status  */
.tbl--cases th:nth-child(5)  { width: 8%  }  /* Stage   */
.tbl--cases th:nth-child(6)  { width: 6%  }  /* Tier    */
.tbl--cases th:nth-child(7)  { width: 8%  }  /* ISV     */
.tbl--cases th:nth-child(8)  { width: 7%  }  /* Payment */
.tbl--cases th:nth-child(9)  { width: 5%  }  /* Chat    */
.tbl--cases th:nth-child(10) { width: 5%  }  /* Docs    */
.tbl--cases th:nth-child(11) { width: 6%  }  /* Case id */
.tbl--cases th:nth-child(12) { width: 8%  }  /* status action + ✕ (two buttons) */

/* ── Atividade ── */
/* ⚠️Must MATCH the colgroup built in app.js renderAtividade — two declarations of the same six
   tracks, and a disagreement shows up as a column that jumps when the table repaints. Col 5 is
   Descrição (prose, widest); it took the space back from Utilizador and Ação. Totals 100%. */
.tbl--audit th:nth-child(1) { width: 15% }
.tbl--audit th:nth-child(2) { width: 19% }
.tbl--audit th:nth-child(3) { width: 8%  }
.tbl--audit th:nth-child(4) { width: 17% }
.tbl--audit th:nth-child(5) { width: 31% }
.tbl--audit th:nth-child(6) { width: 10% }

/* ⚠️ACTION-ONLY CELLS MUST NOT BE TRUNCATED. The fit pass put
   overflow:hidden + text-overflow:ellipsis + nowrap on EVERY cell, which is right for text and
   wrong for a control: the Casos ✕ is a 28px button in a 42px cell whose 8px+8px padding leaves
   only 26px, so the line overflowed (scrollWidth 57 vs clientWidth 42) and the browser painted an
   ellipsis for it — inheriting the danger button's red at the point of truncation, and itself
   clipped by the cell, so what actually rendered was TWO small red dots beside the ✕.
   An ellipsis means "there is more text"; there is no more text, there is a button. So: no
   ellipsis, and enough room that nothing clips. Identified by the absence of data-label — an
   action cell has no header, so finishTable never stamps one (same selector the stacked-card
   layout already uses). */
main .table-scroll > table td:not([data-label]) {
  text-overflow: clip;
  overflow: visible;
  padding-left: 4px;
  padding-right: 4px;
  text-align: center;
}

/* compact Histórico status pills */
main .table-scroll > table td.st-ok   { color: #3ad07a; font-weight: 700; }
main .table-scroll > table td.st-err  { color: #e5534b; }
main .table-scroll > table td.st-warn { color: #d9a441; }

/* ── Capped height so BOTH scrollbars stay on screen (desktop) ──────────────────────────────
   Wrapping the table fixed the column squeeze but not the reported symptom: the horizontal bar
   still lives at the bottom edge of the scroll box, and the real Histórico is 105 rows, so at
   1280x900 that bar sat 3417px below the fold. A vertical mouse wheel does not pan a horizontal
   scroller, so a desktop mouse user still had nothing to grab. Capping the box is the only thing
   that puts the horizontal bar on screen AT ANY ROW COUNT.

   ⚠️DESKTOP ONLY, DELIBERATELY. On a phone the horizontal axis is already solved (you swipe the
   rows) and a nested vertical scroller is a net loss: at 390x844 a 70dvh box eats most of the
   screen and the page can only be scrolled past it by finding the gap around it. So ≤640px keeps
   the previous behaviour — horizontal wrapper, page scrolls vertically, no cap, no sticky header.

   Self-limiting by construction: a table shorter than the cap renders at its natural height with
   no vertical bar, so SHORT tables (Portfólio, Atividade, a filtered Casos) are untouched and no
   rule has to classify a table as long or short. */
@media (min-width: 641px) {
  main .table-scroll {
    max-height: 70vh;
    max-height: 70dvh;   /* dvh where supported; the vh line above is the fallback */
    overflow-y: auto;    /* vertical only — see the overflow-x:hidden note on .table-scroll */
  }

  /* ⚠️LOAD-BEARING: the base `table { overflow: hidden }` (it exists only to clip the border
     radius) makes the TABLE its own scrollport, and a sticky cell sticks to its NEAREST
     scrollport — so with it in place the header would stick to the table, i.e. not stick at all
     while the wrapper scrolls. The radius is clipped by the wrapper instead. */
  main .table-scroll > table { overflow: visible; }

  main .table-scroll > table thead th {
    position: sticky;
    top: 0;
    z-index: 2;
    background: var(--panel-2);   /* opaque, or the rows show through as they pass underneath */
    /* ⚠️border-collapse:collapse does NOT carry a cell border with a sticky cell — the header's
       bottom border belongs to the collapsed grid and scrolls away with the rows. An inset shadow
       is painted inside the cell, so it travels with the sticky header. */
    box-shadow: inset 0 -1px 0 var(--border);
  }
}
h2 { font-size: 18px; margin: 0 0 14px; color: var(--text-strong); }
.muted { color: var(--muted); }

.toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 12px; }
input[type=search] {
  padding: 7px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  width: 260px;
  font-size: 14px;
  background: var(--panel-2);
  color: var(--text);
}
input[type=search]::placeholder { color: var(--muted); }
input[type=search]:focus { outline: none; border-color: var(--accent); }

table {
  width: 100%;
  border-collapse: collapse;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow: hidden;
  box-shadow: var(--shadow);
}
th, td { text-align: left; padding: 9px 12px; border-bottom: 1px solid var(--border); vertical-align: top; }
th {
  background: var(--panel-2);
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: .4px;
  color: var(--muted);
  cursor: pointer;
  user-select: none;
  white-space: nowrap;
}
tbody tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--panel-2); }
td a { color: var(--accent); text-decoration: none; font-weight: 500; }
td a:hover { text-decoration: underline; }

.tag { display: inline-block; font-size: 11px; padding: 2px 7px; border-radius: 10px; background: var(--border); color: var(--text); }
.tag.wa { background: #122a1e; color: #57ab5a; }
.tag.web { background: #12233a; color: #6cb6ff; }
.tag.other { background: #221b34; color: #b083f0; }
/* ---- Import origin (UE / não-UE) ----
   UE is the norm and reads as quiet chrome; não-UE is the exception and carries an amber outline so
   it is findable when scanning a full Cases table or a pipeline board. Deliberately NOT red — a
   non-EU import is a different process, not an error state. */
.tag.origin-badge { background: transparent; border: 1px solid var(--border-strong, #3a3f55); color: var(--muted, #98a0b3); font-weight: 500; letter-spacing: .2px; }
.tag.origin-badge.noneu { background: rgba(220, 160, 40, 0.12); border-color: #c9922e; color: #e0a83a; font-weight: 600; }
/* The buyer-type marker on a Histórico Procura row (2026-09-04). Quiet on purpose: it sits INSIDE
   the Iniciador cell beside a name, so it has to read as an annotation on that row rather than
   compete with it — the same weight, for the same reason, as .tag.origin-badge above. Only a
   COMPANY run is marked, so there is no private counterpart needing a colour of its own. */
.tag.buyer-company { background: transparent; border: 1px solid var(--border-strong, #3a3f55); color: var(--muted, #98a0b3); font-weight: 600; letter-spacing: .2px; margin-left: 6px; }
/* the case-view identity line: "Case <id>  [badge] [✎ origem]" on one baseline */
.case-idline { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 2px; }
.case-idline .origin-edit { padding: 2px 8px; font-size: 11px; }
/* full-width sub-heading inside a group card's kv grid (see groupCard 'sep' rows) */
.kv-sep { grid-column: 1 / -1; font-size: 11px; font-weight: 600; letter-spacing: .5px; text-transform: uppercase; color: var(--muted, #98a0b3); border-bottom: 1px solid var(--border, #2a2a2a); padding-bottom: 3px; margin-top: 6px; }
.kv-sep:first-child { margin-top: 0; }
/* "+ New case" origin picker: select above a one-line explanation of what não-UE turns on */
.origin-col { display: flex; flex-direction: column; gap: 4px; min-width: 300px; max-width: 420px; }
.form .origin-col select { background: var(--panel-2); border: 1px solid var(--border-strong); border-radius: 6px; color: var(--text); padding: 7px 10px; font-size: 14px; }
.form .origin-col select:focus { outline: none; border-color: var(--accent); }
.origin-hint { font-size: 12px; line-height: 1.45; }

.tag.good { background: #122a1e; color: #57ab5a; font-size: 13px; padding: 4px 12px; }
.tag.bad { background: #2d1618; color: #e5534b; font-size: 13px; padding: 4px 12px; }

input[type=date] {
  padding: 7px 10px;
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  font: inherit;
  font-size: 14px;
  background: var(--panel-2);
  color: var(--text);
}
input[type=date]:focus { outline: none; border-color: var(--accent); }

.back { display: inline-block; margin-bottom: 12px; color: var(--accent); text-decoration: none; }
.back:hover { text-decoration: underline; }

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px 16px;
  box-shadow: var(--shadow);
  margin-bottom: 16px;
}

.chat { display: flex; flex-direction: column; gap: 8px; }
.bubble { max-width: 72%; padding: 8px 12px; border-radius: 12px; white-space: pre-wrap; word-break: break-word; }
.bubble.user { align-self: flex-start; background: var(--panel-2); border: 1px solid var(--border-strong); color: var(--text); }
.bubble.assistant { align-self: flex-end; background: var(--accent-soft); border: 1px solid #2f5279; color: var(--text-strong); }
.bubble .meta { font-size: 10px; opacity: .65; margin-top: 4px; }

details { background: var(--panel); border: 1px solid var(--border); border-radius: 6px; margin-bottom: 8px; }
details > summary { cursor: pointer; padding: 8px 12px; font-weight: 600; color: var(--text-strong); }
details pre {
  margin: 0;
  padding: 10px 12px 12px;
  overflow-x: auto;
  font-size: 12.5px;
  background: var(--bg);
  color: var(--text);
  border-top: 1px solid var(--border);
}
.kv { display: grid; grid-template-columns: max-content 1fr; gap: 4px 16px; padding: 4px 12px 10px; }
.kv .k { color: var(--muted); }

.docs a {
  display: inline-block;
  margin: 4px 8px 4px 0;
  padding: 6px 10px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  text-decoration: none;
  color: var(--accent);
}
.docs a:hover { border-color: var(--accent); }

.excluded { font-size: 12px; color: var(--muted); margin-top: 14px; }
.err { color: #e5534b; }

/* Portfolio: buttons, forms, photo thumbnails */
.btn {
  display: inline-block;
  padding: 6px 12px;
  margin: 8px 0;
  border-radius: 6px;
  border: 1px solid var(--border-strong);
  background: var(--panel-2);
  color: var(--text);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  text-decoration: none;
}
.btn:hover { border-color: var(--accent); }
.btn.primary { background: var(--accent-soft); border-color: #2f5279; color: var(--text-strong); }
.btn.danger { color: #e5534b; }
.btn.danger:hover { border-color: #e5534b; }
.btn.small { padding: 2px 8px; font-size: 12px; margin: 0; }

.form .frow { display: flex; align-items: flex-start; gap: 12px; margin-bottom: 10px; }
.form .frow > label:first-child { width: 130px; color: var(--muted); flex: none; padding-top: 6px; }
.form input[type=text], .form input[type=number], .form textarea {
  background: var(--panel-2);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  color: var(--text);
  padding: 6px 9px;
  font: inherit;
  font-size: 14px;
  min-width: 300px;
}
.form input:focus, .form textarea:focus { outline: none; border-color: var(--accent); }
.form textarea { resize: vertical; }

img.thumb { width: 96px; height: 64px; object-fit: cover; border-radius: 5px; border: 1px solid var(--border); display: block; background: var(--panel-2); }
.photos { display: flex; flex-wrap: wrap; gap: 14px; margin-bottom: 12px; }
.photo { position: relative; }
.photo .btn.danger.small {
  position: absolute; top: -8px; right: -8px;
  width: 22px; height: 22px; padding: 0; line-height: 20px; text-align: center;
  border-radius: 50%; background: var(--panel);
}

/* Pipeline kanban */
.kanban { display: flex; gap: 12px; overflow-x: auto; padding-bottom: 10px; align-items: flex-start; }
.kcol {
  flex: 1 0 200px;
  min-width: 200px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow);
}
.kcol-head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 9px 11px;
  border-bottom: 1px solid var(--border);
  background: var(--panel-2);
  border-radius: 8px 8px 0 0;
}
.kcol-title { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; color: var(--text-strong); }
.kcol-count { font-size: 12px; color: var(--muted); background: var(--bg); border-radius: 10px; padding: 1px 8px; }
.kcol-body { padding: 9px; display: flex; flex-direction: column; gap: 9px; min-height: 40px; }
.kcard {
  background: var(--panel-2);
  border: 1px solid var(--border-strong);
  border-radius: 7px;
  padding: 9px 10px;
}
.kcard-title a { color: var(--accent); text-decoration: none; font-weight: 600; }
.kcard-title a:hover { text-decoration: underline; }
.kcard-sub { color: var(--text); font-size: 13px; margin-top: 2px; }
.kcard-meta { margin-top: 6px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.kcard-unset { color: #d6a01a; font-size: 11px; margin-top: 5px; }
.kselect {
  margin-top: 9px;
  width: 100%;
  background: var(--bg);
  color: var(--text);
  border: 1px solid var(--border-strong);
  border-radius: 5px;
  padding: 5px 7px;
  font: inherit;
  font-size: 13px;
}
.kselect:focus { outline: none; border-color: var(--accent); }
/* drag-and-drop + fixed lanes / sticky headers / per-column scroll (task 1) */
.kanban { height: calc(100vh - 150px); align-items: stretch; overflow-x: auto; overflow-y: hidden; }
.kcol { display: flex; flex-direction: column; flex: 0 0 260px; min-width: 260px; max-width: 260px; height: 100%; }
.kcol-head { position: sticky; top: 0; z-index: 2; }
.kcol-body { flex: 1 1 auto; min-height: 80px; overflow-y: auto; }
.kcard { cursor: grab; }
.kcard:active { cursor: grabbing; }
.kcard.dragging { opacity: .45; }
.kcol.kcol-over { background: var(--accent-soft); }
/* archive X (task 2) — muted red, brightens on hover */
.kcard { position: relative; }
.kcard-x {
  position: absolute; top: 4px; right: 5px;
  width: 18px; height: 18px; padding: 0; line-height: 16px; text-align: center;
  background: transparent; border: none; cursor: pointer;
  color: #b06a6a; font-size: 16px; border-radius: 4px;
}
.kcard-x:hover { color: #e5534b; background: rgba(229,83,75,0.12); }
/* checklist + ready badge (task 3) */
.kcard-check { margin-top: 8px; display: flex; flex-direction: column; gap: 5px; }
.kcheck-row { display: flex; align-items: center; gap: 7px; font-size: 12px; color: var(--text); cursor: pointer; }
.kcheck-row input { width: 18px; height: 18px; cursor: pointer; flex: none; accent-color: #57ab5a; }
.kcard-ready { margin-top: 7px; color: #57ab5a; font-size: 11px; font-weight: 700; letter-spacing: .3px; }
.kcard--ready { border-color: #57ab5a; box-shadow: 0 0 0 1px #57ab5a inset; }
.kcol-archive .kcol-head { background: #2a2420; }
/* confirm modal (task 3) */
.modal-overlay { position: fixed; inset: 0; background: rgba(0,0,0,.55); display: flex; align-items: center; justify-content: center; z-index: 1000; opacity: 0; transition: opacity .18s ease; padding: 16px; }
.modal-overlay.open { opacity: 1; }
.modal { background: var(--panel); border: 1px solid var(--border-strong); border-radius: 10px; box-shadow: var(--shadow); padding: 20px; width: min(92vw, 420px); transform: translateY(8px); transition: transform .18s ease; }
.modal-overlay.open .modal { transform: translateY(0); }
.modal-msg { margin: 0 0 18px; color: var(--text-strong); font-size: 15px; line-height: 1.4; }
.modal-actions { display: flex; gap: 10px; justify-content: flex-end; }
.modal-actions .btn { min-width: 92px; }
.btn.ghost { background: transparent; color: var(--muted); border: 1px solid var(--border-strong); }
.btn.ghost:hover { color: var(--text-strong); }
/* mobile (task 3) */
@media (max-width: 640px) {
  main { padding: 12px; }
  nav { flex-wrap: wrap; }
  .kanban { height: calc(100dvh - 130px); scroll-snap-type: x mandatory; -webkit-overflow-scrolling: touch; }
  .kcol { flex: 0 0 85vw; min-width: 85vw; max-width: 85vw; scroll-snap-align: start; }
  .kselect { min-height: 34px; }
  .kcheck-row { font-size: 13px; }
  .kcard-x { width: 26px; height: 26px; font-size: 18px; line-height: 24px; top: 3px; right: 4px; }
}
/* desktop: all 5 lanes on screen at 100% zoom — full-bleed board out of the 1100px cap + flexible lanes (task 3c) */
@media (min-width: 641px) {
  .kanban { width: 100vw; margin-left: calc(50% - 50vw); padding-left: 20px; padding-right: 20px; box-sizing: border-box; overflow-x: auto; }
  .kcol { flex: 1 1 0; min-width: 200px; max-width: none; }
}
/* extraction confirm view (task 3e) — scoped row tightening, does NOT touch Cases/Portfolio tables */
.extract-table th, .extract-table td { padding: 4px 8px; vertical-align: top; font-size: 12px; }
.extract-table input[type=text] { padding: 3px 6px; font-size: 12px; }
.extract-table .err { font-size: 11px; margin: 2px 0 0; line-height: 1.3; }

/* Dark scrollbars (WebKit) to match the theme */
::-webkit-scrollbar { width: 12px; height: 12px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb { background: var(--border-strong); border-radius: 6px; border: 3px solid var(--bg); }
::-webkit-scrollbar-thumb:hover { background: var(--muted); }

/* ---- Step 2: homologation modal workspace ---- */
.modal-backdrop { position: fixed; inset: 0; background: rgba(0,0,0,0.6); display: flex; align-items: flex-start; justify-content: center; z-index: 1000; overflow-y: auto; padding: 40px 16px; }
.modal-panel { background: #1c1f2a; border: 1px solid var(--border-strong, #3a3f55); border-radius: 10px; width: 780px; max-width: 100%; box-shadow: 0 12px 40px rgba(0,0,0,0.5); display: flex; flex-direction: column; }
.modal-head { display: flex; align-items: center; justify-content: space-between; padding: 14px 18px; border-bottom: 1px solid var(--border, #2a2a2a); }
.modal-head h2 { margin: 0; font-size: 18px; }
.modal-close { background: transparent; border: none; color: var(--muted, #98a0b3); font-size: 20px; cursor: pointer; line-height: 1; padding: 4px 10px; }
.modal-close:hover { color: #fff; }
.modal-body { padding: 16px 18px; }
.modal-body h3 { margin: 16px 0 6px; font-size: 14px; }
.modal-body h3:first-child { margin-top: 0; }
.step5-controls h3 { font-size: 14px; margin: 16px 0 6px; }   /* Submissão block moved to the case page — match the modal h3 size */
.modal-footer { display: flex; align-items: center; justify-content: flex-end; gap: 8px; padding: 12px 18px; border-top: 1px solid var(--border, #2a2a2a); }
.checklist { display: flex; flex-direction: column; gap: 3px; }
.checklist-row { display: grid; grid-template-columns: 20px 220px 1fr; gap: 8px; align-items: center; font-size: 13px; }
.cl-mark { text-align: center; font-weight: bold; }
.cl-mark.ok { color: #3ad07a; }
.cl-mark.bad { color: #e5534b; }
.cl-label { color: var(--muted, #98a0b3); }

/* ---- Step 3: confirm table (modal) ---- */
.confirm-table { width: 100%; border-collapse: collapse; margin-top: 6px; }
.confirm-table th { text-align: left; color: var(--muted, #98a0b3); border-bottom: 1px solid var(--border-strong, #3a3f55); }
.confirm-table td { border-bottom: 1px solid var(--border, #2a2a2a); vertical-align: top; }
.confirm-table .src-win { font-weight: bold; }
.row-mismatch { background: rgba(220, 180, 40, 0.10); }
.tag.warn { background: #6a5a10; color: #ffd84d; }

/* ---- Shared modal input style (submitter editor + confirm list) — replaces the flat system dark input ---- */
.field-input {
  background: var(--panel-2);
  border: 0.5px solid var(--border-strong);
  border-radius: 6px;
  color: var(--text-strong);   /* full-primary so values read as editable, not muted */
  height: 36px;
  padding: 0 12px;
  font: inherit;
  font-size: 14px;
  width: 100%;
  box-sizing: border-box;
}
.field-input:focus { outline: none; border-color: var(--accent); }
.field-input::placeholder { color: var(--muted); }

/* ---- CHANGE 1: shared modal field-row layout (Requerente editor + Confirmação list) — 130px label, input fills the rest, vertically centered ---- */
.modal-field-grid { display: grid; grid-template-columns: 130px 1fr; gap: 14px; align-items: center; }
/* CHANGE 2: center the field block (~520px) and align its headings + readiness tags to the same column */
.modal-body .modal-col { max-width: 520px; margin-left: auto; margin-right: auto; }
.field-label { font-size: 14px; color: var(--text); }
/* CHANGE 2: CO₂ divergence flag — same yellow as the confirm table's .row-mismatch / .tag.warn */
.field-input.diverge { border-color: #ffd84d; background: rgba(220, 180, 40, 0.12); }

/* case hard-delete (typed-confirm modal + archive-card button) */
.del-confirm-input { width: 100%; box-sizing: border-box; padding: 8px 10px; margin-top: 4px; background: #12141c; border: 1px solid var(--border-strong, #3a3f55); border-radius: 6px; color: var(--text-strong, #fff); font-size: 14px; }
.del-confirm-input:focus { outline: none; border-color: var(--accent); }
.kcard-x-del { color: #e5534b; }
.kcard-x-del:hover { color: #fff; background: rgba(229,83,75,0.22); }

/* =============================================
   CASE DETAIL — responsive two-column layout (2026-07-11, presentation only)
   Desktop (>=1000px): Resumo do caso (left) | Documents + Upload + Extração (right),
   page widened to use the full width. Below 1000px: single column (stacks as before).
   ============================================= */
.case-grid { display: block; }
.case-col { min-width: 0; }
@media (min-width: 1000px) {
  /* the case view is wider — widen the whole column so the header still lines up with it */
  body.body--case { --content-max: 1320px; }
  .case-grid {
    display: grid;
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: 16px;
    align-items: start;
  }
}

/* =============================================
   STATS tab — read-only aggregate dashboard (2026-07-12)
   Simple CSS bar/split charts + stat tiles, in the existing dark theme.
   ============================================= */
.stat-section {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow);
  padding: 14px 16px 18px;
  margin-bottom: 16px;
}
.stat-h { font-size: 14px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; color: var(--text-strong); margin: 0 0 12px; }

.stat-row { display: flex; flex-wrap: wrap; gap: 12px; margin-bottom: 6px; }
.stat-tile {
  flex: 1 1 130px; min-width: 120px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 7px;
  padding: 12px 14px;
}
.stat-num { font-size: 26px; font-weight: 700; color: var(--text-strong); line-height: 1.1; }
.stat-tile.ok .stat-num { color: #57ab5a; }
.stat-tile.warn .stat-num { color: #d6a01a; }
.stat-tile.bad .stat-num { color: #e5534b; }
.stat-label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .3px; margin-top: 4px; }
.stat-sub { font-size: 11px; color: var(--muted); margin-top: 2px; }

/* stacked vertical bar chart */
.chart { margin: 14px 0 4px; }
.chart-ymax { font-size: 11px; color: var(--muted); margin-bottom: 4px; }
.chart-bars {
  display: flex; align-items: flex-end; gap: 3px;
  height: 150px;
  border-bottom: 1px solid var(--border);
  padding-bottom: 0;
}
.chart-col {
  flex: 1 1 0; min-width: 4px; height: 100%;
  display: flex; flex-direction: column-reverse;
  background: linear-gradient(var(--bg), var(--bg)) no-repeat bottom / 100% 2px; /* faint baseline */
}
.chart-col:hover { background: var(--panel-2); }
.chart-seg { width: 100%; min-height: 3px; border-radius: 1px 1px 0 0; }
.chart-x { display: flex; justify-content: space-between; font-size: 11px; color: var(--muted); margin-top: 4px; }

/* segment + legend colours */
.seg-success { background: #347d39; }
.seg-needs   { background: #c69026; }
.seg-error   { background: #c93c37; }
.seg-acct    { background: #539bf5; }
.seg-anon    { background: #6e7681; }
.seg-unknown { background: #3d444d; }
.seg-wa      { background: #347d39; }
.seg-email   { background: #539bf5; }
.seg-contact { background: #8957e5; }
.seg-web     { background: #3f8fd0; }
.seg-crm     { background: #9a6a3a; }
.seg-other   { background: #57606a; }

/* The realtime refresh sits under the tiles, not beside one — see renderWebsite. */
.rt-bar { margin: 0 0 12px; }
/* Website (GA4) tables: label / value / share. Fixed layout like every other table here, so a
   long page path is CLIPPED with an ellipsis rather than widening the table into a sideways
   scroller — the house rule the nav_layout_test sweep enforces at 390/768/1280/1440. */
.tbl--web th:nth-child(1) { width: 60% }
.tbl--web th:nth-child(2) { width: 22% }
.tbl--web th:nth-child(3) { width: 18% }
.legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; font-size: 12px; color: var(--muted); }
.legend-item { display: inline-flex; align-items: center; gap: 6px; }
.legend-sw { width: 11px; height: 11px; border-radius: 2px; display: inline-block; }

/* horizontal proportion (split) bar */
.sub-block { margin-top: 16px; }
.sub-title { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .3px; margin: 14px 0 6px; }
.split-bar { display: flex; height: 22px; border-radius: 5px; overflow: hidden; background: var(--bg); border: 1px solid var(--border); }
.split-seg { height: 100%; }
.split-empty { color: var(--muted); font-size: 12px; padding: 3px 8px; }

.stat-footer { font-size: 12px; color: var(--muted); margin-top: 4px; line-height: 1.6; }
.stat-footer .gap-title { color: #d6a01a; margin-top: 8px; font-weight: 600; }
.stat-gaps { margin: 4px 0 0; padding-left: 18px; }
.stat-gaps li { margin: 2px 0; }

/* =============================================
   MOBILE DOC CAPTURE (2026-07-12) — "Fotografar documento" in the case Upload card:
   phone camera -> N pages -> one combined PDF. Plus a <480px pass of the upload area.
   ============================================= */
/* "Fotografar documento" is a touch/small-screen feature (camera capture) — hidden by default at
   desktop widths and revealed at <=820px (the stacked-card breakpoint). See the @media block below. */
.capture-block { display: none; margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--border); }
.capture-title { font-weight: 600; color: var(--text-strong); margin-bottom: 4px; }
.capture-hint { font-size: 12px; margin: 0 0 10px; }
.capture-thumbs { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 10px; }
.capture-thumb { position: relative; width: 84px; }
.capture-thumb img { width: 84px; height: 108px; object-fit: cover; border-radius: 6px; border: 1px solid var(--border-strong); background: var(--panel-2); display: block; }
.capture-thumb-n { position: absolute; bottom: 3px; left: 3px; background: rgba(0,0,0,.65); color: #fff; font-size: 11px; padding: 0 5px; border-radius: 8px; }
.capture-thumb-x {
  position: absolute; top: -8px; right: -8px;
  width: 22px; height: 22px; padding: 0; line-height: 20px; text-align: center;
  border-radius: 50%; background: var(--panel); border: 1px solid var(--border-strong);
  color: #e5534b; cursor: pointer; font-size: 13px;
}
.capture-thumb-x:hover { border-color: #e5534b; }
.capture-row { display: flex; align-items: center; gap: 10px; margin-bottom: 10px; }
.capture-type { background: var(--panel-2); color: var(--text); border: 1px solid var(--border-strong); border-radius: 6px; padding: 7px 9px; font: inherit; font-size: 14px; }
.capture-type:focus { outline: none; border-color: var(--accent); }
.capture-actions { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.capture-count { font-size: 12px; }
.capture-msg { display: block; margin-top: 8px; font-size: 13px; }
.capture-msg.ok { color: #57ab5a; }
.capture-msg.err { color: #e5534b; }

/* <480px: finger-friendly upload + capture (full mobile pass of admin is a separate task) */
@media (max-width: 480px) {
  .upload-drop { padding: 20px 12px !important; }
  .upresults .uprow { word-break: break-word; }
  .capture-actions { flex-direction: column; align-items: stretch; }
  .capture-add, .capture-save { width: 100%; padding: 12px; font-size: 15px; margin: 4px 0; }
  .capture-row { flex-direction: column; align-items: stretch; gap: 6px; }
  .capture-type { width: 100%; padding: 11px 10px; font-size: 15px; }
  .capture-count { text-align: center; }
  .capture-thumb, .capture-thumb img { width: 29vw; }
  .capture-thumb img { height: 37vw; }
  .capture-thumb-x { width: 28px; height: 28px; line-height: 26px; font-size: 15px; }
}

/* =============================================
   FULL MOBILE RESPONSIVE PASS — 360–480px (2026-07-13, presentation only)
   Header nav → horizontal scroll (no wrap-break); list tables → in-view
   horizontal scroll with a pinned first column; 16px inputs (no iOS zoom);
   ≥40px tap targets; safe-area insets on fixed overlays. Pipeline (85vw snap
   lanes), case-grid stacking, and the Stats charts already fit at ≤640.
   ============================================= */
@media (max-width: 640px) {
  /* --- Header: compact; nav scrolls horizontally on its own row (never wraps/breaks) --- */
  header {
    flex-wrap: wrap;
    gap: 8px 12px;
    padding: 10px 12px;
    padding-top: calc(10px + env(safe-area-inset-top));
    padding-left: max(12px, env(safe-area-inset-left));
    padding-right: max(12px, env(safe-area-inset-right));
  }
  /* the bar itself is padded at this width (safe-area insets above), so the inner column must
     not add the desktop gutter on top of it */
  .header-inner { padding: 0; }
  header h1.brand { flex: 0 0 auto; }
  .brand-tile { width: 28px; height: 28px; }
  .header-inner { flex-wrap: wrap; gap: 8px 12px; }
  nav {
    order: 3;
    width: 100%;
    flex-wrap: nowrap;
    overflow-x: auto;
    gap: 4px;
    padding-bottom: 2px;
    -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
  }
  nav::-webkit-scrollbar { display: none; }
  nav a { flex: 0 0 auto; white-space: nowrap; padding: 10px 13px; }  /* ~40px tap height */

  /* --- Content: tighter padding + horizontal safe-area --- */
  main {
    padding: 12px;
    padding-left: max(12px, env(safe-area-inset-left));
    padding-right: max(12px, env(safe-area-inset-right));
  }
  h2 { font-size: 17px; }

  /* --- Toolbar + search full width --- */
  .toolbar { flex-wrap: wrap; gap: 8px; }
  input[type=search] { width: 100%; }

  /* --- List tables ---
     ⚠️THE PINNED-FIRST-COLUMN PATTERN IS RETIRED (2026-07-20). It existed only to make horizontal
     scrolling survivable, and there is no horizontal scrolling any more: the tables are
     table-layout:fixed so they cannot exceed their box, and below 820px they become STACKED CARDS
     (see the block at the end of this file). nowrap + sticky here would fight that layout. */
  main table { width: 100%; }

  /* long doc-links / URLs / case-ids in content wrap instead of forcing page width */
  #view a { overflow-wrap: anywhere; }
  main table a { overflow-wrap: normal; }  /* but table links stay on one line (cell scrolls) */

  /* --- 16px form controls so iOS Safari never zooms on focus --- */
  input[type=search], input[type=date], input[type=text], input[type=number],
  input[type=email], input[type=tel], select, textarea,
  .field-input, .kselect, .capture-type, .del-confirm-input { font-size: 16px; }
  .extract-table input[type=text] { font-size: 16px; }  /* class rule out-specifies the bare input rule */

  /* --- ≥40px tap targets on the key controls --- */
  .btn { padding: 9px 14px; }
  .btn.small { padding: 7px 11px; font-size: 12px; }
  .field-input { height: 42px; }
  .kselect { min-height: 42px; }
  input[type=checkbox], input[type=radio] { width: 20px; height: 20px; }  /* bigger tap target */
  .kcheck-row input { width: 22px; height: 22px; }
  .btn.small, .del-confirm-input, .kselect { min-height: 40px; }
  .del-confirm-input { padding: 11px 12px; }

  /* --- "+ New case" / portfolio forms: stack label over a full-width input --- */
  .form .frow { flex-direction: column; align-items: stretch; gap: 4px; }
  .form .frow > label:first-child { width: auto; padding-top: 0; }
  .form input[type=text], .form input[type=number], .form textarea { min-width: 0; width: 100%; }

  /* --- Case dashboard KV grid: shrinkable value column + break long unbreakable
         tokens (e.g. the DeclaracaoHomologacao_Signed_*.PDF filename) so a no-space
         value can't force the grid wider than the viewport --- */
  .kv { grid-template-columns: minmax(84px, 40%) minmax(0, 1fr); gap: 3px 10px; }
  .kv > * { min-width: 0; overflow-wrap: anywhere; }

  /* --- Modals: fit small screens, stack the field grid, safe-area bottom --- */
  .modal-overlay { padding: 12px; }
  .modal { width: min(94vw, 440px); }
  .modal-backdrop { padding: 20px 12px calc(20px + env(safe-area-inset-bottom)); }
  .modal-body { padding: 14px 14px; }
  .modal-field-grid { grid-template-columns: 1fr; gap: 6px; align-items: stretch; }
  .modal-body .modal-col { max-width: none; }
  .checklist-row { grid-template-columns: 20px minmax(0, 1fr) auto; gap: 6px; }

  /* --- Stats: a touch tighter so the charts breathe --- */
  .stat-section { padding: 12px 12px 14px; }
  .stat-num { font-size: 22px; }
  .chart-bars { height: 120px; }
}

/* =============================================
   STATS v2 — period toggle + accounts list/drill-down (2026-07-13)
   ============================================= */
.period-toggle {
  display: inline-flex; gap: 3px;
  background: var(--panel); border: 1px solid var(--border);
  border-radius: 8px; padding: 3px; margin-bottom: 16px;
}
.period-btn {
  padding: 7px 18px; border: none; background: none;
  color: var(--muted); font: inherit; font-size: 13px; font-weight: 600;
  border-radius: 6px; cursor: pointer; transition: background .12s, color .12s;
}
.period-btn:hover { color: var(--text-strong); }
.period-btn.active { background: var(--accent-soft); color: var(--text-strong); }

.acct-search {
  width: 340px; max-width: 100%; margin: 4px 0 14px;
  padding: 8px 12px; border: 1px solid var(--border-strong); border-radius: 6px;
  background: var(--panel-2); color: var(--text); font-size: 14px;
}
.acct-search:focus { outline: none; border-color: var(--accent); }
.acct-search::placeholder { color: var(--muted); }

.acctlist { display: flex; flex-direction: column; gap: 8px; }
.acct { border: 1px solid var(--border); border-radius: 8px; background: var(--panel); }
.acct-head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) max-content max-content 18px;
  grid-template-areas: "email dates counts caret";
  gap: 4px 16px; align-items: center;
  width: 100%; padding: 11px 13px;
  background: none; border: none; text-align: left; cursor: pointer;
  color: inherit; font: inherit; border-radius: 8px;
}
.acct-head:hover { background: var(--panel-2); }
.acct.open > .acct-head { border-radius: 8px 8px 0 0; }
.acct-email-wrap { grid-area: email; display: flex; align-items: center; gap: 8px; min-width: 0; }
/* ⚠️user-select:all IS THE FIX FOR "the addresses cannot be copied" — AND THE CAUSE WAS NEVER
   user-select BEING OFF. Measured on the live page before the change: computed user-select was
   `auto` on both the row header and the address, there is no overlay, and forcing
   `user-select: text` changed nothing at all. Two unrelated things were in the way:
     - the row header is a <button>, and Chromium gives content inside one no text-selection
       granularity, so a double-click on an address selected NOTHING;
     - .acct-email-wrap is a flex container, and a drag that runs off the end of a flex item keeps
       going into the next one — a full-span drag returned 307 characters (the address, the quota
       badge, the dates and the three counters) instead of the 27-character address.
   `all` sidesteps both by making the address a single atomic selectable unit: one click puts the
   whole address on the clipboard, and a drag across it cannot bleed into the badge beside it.
   ⚠️It also means the address cannot be PART-selected, which is the intended trade: nobody needs
   half an email address, and the alternative (un-flexing .acct-email-wrap) moves the badge.
   ⚠️THE MARKUP WAS DELIBERATELY LEFT ALONE. A first pass restructured the header to a
   <div role="button">; an A/B then showed the gesture is clean inside the real <button> too once
   this line exists, so the restructure was reverted rather than kept for tidiness. */
.acct-email { font-weight: 600; color: var(--text-strong); overflow-wrap: anywhere; user-select: all; -webkit-user-select: all; }
.qbadge {
  flex: 0 0 auto; font-size: 11px; font-weight: 600; padding: 1px 7px;
  border-radius: 9px; background: var(--panel-2); color: var(--muted);
  border: 1px solid var(--border); white-space: nowrap;
}
.qbadge.inf { color: var(--accent); border-color: #2f5279; }
.qbadge.over { color: #d6a01a; border-color: #4a4220; }
.acct-meta2 { grid-area: dates; font-size: 12px; color: var(--muted); white-space: nowrap; }
.acct-counts2 { grid-area: counts; display: flex; gap: 10px; font-size: 12px; white-space: nowrap; }
.acct-counts2 span { color: var(--muted); }
.acct-caret { grid-area: caret; color: var(--muted); font-size: 12px; text-align: center; }

/* expandable drill-down — grid-rows 0fr→1fr animates to natural height, no clipping */
.acct-detail { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .26s ease; }
.acct.open > .acct-detail { grid-template-rows: 1fr; }
/* The DIRECT grid child must carry no padding/border, or grid-template-rows:0fr can't shrink it
   past that box and ~21px of .acct-detail-in padding+border stays as a residual strip after
   open→close. So the grid child is this bare clip; the padded content nests inside it. */
.acct-detail > * { overflow: hidden; min-height: 0; }
.acct-detail-clip { min-height: 0; }
.acct-detail-in { padding: 6px 13px 14px; border-top: 1px solid var(--border); display: flex; flex-direction: column; gap: 10px; }
.acct-sub-h { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); margin: 6px 0 5px; }
.acct-sub-empty { color: var(--muted); font-size: 13px; }
.acct-sub-list { display: flex; flex-direction: column; }
.acct-sub-row { display: flex; align-items: baseline; gap: 10px; font-size: 13px; padding: 4px 0; border-bottom: 1px solid rgba(255, 255, 255, .045); }
.acct-sub-row:last-child { border-bottom: none; }
.asr-main { flex: 1 1 auto; min-width: 0; color: var(--text); overflow-wrap: anywhere; }
.asr-main.mono { font-family: ui-monospace, "SFMono-Regular", Menlo, monospace; letter-spacing: .3px; }
.asr-date { flex: 0 0 auto; color: var(--muted); font-size: 12px; }
.asr-val { flex: 0 0 auto; color: var(--text-strong); font-weight: 500; }

/* phone: accounts become stacked cards; period pills go full-width */
@media (max-width: 640px) {
  .acct-head {
    grid-template-columns: minmax(0, 1fr) 18px;
    grid-template-areas:
      "email caret"
      "dates dates"
      "counts counts";
    gap: 5px 10px; padding: 12px;
  }
  .acct-meta2, .acct-counts2 { white-space: normal; }
  .acct-counts2 { flex-wrap: wrap; gap: 6px 14px; }
  .period-toggle { display: flex; width: 100%; }
  .period-btn { flex: 1 1 0; min-height: 40px; }
}


/* --- Pending "⚠ couldn't confirm" upload row (app.js renderManualRow) -------------------------
   A native <select> sizes itself to its WIDEST <option>. Since the picker now offers the full
   pt-PT vocabulary, the longest label ("Documento de identificação do comprador (CC/passaporte)")
   stretched it to ~445px — wider than a 390px phone viewport, so the row ran off-screen. Cap it
   to the card, and give it its own full-width line on phones (also an easier tap target). */
.uprow select { max-width: 100%; }
@media (max-width: 640px) {
  .uprow { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
  .uprow select { display: block; width: 100%; flex: 1 0 100%; margin: 2px 0; }
}

/* ================================================================================================
   CASE-VIEW REDESIGN (2026-07-16): Resumo group cards + Documents rows + upload batch bar + the
   extraction wizard modal. Appended block — everything above is untouched.
   ================================================================================================ */

/* --- Resumo do caso: one card per logical group, laid out by the existing .case-grid (which already
   stacks <1000px), so no new responsive machinery. ------------------------------------------------ */
.group-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 10px 12px 4px;
  margin-bottom: 12px;
  background: rgba(255, 255, 255, 0.015);
}
.group-head { display: flex; align-items: center; gap: 8px; margin-bottom: 2px; }
.group-head h3 { margin: 0; font-size: 13px; font-weight: 600; letter-spacing: 0.02em; }
.group-edit { font-size: 11px; padding: 1px 7px; font-weight: 400; margin-left: auto; }
.group-card .kv { padding: 4px 0 6px; font-size: 13px; }

/* --- Documents list: plain rows again (the per-doc Extrair button + its inline review panel are gone;
   extraction lives in the wizard now). ------------------------------------------------------------ */
/* Document rows (case view) — compact INLINE on desktop, STACKED card ≤820px (2026-07-23). The row is
   flex-wrap; on desktop the actions sit right (margin-left:auto), and ≤820px .doc-actions takes a full
   line (flex-basis:100%) so name+chips stay on top and the actions stack below. */
.doc-row { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; padding: 5px 0; border-bottom: 1px solid rgba(255,255,255,0.05); }
.doc-row:last-child { border-bottom: none; }
.doc-row > a { overflow-wrap: anywhere; }
.doc-state { font-size: 10px; padding: 1px 6px; }
.doc-actions { display: flex; align-items: center; gap: 8px; margin-left: auto; }
/* reclassify picker — content-width, subtle border, NOT the wide grey native box */
.doc-reclass { font: inherit; font-size: 13px; padding: 4px 8px; max-width: 220px; background: var(--panel-2);
  color: var(--text); border: 1px solid var(--border-strong); border-radius: 6px; cursor: pointer; }
.doc-reclass:focus { outline: none; border-color: var(--accent); }
@media (max-width: 820px) {
  .doc-row { align-items: flex-start; padding: 11px 0; }
  /* actions drop to their own full-width line under name+chips; min-width:0 lets nested items shrink */
  .doc-actions { flex: 1 0 100%; min-width: 0; margin-left: 0; margin-top: 8px; gap: 10px; flex-wrap: wrap; }
  /* reclassify select on its OWN line, ≥40px touch. ⚠️min-width:0 is LOAD-BEARING: a <select>'s default
     min-width is its longest OPTION (~440px of pt-PT doctype label), so without it the select refuses to
     shrink and blows the row past the phone viewport (horizontal scroll at 390). */
  .doc-reclass { flex: 1 1 100%; min-width: 0; max-width: none; min-height: 40px; font-size: 15px; padding: 9px 10px; }
  .doc-treat-btn { min-height: 40px; padding: 0 16px; font-size: 15px; }
  /* ✗ pushed to the far right so it is NOT mispressable next to ✓ tratar. ⚠️the DESCENDANT selector
     (specificity 0,2,0) is required to beat `.btn.small { margin: 0 }` — the bare .doc-del-btn (0,1,0) loses. */
  .doc-actions .doc-del-btn { min-height: 40px; min-width: 44px; padding: 0 14px; font-size: 16px; margin-left: auto; }
}

/* --- Upload: ONE batch bar for the pending rows (one Guardar, one Cancelar). --------------------- */
.up-batch {
  display: flex; align-items: center; gap: 8px;
  margin-top: 10px; padding-top: 10px; border-top: 1px solid var(--border);
}
.up-batch-msg { font-size: 12px; }

/* --- Extraction wizard modal --------------------------------------------------------------------- */
.wiz-panel { width: 720px; }
.wiz-head { display: flex; align-items: center; gap: 8px; padding-bottom: 8px; border-bottom: 1px solid var(--border); }
.wiz-title { font-weight: 600; font-size: 14px; flex: 1; min-width: 0; overflow-wrap: anywhere; }
.wiz-pos { font-size: 12px; white-space: nowrap; }
.wiz-skip { font-size: 11px; margin-top: 8px; overflow-wrap: anywhere; }
.wiz-body { margin-top: 10px; min-height: 180px; max-height: 56vh; overflow-y: auto; }
.wiz-read { font-size: 12px; margin-bottom: 8px; }
.wiz-wait { padding: 24px 0; text-align: center; }
.wiz-grid { display: flex; flex-direction: column; gap: 6px; }
.wiz-row { display: grid; grid-template-columns: 150px minmax(0, 1fr); gap: 10px; align-items: start; }
.wiz-lab { color: var(--muted); font-size: 12px; padding-top: 7px; overflow-wrap: anywhere; }
.wiz-cell { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 8px; min-width: 0; }
.wiz-input { flex: 1; min-width: 0; }
.wiz-ro { padding: 6px 0; font-size: 13px; overflow-wrap: anywhere; }
.wiz-icon { color: #ffd84d; cursor: help; font-size: 14px; }
.wiz-warn { flex: 1 0 100%; color: #ffd84d; font-size: 11px; line-height: 1.3; }
.wiz-ack { display: inline-flex; align-items: center; gap: 3px; font-size: 11px; color: var(--muted); white-space: nowrap; }
.wiz-ack input { width: 15px; height: 15px; }

@media (max-width: 640px) {
  .wiz-panel { width: 100%; }
  .wiz-row { grid-template-columns: 1fr; gap: 2px; }
  .wiz-lab { padding-top: 0; }
  .wiz-body { max-height: 60vh; }
  .wiz-input { font-size: 16px; }          /* no iOS focus-zoom */
  .wiz-ack { min-height: 32px; }
  .wiz-ack input { width: 20px; height: 20px; }
  .up-batch { flex-wrap: wrap; }
  .up-batch .btn { flex: 1; min-height: 40px; }
  .up-batch-msg { flex: 1 0 100%; }
  .group-card .kv { grid-template-columns: minmax(84px, 40%) minmax(0, 1fr); }
}


/* ================================================================================================
   CRM EMPLOYEE LOGIN + ROLES + AUDIT (2026-07-16): whoami chrome + the Atividade view.
   Appended block — everything above is untouched.
   ================================================================================================ */
/* Just the first name, top-right; everything else lives behind it. */
.whoami { position: relative; margin-left: auto; flex: 0 0 auto; }
.who-btn {
  display: inline-flex; align-items: center; gap: 5px;
  background: none; border: 1px solid transparent; border-radius: 6px;
  color: var(--text); font: inherit; font-size: 13px; font-weight: 500;
  padding: 5px 9px; cursor: pointer; line-height: 20px; white-space: nowrap;
}
.who-btn:hover { background: var(--panel-2); border-color: var(--border); }
.who-btn .who-caret { font-size: 9px; color: var(--muted); transition: transform .12s ease; }
.whoami.open .who-btn { background: var(--panel-2); border-color: var(--border); }
.whoami.open .who-btn .who-caret { transform: rotate(180deg); }
.who-menu {
  display: none; position: absolute; top: calc(100% + 6px); right: 0; z-index: 30;
  min-width: 168px; padding: 4px;
  background: var(--panel); border: 1px solid var(--border-strong); border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .45);
}
.whoami.open .who-menu { display: block; }
.who-status {
  padding: 7px 10px 8px; font-size: 12px; color: var(--muted);
  border-bottom: 1px solid var(--border); margin-bottom: 4px; overflow-wrap: anywhere;
}
.who-status b { display: block; color: var(--text-strong); font-weight: 600; font-size: 12.5px; }
.who-item {
  display: block; padding: 7px 10px; border-radius: 5px;
  color: var(--text); text-decoration: none; font-size: 13px;
}
.who-item:hover { background: var(--panel-2); color: var(--text-strong); }

/* Stats sub-tabs: Geral | ISV Shadow | ISV Drift */
.subtabs { display: flex; gap: 6px; flex-wrap: wrap; margin: 0 0 14px; }
.subtab {
  padding: 5px 11px; border-radius: 999px; border: 1px solid var(--border);
  color: var(--muted); text-decoration: none; font-size: 12.5px; font-weight: 500; white-space: nowrap;
}
.subtab:hover { background: var(--panel-2); color: var(--text-strong); }
.subtab.active { background: var(--accent-soft); color: var(--text-strong); border-color: transparent; }

.aud-bar { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin: 10px 0 14px; }
.aud-bar .aud-user { max-width: 260px; }
.aud-bar .aud-date { max-width: 160px; }

@media (max-width: 640px) {
  /* Top-RIGHT on mobile too, beside the logo — it used to be a full-width third row, which was
     right for the old email+pill+Sair strip but wastes a whole row on a 390px screen now that it is
     one short name. Row 1 = logo + name; the nav (order:3) wraps to its own scrolling row below. */
  .whoami { order: 2; width: auto; margin-left: auto; padding-top: 0; }
  .aud-bar .aud-user, .aud-bar .aud-date { max-width: none; flex: 1 0 100%; }
}

/* =============================================
   <=820px — STACKED CARDS (2026-07-20, presentation only)
   ============================================= */
/* Below this width even a fit-to-width table is unreadable: Histórico is 11 columns, which is
   ~70px each at 768px and hopeless at 390px. Rather than a horizontal bar (explicitly ruled out)
   or hiding columns (which would put data out of reach), each row becomes a card and each cell
   carries its own field name from data-label. Everything stays on screen and readable, and the
   page keeps exactly one scrollbar — the vertical one it always had.
   ⚠️820 rather than 640 on purpose: 768 is a target width and an 11-column table is not usable
   there. Card or nothing. */
@media (max-width: 820px) {
  /* reveal the camera-capture block on tablet/phone (hidden at desktop, see .capture-block above) */
  .capture-block { display: block; }
  main .table-scroll {
    max-height: none;        /* the cap is a desktop device; cards scroll with the page */
    overflow: visible;
  }
  main .table-scroll > table,
  main .table-scroll > table tbody,
  main .table-scroll > table tr,
  main .table-scroll > table td {
    display: block;
    width: auto;
  }
  main .table-scroll > table { border: none; background: none; box-shadow: none; }
  main .table-scroll > table thead { display: none; }   /* data-label carries the field names */
  main .table-scroll > table tr {
    background: var(--panel);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-bottom: 8px;
    padding: 4px 10px;
  }
  main .table-scroll > table tbody tr:hover { background: var(--panel); }
  main .table-scroll > table td {
    position: static;        /* undo any sticky left over from the retired pinned column */
    display: grid;
    grid-template-columns: 40% 1fr;
    gap: 10px;
    align-items: baseline;
    border: none;
    border-bottom: 1px solid rgba(255, 255, 255, 0.04);
    padding: 6px 0;
    font-size: 13px;
    /* cards have room to wrap, so nothing is truncated here — the ellipsis is a desktop device */
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
    word-break: break-word;
  }
  main .table-scroll > table tr td:last-child { border-bottom: none; }
  main .table-scroll > table td::before {
    content: attr(data-label);
    color: var(--muted);
    font-size: 10.5px;
    text-transform: uppercase;
    letter-spacing: .4px;
    line-height: 1.5;
  }
  /* an action-only cell (the Casos ✕) has no label and no text — do not give it a whole row */
  main .table-scroll > table td:not([data-label]) { grid-template-columns: 1fr; }
  main .table-scroll > table td:not([data-label])::before { content: none; }
}

/* ── Leads / active / archived split (2026-07-22) ─────────────────────────────────────────────── */
/* Cases-tab status filter pills (Ativos / Leads / Arquivados / Todos) */
.status-pills { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 12px; }
.status-pill {
  background: var(--panel-2); color: var(--muted);
  border: 1px solid var(--border-strong); border-radius: 14px;
  padding: 5px 12px; font-size: 13px; cursor: pointer; line-height: 1;
}
.status-pill:hover { color: var(--text); }
.status-pill.on { background: var(--accent-soft); color: var(--text-strong); border-color: var(--accent); font-weight: 600; }
/* case-view status badge + its transition control */
.case-status-slot { display: inline-flex; align-items: center; gap: 8px; margin-left: 6px; }
.tag.case-status-badge { font-weight: 600; }
.tag.cs-lead { background: #221b34; color: #b083f0; }
.tag.cs-active { background: #122a1e; color: #57ab5a; }
.tag.cs-archived { background: var(--border); color: var(--muted); }
.case-status-btn { white-space: nowrap; }
/* Cases-list trailing action cell keeps the status + delete buttons on one line. Tighter side
   padding than a normal cell so two small buttons fit the fixed-width column without overflowing. */
.case-row-actions { white-space: nowrap; padding-left: 6px; padding-right: 6px; text-align: right; }
.case-row-actions .btn { margin-left: 4px; padding-left: 7px; padding-right: 7px; }

/* ── Lead auto-classification (lead_kind) — 2026-07-23 ─────────────────────────────────────────── */
/* the pill wrapper now stacks two rows (case-status + kind sub-filter) */
.status-pills-wrap { margin: 0 0 12px; }
.status-pills-wrap .status-pills { margin-bottom: 6px; }
.status-pills-wrap .kind-pills { margin-bottom: 0; }
.kind-pills .kind-note { align-self: center; font-size: 12px; margin-left: 6px; }
/* kind pills: a small colour dot before the label; the ON state fills with the kind colour */
.kind-pill::before { content: ''; display: inline-block; width: 8px; height: 8px; border-radius: 50%; margin-right: 6px; vertical-align: middle; background: var(--muted); }
.kind-pill.lk-potencial_cliente::before { background: #57ab5a; }
.kind-pill.lk-fornecedor::before { background: #6cb6ff; }
.kind-pill.lk-interno_teste::before { background: #768390; }
.kind-pill.lk-spam::before { background: #e5534b; }
.kind-pill.lk-all::before { display: none; }
/* kind badge on rows + case view */
.tag.lk-badge { font-weight: 600; }
.tag.lk-potencial_cliente { background: #122a1e; color: #57ab5a; }
.tag.lk-fornecedor { background: #12233a; color: #6cb6ff; }
.tag.lk-interno_teste { background: var(--border); color: var(--muted); }
.tag.lk-spam { background: #3a1d1b; color: #e5534b; }
.tag.lk-none { background: transparent; border: 1px dashed var(--border-strong); color: var(--muted); }
/* case-view lead-kind control */
.lead-kind-slot { display: inline-flex; align-items: center; gap: 6px; margin-left: 8px; }
.lk-select { background: var(--panel-2); border: 1px solid var(--border-strong); border-radius: 6px; color: var(--text); font-size: 12px; padding: 3px 6px; }
.lk-reclassify { white-space: nowrap; }

/* ── Endgame archive suggestion (pipeline card + case view) ─────────────────────────────────────────
   Shown INLINE when the final Importing step "DUA recebido (cliente)" is ticked on an active case. A
   calm call-to-action, not an alarm: muted-blue accent fill + left rule, reusing the standard buttons.
   Default is a wide banner (case view, full-width Resumo card); the pipeline card override stacks it. */
.archive-suggest {
  margin-top: 8px;
  padding: 9px 12px;
  background: var(--accent-soft);
  border: 1px solid var(--border-strong);
  border-left: 3px solid var(--accent);
  border-radius: 6px;
  display: flex;
  align-items: center;
  gap: 10px;
  flex-wrap: wrap;
}
.archive-suggest-msg { font-size: 13px; font-weight: 600; color: var(--text-strong); flex: 1 1 200px; }
.archive-suggest-btns { display: flex; gap: 6px; flex-wrap: wrap; }
/* Pipeline card is narrow → stack the message over full-width buttons. */
.kcard .archive-suggest { flex-direction: column; align-items: stretch; gap: 7px; margin-top: 7px; padding: 8px 9px; }
.kcard .archive-suggest-msg { font-size: 12px; flex: none; }
.kcard .archive-suggest-btns .btn { flex: 1 1 auto; white-space: nowrap; text-align: center; }

/* ===== CONTABILIDADE =====
   Standing rule: tables FIT, they never scroll sideways. table-layout:fixed + % colgroup widths
   makes that structural rather than hopeful; every cell truncates with the full value on title.
   Below 820px the rows become stacked cards, which is the only honest way to show 8 columns on a
   phone without a scrollbar. */
.contab-bar { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin: 12px 0 18px; }
.contab-totals { display: flex; gap: 12px; flex-wrap: wrap; margin: 0 0 20px; }
.contab-tot { flex: 1 1 160px; border: 1px solid var(--border, #333); border-radius: 10px; padding: 12px 14px; }
.contab-tot-l { display: block; font-size: 0.75rem; opacity: 0.7; }
.contab-tot-v { display: block; font-size: 1.15rem; font-weight: 700; }
.contab-tot.is-pos .contab-tot-v { color: #4caf50; }
.contab-tot.is-neg .contab-tot-v { color: #e05252; }
.contab-totals--sm .contab-tot { padding: 8px 10px; }
.contab-totals--sm .contab-tot-v { font-size: 1rem; }

.contab-tablewrap { width: 100%; }
.contab-table { width: 100%; table-layout: fixed; border-collapse: collapse; }
.contab-table th, .contab-table td { padding: 7px 9px; text-align: left; border-bottom: 1px solid var(--border, #333); font-size: 0.875rem; }
.contab-table th { font-weight: 600; opacity: 0.75; }
.contab-table .ta-r { text-align: right; }
.contab-table .trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

.contab-form { display: flex; gap: 10px; flex-wrap: wrap; align-items: flex-end; margin: 12px 0 4px; }
.contab-field { display: flex; flex-direction: column; gap: 4px; font-size: 0.8125rem; }
.contab-field .inp { min-width: 140px; }
.contab-check { display: flex; align-items: center; gap: 6px; font-size: 0.8125rem; }
.contab-card { margin-top: 16px; }

@media (max-width: 820px) {
  .contab-table, .contab-table tbody, .contab-table tr, .contab-table td { display: block; width: auto; }
  .contab-table thead { display: none; }
  .contab-table colgroup { display: none; }
  .contab-table tr { border: 1px solid var(--border, #333); border-radius: 10px; padding: 8px 10px; margin-bottom: 10px; }
  .contab-table td { border: 0; padding: 3px 0; white-space: normal; }
  .contab-table .ta-r { text-align: left; }
  .contab-form .inp, .contab-field { width: 100%; }
}

/* ===== ATIVIDADE (paged) + STATS ad link =====
   Standing rule: tables FIT. table-layout:fixed + a % colgroup makes that structural, every cell
   truncates with its full value on the title attribute, and below 820px the rows become stacked
   cards - the only honest way to show six columns on a phone without a sideways scrollbar. */
.aud-tablewrap { width: 100%; }
.tbl--audit { width: 100%; table-layout: fixed; border-collapse: collapse; }
.tbl--audit th, .tbl--audit td { padding: 6px 9px; text-align: left; font-size: 0.8125rem; }
.tbl--audit .trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aud-count { margin-top: 8px; font-size: 11.5px; }
.aud-more { margin-top: 6px; }

/* ── Stats "Quem — por identidade" tables + the founder mark ──────────────────────────────────
   Standing rule, same as every other table here: 100% width, table-layout:fixed + a % colgroup so
   fit is STRUCTURAL, every cell truncates with its full value on the title attribute, and below
   820px the rows become stacked cards rather than growing a sideways scrollbar. */
.tbl--ident { width: 100%; table-layout: fixed; border-collapse: collapse; margin-top: 6px; }
.tbl--ident th, .tbl--ident td { padding: 5px 8px; text-align: left; font-size: 0.8125rem; }
.tbl--ident .trunc { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* a founder row is us, not a customer — marked so the eye can discount it while scanning */
.ident-founder { font-weight: 600; }

/* ── Histórico › Procura — ONE ROW PER FINDER RUN (2026-09-08) ───────────────────────────────
   The tab now lists runs, not child estimates: each run is one row (its criteria, who, status,
   counts, duration, spend) that expands into a nested table of its child estimates. Two width
   tables — developer (7 columns, with 'Quem') and ops (6, no identity) — each restated in full and
   summing to 100, because a fixed-layout table with unassigned width overflows its box. Standing
   rule: tables FIT, no horizontal scrollbar on the page. Critérios is the wide column; the numeric
   ones (Resultados / Duração / Custo) are narrow. */
/* Developer: Data, Critérios, Quem, Estado, Resultados, Duração, Custo. */
.tbl--hist.has-identity.tbl--hist-procura th:nth-child(1) { width: 9%  }   /* Data (caret+date+count) */
.tbl--hist.has-identity.tbl--hist-procura th:nth-child(2) { width: 33% }   /* Critérios */
.tbl--hist.has-identity.tbl--hist-procura th:nth-child(3) { width: 17% }   /* Quem — a full IPv4 must not clip */
.tbl--hist.has-identity.tbl--hist-procura th:nth-child(4) { width: 9%  }   /* Estado */
.tbl--hist.has-identity.tbl--hist-procura th:nth-child(5) { width: 12% }   /* Resultados (E/T/M) */
.tbl--hist.has-identity.tbl--hist-procura th:nth-child(6) { width: 9%  }   /* Duração */
.tbl--hist.has-identity.tbl--hist-procura th:nth-child(7) { width: 11% }   /* Custo */
/* Ops: Data, Critérios, Estado, Resultados, Duração, Custo — the server strips 'Quem'. */
.tbl--hist-procura:not(.has-identity) th:nth-child(1) { width: 10% }   /* Data */
.tbl--hist-procura:not(.has-identity) th:nth-child(2) { width: 43% }   /* Critérios */
.tbl--hist-procura:not(.has-identity) th:nth-child(3) { width: 11% }   /* Estado */
.tbl--hist-procura:not(.has-identity) th:nth-child(4) { width: 14% }   /* Resultados */
.tbl--hist-procura:not(.has-identity) th:nth-child(5) { width: 10% }   /* Duração */
.tbl--hist-procura:not(.has-identity) th:nth-child(6) { width: 12% }   /* Custo */

/* Right-align the numeric run columns so figures read against themselves; not in the stacked
   layout, where every cell is a labelled grid row. Developer: cols 5-7; ops: cols 4-6. */
@media (min-width: 821px) {
  .tbl--hist.has-identity.tbl--hist-procura td:nth-child(5),
  .tbl--hist.has-identity.tbl--hist-procura td:nth-child(6),
  .tbl--hist.has-identity.tbl--hist-procura td:nth-child(7),
  .tbl--hist-procura:not(.has-identity) td:nth-child(4),
  .tbl--hist-procura:not(.has-identity) td:nth-child(5),
  .tbl--hist-procura:not(.has-identity) td:nth-child(6) { text-align: right; }
}

/* ── the run row itself ─────────────────────────────────────────────────────────────────────── */
.tbl--hist-procura tbody tr.run-row { cursor: pointer; }
.tbl--hist-procura tbody tr.run-row:hover { background: var(--panel, #1b1f2b); }
.run-caret { display: inline-block; width: 1em; color: var(--muted, #98a0b3); font-size: 10px; }
tr.run-open .run-caret { color: var(--text, #e7ebf3); }
.run-kidcount { font-size: 11px; }
/* Critérios: it is the row's subject, so it stays legible and clips with a tooltip like every
   other free-text cell. */
.tbl--hist-procura td.run-crit { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The operator badge — a marked (ops / CRM) run surfaced under the tráfego-nosso switch. Distinct
   from the company badge beside it: a filled chip, not an outline. */
.tag.run-operator { background: var(--panel-2, #262b3a); border: 1px solid var(--border-strong, #3a3f55);
  color: var(--muted, #98a0b3); font-weight: 600; letter-spacing: .2px; margin-left: 6px; }
.run-country { font-size: 11px; }

/* ── the expansion row + the nested child-estimate table ─────────────────────────────────────
   ⚠️FULL SELECTOR TO OUT-SPECIFY `main .table-scroll > table td` (0,1,3), which otherwise keeps its
   6px/8px padding on this full-width cell and boxes the nested table in dead space. */
main .table-scroll > table tr.row-expand > td { padding: 0; background: var(--panel-2, #161a24); }
.run-children-wrap { overflow-x: auto; padding: 6px 8px 10px 24px; }   /* indented under the run */
.run-empty { padding: 8px 2px; font-style: italic; }
/* The nested table is NOT a `.table-scroll > table`, so it does not inherit the fit-to-width rules;
   restate them here. Fixed layout + width:100% so it fills the expansion cell without overflowing;
   its own scroller (.run-children-wrap) catches any narrow-viewport overflow. */
.run-children { width: 100%; table-layout: fixed; border-collapse: collapse; background: transparent; }
.run-children th, .run-children td {
  padding: 5px 8px; font-size: 12px; text-align: left;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  border-bottom: 1px solid var(--border, #2a2f3d);
}
.run-children th { font-size: 10.5px; color: var(--muted, #98a0b3); font-weight: 600; }
.run-children tbody tr:last-child td { border-bottom: none; }
/* Same column proportions as the Website estimate table (9 tracks summing to 100). */
.run-children th:nth-child(1) { width: 8%  }   /* Data */
.run-children th:nth-child(2) { width: 22% }   /* Viatura */
.run-children th:nth-child(3) { width: 8%  }   /* Combustível */
.run-children th:nth-child(4) { width: 8%  }   /* Km */
.run-children th:nth-child(5) { width: 12% }   /* Total */
.run-children th:nth-child(6) { width: 12%; text-align: right; }   /* Poupança */
.run-children td:nth-child(6) { text-align: right; }
.run-children th:nth-child(7) { width: 12% }   /* Preço típico PT */
.run-children th:nth-child(8) { width: 8%  }   /* Estado */
.run-children th:nth-child(9) { width: 10% }   /* Anúncio / Site */

/* A row whose ORIGIN was inferred from the burst shape rather than recorded. Muted and italic:
   it is a weaker statement than the rows around it and should not read as equally certain. The
   tooltip carries the why; this only has to stop it looking like a recorded fact. */
.ident-inferred { color: var(--muted); font-style: italic; }
.seg-founder { background: var(--seg-founder, #b07d2b); }
@media (max-width: 820px) {
  .tbl--ident, .tbl--ident tbody, .tbl--ident tr, .tbl--ident td { display: block; width: auto; }
  .tbl--ident thead, .tbl--ident colgroup { display: none; }
  .tbl--ident tr { border: 1px solid var(--border, #333); border-radius: 10px; padding: 8px 10px; margin-bottom: 8px; }
  .tbl--ident td { border: 0; padding: 3px 0; white-space: normal; }
}

/* the ad column in the Stats account drill-down */
.asr-ad { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 12ch; }
.acct-sub-more { margin-top: 6px; align-self: flex-start; }

/* ── Stats → Accounts → Estimate runs ──
   A FIXED four-track grid. The shared .acct-sub-row is flex with .asr-main flex:1, which let the car
   name eat all the slack — so the anúncio link, the date and the price each began at a different x on
   every row and the columns staircased. Saved searches / Redeemed codes stay on the flex row: they
   are two-cell rows and were never misaligned.
   ⚠️1fr + fixed tracks, NOT percentages: the row carries a 10px gap, and % tracks + gaps overflow
   their container. The 1fr absorbs the remainder instead, so fit is structural and no width can
   produce a horizontal scrollbar. Track sizes are the real content: 'standvirtual' is the longest
   siteLabel (12), fmtDay is ISO (10), money is '€193.682,65' (11). */
.acct-sub-row--est { display: grid; grid-template-columns: minmax(0, 1fr) 12ch 10.5ch 11ch; align-items: baseline; }
/* truncate+tooltip, overriding the shared rule's overflow-wrap:anywhere (which WRAPS and would make
   rows different heights, breaking the alignment this grid exists to create) */
.acct-sub-row--est .asr-main { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; overflow-wrap: normal; }
/* the track governs the ad width now, so the ch cap would only truncate it twice */
.acct-sub-row--est .asr-ad { max-width: none; }
.acct-sub-row--est .asr-date { text-align: left; }
/* money right-aligns so the euro figures line up on their last digit and are scannable down the column */
.acct-sub-row--est .asr-val { text-align: right; font-variant-numeric: tabular-nums; }
/* no concluded price: one muted dash, CENTRED — right-aligned it would read as a near-zero value */
.acct-sub-row--est .asr-val--none { text-align: center; color: var(--muted); font-weight: 400; }

@media (max-width: 820px) {
  .tbl--audit, .tbl--audit tbody, .tbl--audit tr, .tbl--audit td { display: block; width: auto; }
  .tbl--audit thead, .tbl--audit colgroup { display: none; }
  .tbl--audit tr { border: 1px solid var(--border, #333); border-radius: 10px; padding: 8px 10px; margin-bottom: 10px; }
  .tbl--audit td { border: 0; padding: 3px 0; white-space: normal; }
  .asr-ad { max-width: none; }
  /* stacked: car on its own line, then anúncio | data | preço. Still one grid, so the three
     lower cells stay column-aligned across rows instead of reverting to a ragged flex row. */
  .acct-sub-row--est { grid-template-columns: minmax(0, 1fr) 10.5ch 11ch; row-gap: 2px; }
  .acct-sub-row--est .asr-main { grid-column: 1 / -1; }
}

/* ── Case Estimativas panel (2026-07-26) ── */
.estim-card { margin-top: 14px; }
.estim-tabs { display: flex; gap: 8px; margin: 10px 0 6px; }
.estim-tool { margin-bottom: 10px; }
.estim-linkrow { display: flex; gap: 8px; align-items: center; margin: 8px 0; flex-wrap: wrap; }
.estim-linkrow .field-input { flex: 1 1 320px; min-width: 0; }
.estim-result { border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; margin: 8px 0; }
.estim-result-h { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; margin-bottom: 4px; }
.estim-figs { display: flex; gap: 14px; flex-wrap: wrap; font-size: 13px; margin-bottom: 8px; }
.estim-listh { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .4px; color: var(--muted); margin: 14px 0 6px; }
.estim-quoted { color: var(--text-strong); font-weight: 600; }
/* one primary + one gear, side by side, never wrapping */
.estim-actions { white-space: nowrap; }
.estim-actions .btn { margin-left: 0; min-width: 0; overflow: hidden; text-overflow: ellipsis; }

/* ── gear menu ── the button lives in the row, the MENU is portalled to <body> ────────────────────
   ⚠️position:fixed and appended to body on purpose: every fixed-layout table here sets
   `td { overflow: hidden }` for truncation, so a menu rendered inside the cell is clipped after the
   first item. app.js computes left/top from the button's rect and flips it above when it would run
   off the bottom. */
.gear-btn { flex: 0 0 auto; background: none; border: 1px solid transparent; color: var(--muted);
  cursor: pointer; font-size: 13px; line-height: 1; padding: 4px 6px; border-radius: 6px; }
.gear-btn:hover, .gear-btn[aria-expanded="true"] { color: var(--text-strong); background: var(--panel-2); border-color: var(--border); }
.gear-menu { position: fixed; z-index: 1200; min-width: 190px; padding: 4px;
  background: var(--panel); border: 1px solid var(--border-strong); border-radius: 8px; box-shadow: var(--shadow); }
.gear-item { display: block; width: 100%; text-align: left; background: none; border: 0;
  color: var(--text); cursor: pointer; font-size: 12.5px; padding: 7px 10px; border-radius: 5px; white-space: nowrap; }
.gear-item:hover { background: var(--panel-2); color: var(--text-strong); }
.gear-item--danger { color: var(--money-neg); }
.gear-item--danger:hover { background: rgba(229, 83, 75, 0.12); color: var(--money-neg); }
/* A negative saving means importing costs MORE than buying in PT - it must LOOK different. */
/* THE signed-money pair. `.neg` stays as an alias so nothing that already used it changes colour. */
.money-pos { color: var(--money-pos); }
.money-neg, .estim-result .neg, .tbl--estim .neg, .tbl--finder .neg { color: var(--money-neg); }
/* fit is structural: NINE tracks summing to 100, no horizontal scrollbar by construction.
   Data 8 · Carro 23 · Preço 9 · ISV 9 · Total 10 · Poupança 10 · Cotado 9 · Anúncio 8 · acções 14.
   ⚠️The action column is the one that must not be squeezed: the old layout scrolled sideways and cut
   "Marcar como cotado" down to "Marcar", which is how a mystery button gets shipped. */
/* ⚠️WIDTHS ARE MEASURED, NOT GUESSED (2026-07-27). At a 900px viewport the case column is ~810px and
   the old split clipped FIVE columns — the date, three money columns and Anúncio — so a value ended
   in an ellipsis and its right edge no longer met its header's. That is what "headers not aligned
   with their values" was: truncation, not text-align. Sized off the real worst case, a six-figure
   car (Aston Martin / Ferrari are in the live log): "€140.580,00" needs ~86px + padding.
   Carro is the one that truncates by design — it has the thumbnail, a tooltip and the most slack. */
.tbl--estim { width: 100%; table-layout: fixed; }
.tbl--estim th:nth-child(1) { width: 10%   }  /* Data     — "2026-07-27" needs 80px @810 */
.tbl--estim th:nth-child(2) { width: 15%   }  /* Carro    — truncates by design (thumb + tooltip) */
.tbl--estim th:nth-child(3) { width: 11%   }  /* Preço    */
.tbl--estim th:nth-child(4) { width: 10.5% }  /* ISV      */
.tbl--estim th:nth-child(5) { width: 11%   }  /* Total    */
.tbl--estim th:nth-child(6) { width: 11%   }  /* Poupança */
.tbl--estim th:nth-child(7) { width: 10.5% }  /* Cotado   */
.tbl--estim th:nth-child(8) { width: 9%    }  /* Anúncio  */
.tbl--estim th:nth-child(9) { width: 12%   }  /* acções   — ⚠️the button+gear need ~97px at 810px;
     cutting this to 10% clipped "Definir viatura" into "Definir vi…", which is the same mystery
     button the horizontal scrollbar used to produce. Sized so the control fits, never the reverse. */
/* Above ~1200px the money columns have far more room than they need (89px of digits in a 123px
   track), so the slack goes to the CAR NAME — the identity of the row, and the thing an operator
   reads first. The set above is the tight one; this is the same table with its spare width spent
   where it helps instead of padding five number columns. */
@media (min-width: 1201px) {
  .tbl--estim th:nth-child(1) { width: 8%    }
  .tbl--estim th:nth-child(2) { width: 22%   }
  .tbl--estim th:nth-child(3) { width: 10%   }
  .tbl--estim th:nth-child(4) { width: 9.5%  }
  .tbl--estim th:nth-child(5) { width: 10%   }
  .tbl--estim th:nth-child(6) { width: 10%   }
  .tbl--estim th:nth-child(7) { width: 9.5%  }
  .tbl--estim th:nth-child(8) { width: 8%    }
  .tbl--estim th:nth-child(9) { width: 13%   }
}
/* Below ~1000px the padding is what pushes the money into an ellipsis; buy it back rather than
   shaving another column. Tighter again in the last stretch before the cards take over (821-950),
   where a six-figure car needs every pixel. ⚠️The car name truncates hard in that band by design —
   it keeps its tooltip, and a clipped NUMBER is the thing that must never happen.
   ⚠️THE SELECTOR IS DELIBERATELY LONG: the base rule is `main .table-scroll > table td` (0,1,3), so
   a plain `.tbl--estim td` (0,1,1) loses and the padding silently stays at 8px — measured, not
   assumed. Same trap as the stacked-row height; when overriding here, match the base selector. */
@media (max-width: 1000px) {
  main .table-scroll > table.tbl--estim th,
  main .table-scroll > table.tbl--estim td { padding-left: 5px; padding-right: 5px; }
  .tbl--estim .estim-actions .btn { padding-left: 6px; padding-right: 6px; }
}
@media (max-width: 950px) {
  main .table-scroll > table.tbl--estim th,
  main .table-scroll > table.tbl--estim td { padding-left: 3px; padding-right: 3px; }
}
/* Between the stacking breakpoint and ~1100px the table is real but tight, and "Definir viatura"
   does not fit its column. The label SHORTENS rather than being cut off mid-word: a shorter word is
   still a word, "Definir vi…" is a mystery. The full sentence stays in the tooltip either way, and
   the stacked card layout below 820px shows the long label again because it has the room. */
.estim-actions .lbl-short { display: none; }
@media (min-width: 821px) and (max-width: 1100px) {
  .estim-actions .lbl-long { display: none; }
  .estim-actions .lbl-short { display: inline; }
}
.tbl--estim td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* the header sits over its own numbers, which is what the cells alone never gave us */
.tbl--estim td.num, .tbl--estim th.num { text-align: right; font-variant-numeric: tabular-nums; }
/* Cotado: a STATUS column carrying an amount when there is one. Centred, header and value alike, so
   a column that is mostly em dashes does not read as a broken money column hugging the right edge.
   tabular-nums stays: the amounts that do appear still line up with each other. */
.tbl--estim td.mid, .tbl--estim th.mid { text-align: center; font-variant-numeric: tabular-nums; }
/* the thumbnail is a link to the ad; the placeholder is not a control and stays inert */
.estim-thumb-link { display: inline-flex; flex: 0 0 auto; line-height: 0; border-radius: 4px; }
.estim-thumb-link:hover .estim-thumb { border-color: var(--accent); }
.estim-thumb-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* the landed cost is the figure the case is about */
.tbl--estim td.estim-total { color: var(--text-strong); font-weight: 600; }

/* ── ONE BASELINE PER ROW (2026-07-27) ───────────────────────────────────────────────────────────
   The global rule is `th, td { vertical-align: top }`, so every cell hung its content from the top
   and anything with a different intrinsic height landed on a different axis: measured at 1440, the
   thumbnail sat 3px above the row centre, the Anúncio link 10px above it, and the action buttons
   6.7px BELOW it — which is the "buttons sit lower and bleed into the row below" in the screenshot.
   ⚠️The two composite cells were the worst of it because they were `display: flex` ON THE <td>: a
   flex td is no longer a table-cell, so it does not stretch to the row, vertical-align does not
   apply, and it paints its own bottom border at its own height — that short rule under CARRO that
   stopped mid-table. The flex now lives on an inner .cellflex span and the cells are cells again.
   ⚠️Long selector on purpose: the base is `main .table-scroll > table td` (0,1,3). */
main .table-scroll > table.tbl--estim td { vertical-align: middle; }
.tbl--estim .cellflex { display: flex; align-items: center; gap: 8px; min-width: 0; }
.tbl--estim td.estim-actions .cellflex { gap: 6px; justify-content: flex-end; }

/* ── ONE SEPARATOR, ON THE ROW ────────────────────────────────────────────────────────────────────
   Row separation is a single full-width line under the <tr>, never nine per-cell borders that can
   drift apart. border-collapse is `collapse` here, so a tr border paints. */
main .table-scroll > table.tbl--estim tbody td { border-bottom: 0; }
main .table-scroll > table.tbl--estim tbody tr { border-bottom: 1px solid var(--border); }
main .table-scroll > table.tbl--estim tbody tr:last-child { border-bottom: 0; }

/* Carro: thumb + name on one line. The NAME is what truncates, so min-width:0 is load-bearing -
   without it a flex child refuses to shrink and pushes the table wide. */
.tbl--estim td.estim-carcell { white-space: nowrap; }
.estim-carname { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.estim-thumb { flex: 0 0 auto; width: 44px; height: 32px; border-radius: 4px; object-fit: cover;
  background: var(--panel-2); border: 1px solid var(--border); }
/* A DRAWN car, not a character: an emoji depends on a font the machine may not have (it rendered as
   a tofu box in headless Chrome) and a blank tile reads as broken rather than as "no photo". */
.estim-thumb--none { border-style: dashed; display: inline-flex; align-items: center; justify-content: center; }
.estim-thumb-glyph { width: 26px; height: 26px; color: var(--muted); opacity: .8; }
/* ONE rhythm: every row the same height. It was 51/50/50 — a 1px wobble from each row sizing itself
   to its own content. The thumb is 32px + 2×6px padding, so 52 seats it with room and nothing
   overflows. ⚠️Same long selector as the vertical-align rule above, and the stacked block MUST
   override it with a selector of at least equal weight (it does, further down). */
main .table-scroll > table.tbl--estim tbody tr { height: 52px; }
/* A case with no car yet is a real state, not a gap: say so rather than leaving a hole. */
.kcard-novehicle, .no-vehicle { font-style: italic; opacity: .75; }
/* the latest quoted amount is what the client is holding us to: same weight as the case total */
.kcard-quote { font-size: 12px; color: var(--text-strong); font-weight: 600; margin-top: 2px; }
@media (max-width: 820px) {
  .tbl--estim, .tbl--estim tbody, .tbl--estim tr, .tbl--estim td { display: block; width: auto; }
  .tbl--estim thead { display: none; }
  .tbl--estim tr { border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; margin-bottom: 10px; }
  /* ⚠️MUST match the desktop selector, not be a looser one: the desktop rule is now
     `main .table-scroll > table.tbl--estim tbody tr` (0,2,3), so a plain `.tbl--estim tbody tr`
     (0,2,1) loses and every stacked card is clamped to 52px with its rows printing on top of each
     other. This is the third time a short override has lost to a long base rule in this table. */
  main .table-scroll > table.tbl--estim tbody tr { height: auto; }
  .tbl--estim td { border: 0; padding: 3px 0; white-space: normal; }
  /* in a card every value sits in the same left-aligned column, money and status alike */
  .tbl--estim td.num, .tbl--estim td.mid { text-align: left; }
  /* the card separator is the card's own border, not a per-row rule */
  main .table-scroll > table.tbl--estim tbody tr { border-bottom: 1px solid var(--border); }
  /* ⚠️In a card the value column is only 60% of the width (the label takes 40%), so the car name no
     longer has the whole row to itself and the desktop ellipsis would cut it. Cards have room to
     WRAP - that is the whole point of them - so the truncation rule is lifted here. */
  .tbl--estim td.estim-carcell { white-space: normal; }
  .tbl--estim td.estim-carcell .cellflex { align-items: flex-start; }
  .tbl--estim .estim-carname { white-space: normal; overflow: visible; text-overflow: clip; }
  /* stacked: the label/value grid applies to every cell including these two; the flex row inside
     them just lays the thumb and the buttons out horizontally within the value column. */
  .tbl--estim td.estim-actions .cellflex { justify-content: flex-start; }
}

.estim-need-prompt { margin: 6px 0; font-size: 12.5px; }
.estim-need-err { margin: 4px 0 0; font-size: 12.5px; }
/* an outstanding field is information, not a missing value */
.tbl--estim .estim-pending { font-style: italic; text-align: left; }

/* the link field carries its own ✕, like the website estimator input */
.estim-inputwrap { position: relative; display: inline-flex; flex: 1 1 320px; min-width: 0; }
.estim-inputwrap .field-input { width: 100%; padding-right: 30px; }
.estim-clear { position: absolute; right: 4px; top: 50%; transform: translateY(-50%);
  background: none; border: 0; color: var(--muted); cursor: pointer; font-size: 14px;
  line-height: 1; padding: 4px 6px; border-radius: 4px; }
.estim-clear:hover { color: var(--text-strong); background: var(--panel); }
/* an estimate the operator marked wrong stays VISIBLE and struck, never rewritten */
.tbl--estim tr.estim-invalid td { opacity: .6; }
.tbl--estim tr.estim-invalid .estim-carcell { text-decoration: line-through; }
.estim-invalid-badge { color: #e5534b; font-size: 11px; white-space: nowrap; }

/* ── case-view tabs ── segmented bar on desktop, scrollable pills on a phone ── */
.casetabs { display: flex; gap: 2px; margin: 14px 0 0; border-bottom: 1px solid var(--border);
  overflow-x: auto; scrollbar-width: none; }
.casetabs::-webkit-scrollbar { display: none; }
.casetabs .casetab { flex: 0 0 auto; padding: 8px 14px; font-size: 13px; color: var(--muted);
  text-decoration: none; border-bottom: 2px solid transparent; white-space: nowrap; }
.casetabs .casetab:hover { color: var(--text); }
.casetabs .casetab.active { color: var(--text-strong); border-bottom-color: var(--text-strong); font-weight: 600; }
/* the second level reads as subordinate: smaller, pill-shaped, no rule under it */
.casetabs--sub { border-bottom: 0; margin-top: 10px; gap: 6px; }
.casetabs--sub .casetab { padding: 5px 11px; font-size: 12.5px; border: 1px solid var(--border);
  border-radius: 999px; border-bottom-width: 1px; }
.casetabs--sub .casetab.active { background: var(--panel); border-color: var(--border-strong); }
.casetab-bodies { margin-top: 12px; }
@media (max-width: 820px) {
  /* pills, and the bar scrolls inside itself so the PAGE never does */
  .casetabs { gap: 6px; border-bottom: 0; }
  .casetabs .casetab { border: 1px solid var(--border); border-radius: 999px; padding: 6px 12px; }
  .casetabs .casetab.active { background: var(--panel); border-color: var(--border-strong); }
}

/* ── deadlines (Prazos) ── one pill definition, three surfaces ── */
.dl-pill { display: inline-block; padding: 1px 7px; border-radius: 999px; font-size: 11px;
  font-weight: 600; white-space: nowrap; border: 1px solid transparent; }
.dl-pill.green { color: #2ea043; border-color: #2ea04355; background: #2ea04315; }
.dl-pill.amber { color: #d29922; border-color: #d2992255; background: #d2992215; }
/* red covers <=2 days AND everything overdue: at that point the distinction stops being useful */
/* overdue: the same red as a negative saving, from the same token */
.dl-pill.red   { color: var(--money-neg); border-color: #e5534b55; background: #e5534b15; }
.dl-row { display: flex; align-items: center; gap: 8px; padding: 5px 0; font-size: 13px; }
.dl-row .dl-label { flex: 1 1 auto; min-width: 0; }
.dl-row .dl-date { font-size: 12px; white-space: nowrap; }
.dl-row .dl-src { font-size: 11px; font-style: italic; }
.dl-donehead { font-size: 11px; text-transform: uppercase; letter-spacing: .4px; margin: 8px 0 2px; }
.dl-donerow { font-size: 12px; padding: 2px 0; }
.prazos-all { margin-bottom: 14px; }
.tbl--prazos { width: 100%; table-layout: fixed; }
.tbl--prazos th:nth-child(1) { width: 16% }
.tbl--prazos th:nth-child(2) { width: 30% }
.tbl--prazos th:nth-child(3) { width: 34% }
.tbl--prazos th:nth-child(4) { width: 20% }
.tbl--prazos td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
@media (max-width: 820px) {
  .tbl--prazos, .tbl--prazos tbody, .tbl--prazos tr, .tbl--prazos td { display: block; width: auto; }
  .tbl--prazos thead { display: none; }
  .tbl--prazos tr { border: 1px solid var(--border); border-radius: 10px; padding: 8px 10px; margin-bottom: 10px; }
  .tbl--prazos td { border: 0; padding: 3px 0; white-space: normal; }
}


/* ── Visitor-context hover box (Histórico, developer role only) ─────────────────────────────────
   ⚠️Fixed and portalled to <body> by app.js. Two reasons, both learned here: the table lives in an
   overflow container that CLIPS an absolutely positioned child, and .nav is a stacking context that
   a z-index inside the table cannot climb over.
   ⚠️[hidden] is declared explicitly. The UA rule that hides a [hidden] element is display:none, and
   the display below would otherwise win against it. */
.vcbox {
  position: fixed;
  z-index: 9000;
  max-width: 280px;
  padding: 9px 11px;
  display: block;
  background: var(--panel);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  box-shadow: 0 8px 24px rgba(0, 0, 0, .45);
  color: var(--text);
  font-size: 12px;
  line-height: 1.45;
  /* The box must never sit between the cursor and the cell it belongs to: a pointer event landing
     on it would fire mouseleave on the cell and flicker the box out from under the pointer. */
  pointer-events: none;
}
.vcbox[hidden] { display: none; }
.vcbox-lead { color: var(--muted); margin-bottom: 7px; }
.vcbox-rows { display: grid; grid-template-columns: auto 1fr; gap: 3px 14px; }
.vcbox-k { color: var(--muted); white-space: nowrap; }
.vcbox-v { color: var(--text-strong); text-align: right; }
.vcbox-note { margin-top: 7px; color: var(--muted); }
.vc-host { cursor: help; }


/* ── Histórico pager ─────────────────────────────────────────────────────────────────────────────
   The Histórico is server-paginated (25/50/100). The tools row sits ABOVE the table with the
   sub-tabs because it describes what you are about to read; the pager sits UNDER it, beside the
   total, because that is where the eye already is once the rows have been read.
   ⚠️Both wrap rather than scroll: this panel's standing rule is that nothing produces a horizontal
   scrollbar, and a pager is the easiest thing to forget that about at 390px. */
.hist-tools { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 0 0 10px; }
.hist-perpage { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; }
.hist-range { font-size: 12px; }
.hist-filter { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; }
/* Same border, radius, background and size as .lk-select above, so the toolbar reads as one row. */
.hist-filter-input {
  width: 260px; max-width: 46vw;
  background: var(--panel-2); border: 1px solid var(--border-strong); border-radius: 6px;
  color: var(--text); font-size: 12px; padding: 3px 6px;
}
.hist-filter-input:focus { outline: none; border-color: var(--accent); }
.hist-filter-input::placeholder { color: var(--muted); }
/* ⚠️THE AFFORDANCE IS THE UNDERLINE, NOT A COLOUR CHANGE. These cells already carry meaning through
   colour (.ident-founder, .ident-inferred) and recolouring them to say "clickable" would overwrite
   a fact with a hint. The pointer and the dotted underline say it without taking anything away. */
.hist-clickable { cursor: pointer; text-decoration: underline dotted var(--border-strong); text-underline-offset: 2px; }
.hist-clickable:hover { text-decoration-color: var(--accent); }
.hist-clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.pager { display: flex; align-items: center; gap: 10px; margin: 10px 0 4px; flex-wrap: wrap; }
.pager .btn.small { min-width: 84px; }
.pager .btn.small:disabled { opacity: .45; cursor: default; }
.pager .btn.small:disabled:hover { border-color: var(--border-strong); }
.pager-pos { font-size: 12px; }
@media (max-width: 820px) {
  .pager .btn.small { flex: 1 1 auto; min-height: 40px; }
}

/* ── "Conversa assumida" — human takeover badge (2026-09-10) ──────────────────────────────────────
   Amber, on the Casos row (under the name) and in the case header's identity line, whenever a human
   owns the thread: process.human_owned written by either bot, an open thread_owner row, or a bot_mute
   row for any of the person's identities. Read-side only (lib/takeover_view.js); who + since in the
   text, the long form + channel + note + raw source in the tooltip. A mute gets the amber outline. */
/* ⚠️WRAPS, on the row and in the header. The Client column is ~155px at 1280 and the cell
   ellipsises a nowrap chip at "· mu…" — the who/since it exists to show. A two-line pill under the
   name (the Vehicle cell's deadline pill has the same shape) beats a truncated one. */
.tag.takeover-badge { background: #3a2a10; color: #ffb454; font-weight: 600; white-space: normal; line-height: 1.5; }
.tag.takeover-badge.takeover-muted { border: 1px solid #c9922e; }
.takeover-slot { display: inline-flex; align-items: center; gap: 6px; max-width: 100%; }
/* ⚠️THE HEADER'S LONG FORM WRAPS. The row chip is short and stays nowrap inside its ellipsis cell; the
   header sentence ("Conversa assumida por Diogo (via session …) desde … · bot silenciado") measured
   433px of nowrap text in a 390 viewport (nav_layout 12f) and 510 on the live muted case — a
   page-wide horizontal scroll, the one thing the CRM's tables were rebuilt to never do. */
.takeover-slot .tag.takeover-badge { white-space: normal; line-height: 1.5; }


/* ── Status page + step notifications (2026-09-10) ───────────────────────────────────────────────
   A chip beside a client-facing checklist step: enviada / por enviar (+ Enviar) / desactivada.
   ⚠️THE WORDS ARE CSS-GENERATED (data-label + ::after) so a checklist row's textContent stays the
   step label alone — caseux_verify and phase_test read rows by exact text. */
.notify-chip { display: inline-block; font-size: 10px; line-height: 1.4; padding: 1px 6px; border-radius: 8px; margin-left: 4px; white-space: normal; }
.notify-chip::after { content: attr(data-label); }
.notify-chip.notify-sent { background: #16321a; color: #57ab5a; }
.notify-chip.notify-draft { background: #3a2a10; color: #ffb454; }
.notify-chip.notify-off { background: var(--border); color: var(--muted); }
.notify-send { margin-left: 4px; padding: 1px 7px; font-size: 10px; min-height: 22px; line-height: 1.4; }
.notify-send::after { content: attr(data-label); }
/* the case header: link + rotate + send + the per-case switch; wraps at 390 like the takeover badge */
.notify-slot { display: inline-flex; flex-wrap: wrap; align-items: center; gap: 6px; max-width: 100%; }
.notify-slot .tag { white-space: normal; line-height: 1.5; }
.notify-slot a.notify-link { color: #7cc0ff; text-decoration: none; }
.notify-slot a.notify-link:hover { text-decoration: underline; }
.notify-slot .notify-optout { background: #3a1a1a; color: #ff8a8a; }
.notify-slot .notify-questions { background: #1a2a3a; color: #7cc0ff; }
.notify-toggle { display: inline-flex; align-items: center; gap: 4px; font-size: 11px; cursor: pointer; }
.notify-toggle input { width: 16px; height: 16px; accent-color: #57ab5a; }
.notify-msg { font-size: 11px; }
