/* tacit.css — "tacit" preview design (spec 2026-09-01-tacit-redesign-preview.md).
   EVERY rule is scoped under body.preview: zero leakage into the current
   design. M1 covers fonts, sidebar chrome and the login page; cards/tables/
   buttons are later milestones. */

@font-face {
  font-family: "DM Sans";
  src: url(/static/fonts/dm-sans.woff2) format("woff2");
  font-weight: 300 700;
  font-display: swap;
}
@font-face {
  font-family: "JetBrains Mono";
  src: url(/static/fonts/jetbrains-mono.woff2) format("woff2");
  font-weight: 400 500;
  font-display: swap;
}

/* Tacit design tokens (light = the values this file used to hardcode).
   The [data-theme="dark"] override at the end of this file redefines them.
   --t-danger/--t-accent are constant across both themes. Sidebar tokens are
   declared for completeness: the sidebar is near-black in BOTH themes, so no
   body.preview sidebar rule may use these vars. */
body.preview {
  --t-bg:#f2f2f2; --t-card:#fff; --t-fg:#111; --t-muted:#6b6b6b; --t-faint:#a3a3a3;
  --t-border:#d4d4d4; --t-hairline:#e5e5e5; --t-chip:#f2f2f2; --t-danger:#dc2626;
  --t-accent:#f59e0b; --t-sidebar:#0d0d0d; --t-sidebar-fg:#a3a3a3; --t-sidebar-active:#1c1c1c;
  background:var(--t-bg); color:var(--t-fg); font-family:"DM Sans",system-ui,sans-serif;
}

/* ── Sidebar ─────────────────────────────────────────────── */
/* width/position stay inherited from base.html's .sidebar (fixed, 240px). */
body.preview .sidebar { background:#0d0d0d; overflow:visible; }

body.preview .v2-brand-logo { width:28px; height:28px; border-radius:6px; object-fit:cover; }
body.preview .brand-name { color:#fff; font-family:"DM Sans"; font-weight:600; font-size:15px; }

/* One inset system (owner feedback, mockup painel.png left edge): brand row,
   nav (links AND group labels) and footer all pad horizontally at 16px, so
   logo, icons, labels, email and buttons share one left AND right edge. */
body.preview .sidebar-brand { padding:22px 16px 18px; }

/* ── Brand dropdown (owner ruling, spec 2026-09-01) ─────────
   In preview, a superadmin or multi-member user's brand row is a BUTTON
   (#v2-brand-btn) opening the tenant menu — it replaces the two switcher
   bars, which shell.html no longer renders in preview. A plain-row user
   (single membership, no superadmin) has neither element, so nothing here
   applies to them.
   NOTE: base.html carries a legacy bare-button rule (specificity (0,3,2))
   that would paint these buttons blue with 1rem margin. The trigger uses an
   ID (beats it outright); the menu is nested inside .sidebar-brand, where
   one extra class pushes each selector to (0,4,2)+. Explicit margin/padding
   resets on every button below. */
body.preview .sidebar-brand { position:relative; }
body.preview .sidebar-brand button#v2-brand-btn { display:flex; align-items:center; gap:10px; background:none; border:0; padding:0; cursor:pointer; color:#fff; font:inherit; text-align:left; margin:0; }
body.preview .sidebar-brand button#v2-brand-btn i.bi-chevron-down { color:#a3a3a3; font-size:12px; margin-left:auto; flex:none; }
body.preview .sidebar-brand div.v2-brand-menu { position:absolute; top:calc(100% + 6px); left:0; right:0; min-width:200px; background:#1c1c1c; border:1px solid #2a2a2a; border-radius:8px; padding:6px; z-index:120; box-shadow:0 12px 32px rgba(0,0,0,.4); }
body.preview .sidebar-brand div.v2-brand-menu button.v2-brand-item { display:flex; align-items:center; justify-content:space-between; gap:10px; width:100%; padding:8px 10px; background:none; border:0; border-radius:6px; color:#d4d4d4; font-size:13px; cursor:pointer; text-align:left; margin:0; font-family:inherit; }
body.preview .sidebar-brand div.v2-brand-menu button.v2-brand-item:hover { background:#2a2a2a; color:#fff; }
body.preview .sidebar-brand div.v2-brand-menu button.v2-brand-item.current { color:#fff; font-weight:600; }
body.preview .v2-brand-check { display:none; }
body.preview .v2-brand-item.current .v2-brand-check { display:inline; color:#fff; }

/* ── Nav ─────────────────────────────────────────────────── */
/* The 16px nav inset turns each link's #1c1c1c background into an inset
   rounded pill — never a full-bleed bar. */
body.preview .sidebar-nav { padding:8px 16px; }
body.preview .sidebar-nav .nav-link { color:#a3a3a3; font-size:13.5px; padding:9px 12px; border-radius:8px; }
body.preview .sidebar-nav .nav-link:hover { background:#1c1c1c; color:#fff; }
body.preview .sidebar-nav .nav-link.active { position:relative; background:#1c1c1c; color:#fff; }
/* Active tick: 3px white strip landing exactly at the sidebar's left edge
   (left:-16px = the nav's horizontal padding), full item height, like the mockup. */
body.preview .sidebar-nav .nav-link.active::before { content:""; position:absolute; left:-16px; top:0; bottom:0; width:3px; background:#fff; border-radius:0 2px 2px 0; }
/* Group labels start at the same x as links — inset now comes from .sidebar-nav. */
body.preview .nav-group-label { color:#525252; font-size:10px; text-transform:uppercase; letter-spacing:.8px; padding:16px 0 6px; }

/* ── Footer ──────────────────────────────────────────────── */
body.preview .sidebar-footer { padding:16px; }
body.preview .sidebar-footer .user-chip { background:transparent; padding:8px 0; color:#d4d4d4; font-size:12.5px; display:flex; align-items:center; gap:8px; flex-wrap:nowrap; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
body.preview .v2-role-chip { display:inline-block; flex:none; border:1px solid #333; border-radius:999px; font-size:10px; text-transform:capitalize; letter-spacing:.5px; padding:2px 8px; color:#a3a3a3; }
body.preview .sidebar-footer .user-chip .v2-user-email { min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-weight:500; color:#d4d4d4; font-size:12.5px; }
body.preview .sidebar-action { color:#a3a3a3; }
body.preview .sidebar-action:hover { background:#1c1c1c; color:#fff; }
/* Footer buttons flush with the shared 16px inset (same edges as nav pills). */
body.preview .sidebar-footer .sidebar-action { padding:8px 0; }
body.preview .sidebar-footer .nav-group-label { padding-left:0; padding-right:0; }

/* ── Collapse button + collapsed rail ────────────────────── */
body.preview .sidebar button#sidebar-collapse { position:absolute; right:-14px; top:16px; z-index:101; width:28px; height:28px; margin:0; padding:0; border-radius:50%; background:#111; color:#fff; border:1px solid #333; font-size:14px; line-height:1; display:inline-flex; align-items:center; justify-content:center; cursor:pointer; }

body.preview.sidebar-collapsed .sidebar { width:64px; min-width:64px; }
body.preview.sidebar-collapsed .brand-name,
body.preview.sidebar-collapsed .nav-group-label,
body.preview.sidebar-collapsed .sidebar-footer,
body.preview.sidebar-collapsed .sidebar-nav .nav-link span.nav-text { display:none; }
body.preview.sidebar-collapsed .sidebar-nav .nav-link { justify-content:center; }
body.preview.sidebar-collapsed .main,
body.preview.sidebar-collapsed .switcher-bar { margin-left:64px; }
body.preview.sidebar-collapsed .sidebar button#sidebar-collapse { transform:rotate(180deg); }

/* ── Thin scrollbars ─────────────────────────────────────── */
body.preview *::-webkit-scrollbar { width:4px; height:4px; }
body.preview *::-webkit-scrollbar-thumb { background:var(--t-border); border-radius:2px; }

/* ── Login ───────────────────────────────────────────────── */
body.preview .v2-login { min-height:100vh; background:#0d0d0d; display:flex; align-items:center; justify-content:center; font-family:"DM Sans",system-ui,sans-serif; }
body.preview .v2-login-box { width:100%; max-width:340px; padding:0 20px; }
body.preview .v2-login h1 { color:#fff; font-size:26px; line-height:1.2; font-weight:700; margin:0 0 8px; }
body.preview .v2-login-sub { color:#6b6b6b; font-size:14px; margin:0 0 28px; }
body.preview .v2-login input { width:100%; box-sizing:border-box; background:#161616; border:1px solid #2a2a2a; border-radius:8px; padding:12px 14px; color:#fff; font-family:inherit; font-size:14px; margin-bottom:12px; }
body.preview .v2-login input:focus { outline:none; border-color:#525252; }
body.preview .v2-forgot { display:block; text-align:right; color:#6b6b6b; font-size:13px; margin:0 0 18px; text-decoration:none; }
body.preview .v2-login form button.v2-entrar { width:100%; background:#fff; color:#000; border:0; border-radius:8px; padding:12px; font-weight:600; font-size:15px; cursor:pointer; font-family:inherit; margin:0; }
body.preview .v2-login .error { color:#f87171; font-size:13px; margin:0 0 12px; }
body.preview .v2-preview-toggle { margin-top:24px; text-align:center; }
body.preview .v2-login div.v2-preview-toggle button, body.preview .sidebar div.v2-preview-toggle button { background:none; border:0; color:#525252; font-size:12px; cursor:pointer; text-decoration:underline; font-family:inherit; }

/* ── Public auth pages (forgot / reset / invite-accept) — preview reskin ──
   These three public pages extend base.html and share login.html's
   .login-page / .login-card chrome, but their handlers do NOT pass a
   `preview` flag (ForgotPage / ResetPage / InviteAcceptPage), so there is no
   server-side branch and no .v2-login markup to switch on. Under
   body.preview we restyle the EXISTING chrome into the same tacit family as
   .v2-login: near-black page, DM Sans, dark inputs, white primary button,
   muted links. Every token below (#0d0d0d / #161616 / #2a2a2a / #525252 /
   #6b6b6b / #f87171) is the exact .v2-login palette — no parallel colors. */
body.preview .login-page { background:#0d0d0d; }
body.preview .login-card { background:transparent; box-shadow:none; border-radius:0; padding:0; max-width:340px; }
body.preview .login-card .login-logo { display:none; }
body.preview .login-card h1 { color:#fff; font-size:26px; line-height:1.2; font-weight:700; margin:0 0 8px; text-align:left; }
body.preview .login-card p.tagline { color:#6b6b6b; font-size:14px; text-align:left; margin:0 0 28px; }
body.preview .login-card label { color:#6b6b6b; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.5px; }
body.preview .login-card input[type=text],
body.preview .login-card input[type=password],
body.preview .login-card input[type=email] { background:#161616; border:1px solid #2a2a2a; border-radius:8px; padding:12px 14px; color:#fff; font-size:14px; }
body.preview .login-card input[type=text]:focus,
body.preview .login-card input[type=password]:focus,
body.preview .login-card input[type=email]:focus { outline:none; border-color:#525252; box-shadow:none; }
body.preview .login-card input[disabled] { background:#1c1c1c; color:#6b6b6b; border-color:#2a2a2a; }
body.preview .login-card .btn-primary { background:#fff; color:#000; border:0; border-radius:8px; padding:12px; font-weight:600; font-size:15px; font-family:inherit; width:100%; }
body.preview .login-card a { color:#6b6b6b; text-decoration:none; }
body.preview .login-card a:hover { color:#a3a3a3; }
body.preview .login-card .login-footer { color:#525252; }
body.preview .login-card .error { color:#f87171; }

/* ── Painel (dashboard) — preview reskin ────────────────────
   M2a: every selector below is scoped to body.preview. The dashboard page's
   <main class="main"> holds everything styled here (KPI strip, chart cards,
   filter bar, Colaboradores table); the #modal dialog siblings it and keeps
   its v1 look except for the version badge in the modal title. ──────────── */

/* 1. Page frame */
/* ISSUE #36 — slimmer header block (preview only): explicit line boxes make the
   above-fold budget exact, and 28px→16px bottom margin. `body.preview
   .page-header` is (0,2,0) vs base's `.page-header` (0,1,0), so it wins on
   specificity regardless of which <style>/<link> comes later. */
body.preview .page-header { margin-bottom:16px; }
body.preview .page-header h1 { font-family:"DM Sans",system-ui,sans-serif; font-size:22px; font-weight:700; color:var(--t-fg); line-height:1.15; }
body.preview .page-header p { font-size:13px; color:var(--t-muted); margin-top:2px; line-height:1.35; }
body.preview .page-header .range-btn,
body.preview .page-header #tz-select { background:var(--t-card); border:1px solid var(--t-border); border-radius:999px; font-size:12.5px; padding:7px 14px; box-shadow:none; }
body.preview .page-header #tz-select { width:auto; }
body.preview .page-header .range-btn:hover { background:var(--t-card); border-color:var(--t-border); }
body.preview .page-header .range-picker.is-open .range-btn { box-shadow:none; border-color:#111; }

/* 2. KPI strip — one white card, four cells split by hairlines */
/* ISSUE #36 — strip ≈30% shorter than before (old ≈110–112px → new ≈81px at any
   width): 18px→8px vertical padding, kpi-value 28px→21px, explicit line boxes
   on label/sub so the height is deterministic in the above-fold budget. The 4th
   cell's inline font-size:16px (#kpi-app) still wins there via inline style — it
   is the shortest cell anyway. Uppercase micro labels unchanged. */
body.preview .v2-kpi-strip { background:var(--t-card); border:1px solid var(--t-border); border-radius:8px; padding:8px 0; gap:0 !important; }
body.preview .v2-kpi-strip .kpi-card { background:transparent; box-shadow:none; border:0; border-radius:0; padding:4px 28px; }
body.preview .v2-kpi-strip .kpi-card + .kpi-card { border-left:1px solid var(--t-hairline); }
body.preview .v2-kpi-strip .kpi-icon { display:none; }
body.preview .v2-kpi-strip .kpi-label { font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.6px; color:var(--t-muted); line-height:1.25; }
body.preview .v2-kpi-strip .kpi-value { font-size:21px; font-weight:700; color:var(--t-fg); font-variant-numeric:tabular-nums; }
body.preview .v2-kpi-strip .kpi-sub { font-size:12px; color:var(--t-muted); margin-top:2px; line-height:1.3; }

/* 3. Cards on the dashboard page */
body.preview .main .card { background:var(--t-card); border:1px solid var(--t-border); border-radius:8px; box-shadow:none; padding:22px; }
body.preview .main .card h3 { font-size:15px; font-weight:600; color:var(--t-fg); }
body.preview .main .card h3 i { display:none; }

/* 4. "Produtividade por Colaborador" tier legend (markup is preview-only) */
body.preview .v2-tier-legend { font-size:11px; font-weight:400; color:var(--t-muted); margin-left:12px; display:inline-flex; align-items:center; gap:6px; }
body.preview .v2-sub .v2-tier-legend { margin-left:0; }
body.preview .v2-tier-legend .sq { width:10px; height:10px; border-radius:2px; display:inline-block; margin-left:10px; }
body.preview .v2-tier-legend .sq:first-child { margin-left:0; }
body.preview .v2-tier-legend .sq-low { background:#ef4444; }
body.preview .v2-tier-legend .sq-mid { background:#f59e0b; }
body.preview .v2-tier-legend .sq-high { background:#111; }

/* 5. Colaboradores table */
body.preview .main .table thead th { background:#111; color:#fff; font-size:11px; text-transform:uppercase; letter-spacing:.6px; padding:12px 16px; border:0; position:static; }
body.preview .main #emp-table tbody td { border-top:1px solid var(--t-hairline); padding:14px 16px; font-size:13px; color:var(--t-fg); }
body.preview .main #emp-table tbody tr:hover { background:var(--t-chip); }

/* table search — white pill (same chrome as the page-header controls) */
body.preview .main .table-search { background:var(--t-card); border:1px solid var(--t-border); border-radius:999px; padding:7px 14px; box-shadow:none; }

/* status cell: renderTable() emits inline-styled spans (no class hooks).
   Text is neutralized via !important (inline styles win otherwise); the dot
   colors are matched off the inline marker the JS writes today — replace with
   proper classes in the M2b dashboard.js pass. */
body.preview .main #emp-table tbody td:first-child > span { font-size:12px; color:var(--t-muted) !important; }
body.preview .main #emp-table tbody td:first-child > span[style*="#16a34a"] > span { background:#16a34a !important; width:8px; height:8px; border-radius:50%; }
body.preview .main #emp-table tbody td:first-child > span[style*="#94a3b8"] > span { background:#a3a3a3 !important; width:8px; height:8px; border-radius:50%; }

/* productivity cell: neutral track + monochrome fill (M2b colors it by tier) */
body.preview .main #emp-table .progress { height:6px; background:var(--t-hairline); border-radius:4px; }
/* M2b: the micro-bar FILL is colored inline by SCORE TIER (renderTable) — drop the flat
   #111 !important override so that tier color wins; keep only the track + rounded end. */
body.preview .main #emp-table .progress-fill { border-radius:4px; }

/* app/site (and client-version) badges — both come out of renderTable as bare `.badge` */
body.preview .main #emp-table .badge,
body.preview #modal-title .badge { display:inline-block; background:var(--t-chip); border:1px solid var(--t-hairline); border-radius:999px; padding:2px 10px; font-size:11.5px; font-weight:500; color:var(--t-fg); }
/* App Principal is column 8 (renderTable emits no distinguishing class) — black chip */
body.preview .main #emp-table td:nth-child(8) .badge { background:#111; color:#fff; border-color:#111; border-radius:6px; padding:3px 10px; font-size:11.5px; }

/* Ações — outlined danger pill (overrides base .btn/.btn-danger + inline padding) */
body.preview .main #emp-table .btn-danger { background:var(--t-card); border:1px solid var(--t-danger); color:var(--t-danger); border-radius:999px; padding:5px 14px !important; font-size:12px; font-weight:600; }
body.preview .main #emp-table .btn-danger:hover { background:#fef2f2; }

/* 6. Filter bar (EXIBIR / ORDENAR) — markup is preview-only; M2b wires it */
body.preview .v2-filterbar { display:flex; align-items:center; gap:8px; flex-wrap:wrap; background:var(--t-card); border:1px solid var(--t-border); border-radius:8px; padding:12px 16px; margin-bottom:20px; }
body.preview .v2-filterbar .v2-fb-label { font-size:11px; font-weight:600; letter-spacing:.6px; color:var(--t-muted); text-transform:uppercase; margin-right:4px; }
body.preview .v2-filterbar .v2-fb-sep { width:1px; height:20px; background:var(--t-hairline); margin:0 12px; }
/* specificity (0,3,3) beats base.html's legacy button:not(...) rule (0,3,2 —
   explicit margin/padding reset as belt-and-braces */
body.preview div.v2-filterbar button.v2-pill { background:var(--t-card); border:1px solid var(--t-border); border-radius:999px; margin:0; padding:6px 16px; font-size:12.5px; font-weight:500; color:var(--t-fg); cursor:pointer; font-family:"DM Sans",system-ui,sans-serif; }
body.preview div.v2-filterbar button.v2-pill.active { background:#111; color:#fff; border-color:#111; }

/* ── M2b: Top charts — preview header + doughnut/legend layout ───────────────
   The .v2-hdr* / .v2-donut-* markup only exists inside the preview-only card
   branches (dashboard.html). Non-preview cards use the unchanged h3+canvas. The
   single body:not(.preview) guard below keeps the always-rendered legend wrapper
   hidden off-preview so that card's hbar stays full width. */

/* Card header: muted title + caption on one row, muted subtitle beneath.
   ISSUE #36: 14px→10px bottom margin — denser rhythm above the plot at all widths. */
body.preview .v2-hdr { margin-bottom:10px; }
body.preview .v2-hdr h3 { font-size:15px; font-weight:700; color:var(--t-fg); margin:0; line-height:1.3; }
body.preview .v2-hdr-top { display:flex; align-items:baseline; justify-content:space-between; gap:12px; flex-wrap:wrap; }
body.preview .v2-cap { font-size:11.5px; font-weight:400; color:var(--t-muted); white-space:nowrap; }
body.preview .v2-sub { margin-top:4px; font-size:12px; color:var(--t-muted); line-height:18px; min-height:18px; }

/* "Atividade por Hora" card (preview header only): the Pico hour anchors to the
   top-right of the header, overlapping above the plot — it REPLACES the old
   in-canvas text (the dashed vertical line stays drawn by JS). pointer-events
   none so the element never intercepts the chart's hover/tooltip. */
body.preview .v2-pico { position:absolute; top:34px; right:56px; text-align:right; pointer-events:none; }
/* label keeps title case "Pico" (no uppercase transform) per mockup */
body.preview .v2-pico-label { display:block; font-size:11px; color:var(--t-muted); letter-spacing:.5px; }
body.preview .v2-pico-hour { display:block; font-size:22px; font-weight:700; color:var(--t-fg); line-height:1.1; }

/* Apps card: canvas fills by default (non-preview hbars + preview Top-Top10 hbars). Only
   when the preview doughnut is active — detectable by legend rows being present via :has() —
   does the canvas give half the row to an HTML color legend. */
body.preview .v2-donut-wrap { display:flex; align-items:center; gap:24px; }
body.preview .v2-canvas { flex:1 1 auto; min-width:0; }
@supports (selector(:has(*))) {
  body.preview .v2-donut-wrap:has(.v2-donut-legend .row) .v2-canvas { flex:0 0 56%; max-width:56%; }
}
body.preview .v2-donut-legend { flex:1 1 auto; min-width:0; display:flex; flex-direction:column; gap:9px; }
body.preview .v2-leg-sq { flex:none; width:11px; height:11px; border-radius:3px; }
body.preview .v2-donut-legend .row { display:flex; align-items:center; gap:9px; font-size:13px; color:var(--t-fg); }
body.preview .v2-donut-legend .name { flex:1 1 auto; min-width:0; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; font-weight:500; color:var(--t-fg); }
body.preview .v2-donut-legend .pct { flex:none; color:var(--t-muted); font-variant-numeric:tabular-nums; }

/* Range-picker "Hoje": the button paints solid blue because base.html's legacy bare-button
    rule (line 356: `button` + three `:not()` steps, specificity (0,3,2)) beats the §1 pill
    rules (0,3,0) and dashboard.html's own .range-btn rules. These selectors add explicit
    element steps (div / button / i) so they win outright — no !important needed. M2a's §1
    rules (lines 96–100) remain as a lower-specificity backup for the same pill chrome. */
body.preview .page-header div.range-picker button.range-btn { background:var(--t-card); color:var(--t-fg); border:1px solid var(--t-border); border-radius:999px; margin:0; padding:7px 14px; box-shadow:none; font-family:"DM Sans",system-ui,sans-serif; font-size:12.5px; }
body.preview .page-header .range-btn i.bi-calendar3 { color:var(--t-muted); }
body.preview .page-header .range-btn .rp-chevron { color:var(--t-muted); }
body.preview .page-header div.range-picker button.range-btn:hover { background:var(--t-chip); border-color:var(--t-border); }
body.preview .page-header .range-picker.is-open button.range-btn { border-color:#111; box-shadow:none; }

/* Painel header · area filter ("Todos os setores") — owner-reported bugs,
   preview-only fixes:
   (a) pill chrome misaligned + blue :focus ring from base.html `select:focus`
       (specificity (0,1,1), loaded AFTER this file → only a higher count wins).
   (b) fixed #theme-icon-toggle (38px at top 18 / right 24) overlapping the tz
       select — button stays put; the controls row gains right padding instead.

   Specificity math:
   • wrapper  body.preview .page-header div#areaScope
       = 1 id · .preview+.page-header=2 classes · body+div=2 elements → (1,2,2).
       Beats dashboard.html's own .area-scope (0,1,0), .area-scope.active (0,2,0)
       and base's `select` rule ((0,0,1)) despite base's later load order.
   • select   body.preview .page-header div#areaScope select → (1,3,2)
       vs base select:focus (0,1,1) and [data-theme] .area-scope.active select
       (0,2,2) — the id step wins every pairing; no !important anywhere.

   Height/typography mirror of M2a's `.page-header #tz-select` (7px 14px pad,
   1 box-border, 999px radius, 12.5px type → identical computed box height). */
body.preview .page-header div#areaScope { background:var(--t-card); border:1px solid var(--t-border); border-radius:999px; padding:7px 14px; box-shadow:none; }
body.preview .page-header div#areaScope i { color:var(--t-muted); font-size:13px; }
body.preview .page-header div#areaScope select { border:none; background:none; outline:none; width:auto; font-size:12.5px; color:var(--t-fg); cursor:pointer; }
body.preview .page-header div#areaScope select:focus { border:none; box-shadow:none; outline:none; border-color:var(--t-fg); color:var(--t-fg); }
body.preview .page-header div#areaScope:hover { background:var(--t-chip); border-color:var(--t-border); }
body.preview .page-header div#areaScope.active { border-color:var(--t-fg); box-shadow:none; }
body.preview .page-header div#areaScope.active i,
body.preview .page-header div#areaScope.active select { color:var(--t-fg); }

/* (b) clearance for the fixed 38px #theme-icon-toggle (right:24px → its left
   edge sits 62px from the viewport/content right edge; 52px of in-row padding
   plus the row's natural right inset clears it with room). Same selector shape
   as the existing wrap rule at ≤900px (body.preview .page-header > div:last-child);
   adds only padding-right, so that rule keeps its flex-wrap/align/gap. No other
   page has right-side header controls → only the Painel is affected. */
body.preview .page-header > div:last-child { padding-right:52px; }

/* ──────────────────────────────────────────────────────────────────────────
   Global components — preview (M3a)
   One CSS pass reskinning the shared component classes on EVERY
   authenticated page: administração, máquinas, fila de espera, usuários do
   tenant, assentos, setores, instalação e conta.

   Guardrails honored here:
   • EVERY selector below starts with `body.preview`, scoped to `.main` so
     #modal (M4), the sidebar/login (M1) and the switcher bar stay untouched.
   • base.html's inline <style> loads AFTER this <link>, so all rules win by
     specificity alone (counted per rule in the notes):
       base .btn = (0,1,0) · .btn-primary/.btn-danger = (0,1,0)
       .table thead th = (0,1,2) · .badge = (0,1,0) · .card = (0,1,0)
       legacy bare-button rule button:not(...) = (0,3,2) — moot for us: every
       page button carries class="btn" and is therefore EXCLUDED by :not(.btn).
   • No font imports needed (DM Sans / JetBrains Mono declared at file top).
   • !important appears ONLY where base.html/install markup pins the very
     property we must change inline — each occurrence is documented in place.
   ──────────────────────────────────────────────────────────────────────── */

/* 1. Cards — .card frame itself already exists above (M2a §3); this pass adds
   the h2 headings and the muted description <p> that sits right after a card
   heading (the exact pattern in tenant_install.html / admin.html).
   !important (font-size, color ×4) JUSTIFICATION: those paragraphs carry an
   inline style="font-size:12.5px;color:var(--text-muted)" written for the v1
   design; installing the tacit muted look (13px / #6b6b6b) is impossible
   against an inline style without !important. Only .card description
   paragraphs match — no other element uses this selector shape.
   Specificity (0,3,3)/(0,3,4) vs base `.card h2/h3 context` (≤0,1,2). */
body.preview .main .card h2 { font-size:15px; font-weight:600; color:var(--t-fg); margin-top:0; }
body.preview .main .card h3 + p,
body.preview .main .card h2 + p { font-size:13px !important; color:var(--t-muted) !important; }

/* 2. Tables — every `.table` inside .main (admin users/delegação/registros/
   auditoria, machines, queue, tenant users/invites, seats). Specificity:
   mine (0,3,3) vs base.html `.table thead th` (0,1,2) / `.table tbody td` and
   `tr:hover` (0,1,2) — higher on BOTH class and element counts, so we win
   regardless of base's <style> coming after this link. Collapsed-border model:
   the hairline is drawn on td (base's row border-top merges with it via
   border-collapse:collapse → one line per boundary).
   The #emp-table rules above stay as the higher-specificity override. */
body.preview .main .table thead th { background:#111; color:#fff; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.6px; padding:12px 16px; border:0; position:static; }
body.preview .main .table tbody td { border-top:1px solid var(--t-hairline); padding:12px 16px; font-size:13px; color:var(--t-fg); }
body.preview .main .table tbody tr:hover { background:var(--t-chip); }

/* 3. Buttons — classes actually used across the templates (grepped):
   .btn / .btn-primary / .btn-danger on ADMIN, CONTA, ÁREAS, ASSENTOS, FILA,
   USUÁRIOS + install.js copy buttons. `.btn-sm` does NOT exist anywhere in
   x9-server (templates or static) → no rule added; M3b needs markup work if
   small buttons are wanted beyond the inline-styled copy buttons.
   Mine (0,3,1) vs base (0,1,0); hover rules (0,4,1/0,4,2) win over base's
   (0,2,0)/(0,3,0)-class hovers. */
body.preview .main .btn { background:var(--t-card); border:1px solid var(--t-border); color:var(--t-fg); box-shadow:none; border-radius:8px; padding:9px 18px; font-size:13.5px; font-weight:600; }
body.preview .main .btn:hover { background:var(--t-chip); border-color:var(--t-border); color:var(--t-fg); }
/* solid black — the plain `background:#111` value kills base.html's
   linear-gradient shorthand at higher specificity */
body.preview .main .btn-primary { background:#111; border-color:#111; color:#fff; }
body.preview .main .btn-danger { background:var(--t-card); border:1px solid var(--t-danger); color:var(--t-danger); }
body.preview .main .btn-danger:hover { background:#fef2f2; border-color:var(--t-danger); color:var(--t-danger); }

/* 4. Form controls — the exact base.html selector set
   (input[type=text|password|email|number|date|time|file], select, textarea)
   mirrored type-by-type; checkbox/hidden inputs intentionally untouched.
   Mine (0,3,1)/(0,4,1) beat base's element+type rules (0,2,1)/(0,2,2).
   Widths (width:100%) stay as base sets them. Labels: mine (0,2,1) vs
   bare `label` (0,0,1). */
body.preview .main input[type=text], body.preview .main input[type=password], body.preview .main input[type=email], body.preview .main input[type=number], body.preview .main input[type=date], body.preview .main input[type=time], body.preview .main input[type=file],
body.preview .main select, body.preview .main textarea { background:var(--t-card); border:1px solid var(--t-border); border-radius:8px; padding:10px 14px; font-size:14px; color:var(--t-fg); font-family:"DM Sans",system-ui,sans-serif; }
body.preview .main input[type=text]:focus, body.preview .main input[type=password]:focus, body.preview .main input[type=email]:focus, body.preview .main input[type=number]:focus, body.preview .main input[type=date]:focus, body.preview .main input[type=time]:focus, body.preview .main input[type=file]:focus,
body.preview .main select:focus, body.preview .main textarea:focus { outline:none; border-color:#111; box-shadow:none; }
body.preview .main label { font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.5px; color:var(--t-muted); }

/* 5. Badges — markup in use (grepped across templates + tenant.js/superadmin.js):
   bare `.badge`, `.badge-amber` (Pendente — the mockup chip), `.badge-teal`
   (Enviado/Aceito), `.badge-grey` (Expirado/Revogado/Envio desativado),
   `.badge-red`. Order matters: variant rules FOLLOW the generic one, so
   each re-asserts its hue over the neutral chip base without needing
   !important. All at (0,3,1) vs base (0,1,0); the higher-specificity
   #emp-table badge rules above stay untouched. `.badge-red` remains the only
   red by design; `-teal`/`-grey` are preserved to keep Enviado≠Expirado
   distinguishable once they sit on the new neutral base. */
body.preview .main .badge { display:inline-block; background:var(--t-chip); border:1px solid var(--t-hairline); color:var(--t-fg); border-radius:999px; padding:2px 10px; font-size:11.5px; font-weight:500; }
body.preview .main .badge-amber { background:#fef3c7; border-color:#fde68a; color:#92400e; }
body.preview .main .badge-teal  { background:#f0fdfa; border-color:#ccfbf1; color:#0f766e; }
body.preview .main .badge-grey  { background:#f1f5f9; border-color:#e2e8f0; color:#475569; }
body.preview .main .badge-red   { background:#fef2f2; border-color:#fecaca; color:#dc2626; }

/* 6. Empty state — base's `.empty-state` is (0,1,0); mine (0,3,1) wins
   (color moves from var(--text-faint) #94a3b8 to token muted).
   Current markup in ALL templates (areas/seats/queue/users/install) is PLAIN
   TEXT inside <div class="empty-state"> — no icon element exists yet; the `i`
   rule below is a dead hook M3b can fill: it lands only when an <i> icon is
   added, producing the 48px circle from the mockups. */
body.preview .main .empty-state { color:var(--t-muted); }
body.preview .main .empty-state i { display:inline-flex; align-items:center; justify-content:center; width:48px; height:48px; margin-bottom:14px; border-radius:50%; background:var(--t-chip); color:var(--t-muted); font-size:20px; }

/* 7. Tabs — global-class rule per spec. NOTE: the only `.tabs` in the repo is
   #modal-tabs INSIDE #modal (dashboard.html:413), which M4 owns — my
   `.main`-scoped selectors therefore match NOTHING today and are inert by
   design (no leakage into the modal). They pre-position the styling for any
   non-modal tab strip a later pass adds. Specificity when they do apply:
   (0,4,1)/(0,5,1) vs base `.tabs .tab(.active)` (0,2,0)/(0,3,0). */
body.preview .main .tabs { border-bottom:1px solid var(--t-border); }
body.preview .main .tabs .tab { font-size:13px; font-weight:600; color:var(--t-muted); }
body.preview .main .tabs .tab.active { color:var(--t-fg); border-bottom:2px solid #111; }

/* 8. Page headers — already generalized by the M2a rule
   `body.preview .page-header h1/p` (h1 22px 700 #111, p 13px #6b6b6b) which
   applies to every authenticated header and beats base's `.page-header h1/p`
   ((0,2,2) vs (0,1,1)) even though base loads after this link.
   No new selectors needed for item 8. */

/* 9. Callouts — classes present in template/JS markup: .callout-warn
   (tenant_install.html, install.js legacy-token notice), .callout-info
   (tenant_users.html), .callout-danger (docs.html inline defs). Grepped and
   NOT FOUND: .warn, .alert, .info-box → dead names, no rules. base.html
   defines NO .callout styles, so the generic below also supplies LAYOUT
   (flex + gap) on tenant pages where it is currently unstyled; on docs.html
   it overrides that page's local look ((0,3,1) beats its (0,1,0)/(0,2,0)) in
   favor of the tacit system. `.callout-danger` re-asserted red so it does not
   collapse into the neutral frame. */
body.preview .main .callout { display:flex; align-items:flex-start; gap:12px; border:1px solid var(--t-hairline); border-radius:8px; padding:12px 16px; font-size:13px; line-height:1.55; }
body.preview .main .callout-warn { background:#fffbeb; border-color:#fde68a; color:#92400e; }
body.preview .main .callout-info { background:var(--t-chip); border-color:var(--t-hairline); color:var(--t-muted); }
body.preview .main .callout-danger { background:#fef2f2; border-color:#fecaca; color:var(--t-danger); }

/* 10. Instalação page — token + command blocks. install.js injects
   <code id="ti-token"> / <code id="ti-cmd"> carrying INLINE
   style="font-size:13px;padding:.5rem .7rem;background:var(--bg-subtle,#f4f4f5);
   border-radius:6px" and the Copiar buttons carry inline padding:4px 14px.
   Inline styles cannot be beaten by specificity, hence each !important below:

     code#ti-cmd: background, border-radius, padding — inline pins
       #f4f4f5 / 6px / .5rem .7rem for the v1 design; spec (instalacao.png)
       requires the dark block: #111 bg, #a3e635 JetBrains Mono text, 8px
       radius, 14px 16px padding.
     code#ti-token: background, border-radius, font-size — inline pins
       #f4f4f5 / 6px / 13px; spec requires the light chip: #f5f5f5 + 1px
       #e5e5e5 border, 8px radius, JetBrains Mono 12.5px #111.
     button#ti-copy / #ti-copy-cmd: padding — inline pins 4px 14px (v1 slim);
       the solid-black pill needs 6px 16px to read as a pill over the new .btn
       chrome. Their font-size:12px inline is DESIRABLE ("small") and kept, so
       no !important there.
   All other properties are absent from the inline styles, so plain
   declarations at (1,2,1)/(1,3,1) win outright. */
body.preview .main code#ti-token { background:var(--t-chip) !important; border:1px solid var(--t-hairline); border-radius:8px !important; font-family:"JetBrains Mono",monospace; font-size:12.5px !important; color:var(--t-fg); }
body.preview .main code#ti-cmd { display:block; background:#111 !important; color:#a3e635; font-family:"JetBrains Mono",monospace; border-radius:8px !important; padding:14px 16px !important; }
body.preview .main button#ti-copy, body.preview .main button#ti-copy-cmd { background:#111; border-color:#111; color:#fff; border-radius:999px; padding:6px 16px !important; }

/* 11. Table wrappers — `.table-wrap` (admin/queue/users/superadmin + the
   dashboard page-local def, (0,1,0)) and `.docs-table-wrap` (docs.html local,
   (0,1,0)); mine at (0,3,1) win. overflow-x:auto restores horizontal scroll
   for wide tables (Auditoria, Fila de Espera); overflow-y:hidden + the 8px
   border-radius still clip the black thead corners against the rounded frame
   (the 4px thin scrollbar above keeps the bar unobtrusive). */
body.preview .main .table-wrap { max-width:100%; border:1px solid var(--t-border); border-radius:8px; overflow-x:auto; overflow-y:hidden; }
body.preview .main .docs-table-wrap { max-width:100%; border:1px solid var(--t-border); border-radius:8px; overflow-x:auto; overflow-y:hidden; }

/* ── M3c gap-fill: account grid, areas empty state, token modal ──────
   Two parallel M3 passes left these gaps. The account + areas markup only
   exists inside {% if preview %} branches (account.html, tenant_areas.html);
   the superadmin token modal emits different ids than install.js (item 14). */

/* 12. Account page — two-per-row form grid. `.v2-account-card` also carries
   class="card", so the frame (white / #d4d4d4 / 8px / 22px) already matches
   the `.main .card` rule; re-asserted here so the card reads standalone, plus
   the 20px bottom gap the base .card rule does not supply. Labels/inputs are
   already reskinned by M3a §4. The submit button is the form's next sibling,
   so it sits BELOW the grid — matches conta.png. */
body.preview .v2-account-card { background:var(--t-card); border:1px solid var(--t-border); border-radius:8px; padding:22px; margin-bottom:20px; }
body.preview .v2-form-grid { display:grid; grid-template-columns:1fr 1fr; gap:14px 20px; margin-bottom:16px; }
body.preview .v2-field { min-width:0; }
@media (max-width:700px) {
  body.preview .v2-form-grid { grid-template-columns:1fr; }
}

/* 13. Áreas empty state — the preview branch wraps the glyph in a
   .v2-empty-icon div (tenant_areas.html). M3a §6's `.empty-state i` hook was
   written for a bare <i> directly under .empty-state; here .v2-empty-state
   still carries class="empty-state", so that hook would also paint the nested
   <i> as a second 48px circle inside .v2-empty-icon. The (0,4,2) override
   below neutralizes it: the circle is the .v2-empty-icon div, the <i> is a
   plain glyph (it inherits the 20px / #6b6b6b from .v2-empty-icon). */
body.preview .v2-empty-state { text-align:center; padding:60px 20px; color:var(--t-muted); }
body.preview .v2-empty-icon { width:48px; height:48px; border-radius:50%; background:var(--t-chip); display:flex; align-items:center; justify-content:center; margin:0 auto 14px; font-size:20px; color:var(--t-muted); }
body.preview .main .empty-state .v2-empty-icon i { display:inline; width:auto; height:auto; margin:0; background:none; border-radius:0; }

/* 14. Superadmin token modal — reconciliation vs install.js. M3a §10 styled
   code#ti-token / code#ti-cmd / button#ti-copy / button#ti-copy-cmd, and
   those ARE the ids install.js emits (install.js:79/81/105/107) — correct,
   nothing to change there. superadmin.js's modal (#token-modal-body) instead
   emits <code id="tk-token"> + <button id="tk-copy" class="btn">
   (superadmin.js:165/167), so the #ti-* selectors never match the modal and
   its token kept the v1 look. Same chip + pill re-declared for the tk-* ids:
   the inline styles pin the same four properties, so the !important repeats
   §10's justification (background/border-radius/font-size on the token,
   padding on the button). background:#111 on the button is plain (no
   !important): (1,2,1) beats .main .btn's (0,3,1) outright. */
body.preview .main code#tk-token { background:var(--t-chip) !important; border:1px solid var(--t-hairline); border-radius:8px !important; font-family:"JetBrains Mono",monospace; font-size:12.5px !important; color:var(--t-fg); }
body.preview .main button#tk-copy { background:#111; border-color:#111; color:#fff; border-radius:999px; padding:6px 16px !important; }

/* ──────────────────────────────────────────────────────────────────────────
   M4 — Documentação, modais (globais), file input
   Same guardrails as M3: EVERY selector below starts with `body.preview`, so
   non-preview users see zero difference. tacit.css loads BEFORE base.html's
   inline <style> and before docs.html's own <style> (both later in the
   document), so every rule here wins by specificity, not order — each note
   states its own count against the competing rule it must beat.
   ──────────────────────────────────────────────────────────────────────── */

/* 1. Documentação (docs.html) — TOC rail + section typography.
    The page-local <style> (docs.html:5–44) runs LAST in the cascade, so each
    selector below re-declares the exact local shape plus the body.preview
      local .docs-toc a                (0,1,1) → mine  (0,2,1)
      local .docs-toc a.active         (0,2,1) → mine  (0,3,2)   [kills the --primary tint]
      local .docs-section h2           (0,1,1) → mine  (0,2,2)
      local .docs-section h2 .sec-num  (0,2,1) → mine  (0,3,2)
    Content column width: the local .docs-layout grid already caps the body at
    minmax(0,860px) — already the readable width the mockup shows, so no layout
    override. `.docs-table` internals stay as-is by owner ruling ("tables
    already fine"); only the wrap frame comes from M3a §11. */
body.preview .docs-toc .toc-title { font-size:10px; font-weight:600; letter-spacing:.8px; text-transform:uppercase; color:var(--t-muted); margin-bottom:10px; }
body.preview .docs-toc a { font-size:12.5px; color:var(--t-muted); padding:5px 10px; border-left:2px solid var(--t-border); background:transparent; text-decoration:none; line-height:1.35; }
body.preview .docs-toc a:hover { color:var(--t-fg); }
/* active: #111 + the 2px left indicator turning #111; the local rule's
   color-mix(primary,8%) background is killed to transparent — monochrome */
body.preview .docs-toc a.active { color:var(--t-fg); font-weight:600; border-left-color:#111; background:transparent; }

/* section cards + headings, tacit tokens (M3 card system: #d4d4d4 / 8px) */
body.preview .main .docs-section { background:var(--t-card); border:1px solid var(--t-border); border-radius:8px; box-shadow:none; }
body.preview .main .docs-section h2 { font-size:17px; font-weight:700; color:var(--t-fg); }
body.preview .main .docs-section h2 .sec-num { color:var(--t-faint); }
/* sec-sub + body text (local 13.5px / line-height 1.75) are already neutral — untouched */
body.preview .main .docs-section h3 { font-size:14px; font-weight:600; color:var(--t-fg); }

/* meta chips (.docs-meta, plus the §downloads variant) — M3 neutral chip tokens */
body.preview .docs-chip { background:var(--t-card); border-color:var(--t-border); color:var(--t-muted); }
body.preview .docs-chip strong { color:var(--t-fg); }

/* code: inline chips (ti-token family) + the one <pre><code> block (§5).
   local .docs-section pre (0,2,0) → mine (0,3,1); local .docs-table code's
   white-space:nowrap (0,2,0) stays — no property clash with my chip. */
body.preview .main .docs-section code { background:var(--t-chip); border:1px solid var(--t-hairline); border-radius:6px; padding:1px 6px; font-family:"JetBrains Mono",monospace; font-size:12.5px; color:var(--t-fg); }
body.preview .main .docs-section pre { background:var(--t-chip); border:1px solid var(--t-hairline); border-radius:8px; padding:14px 16px; overflow-x:auto; font-family:"JetBrains Mono",monospace; font-size:12.5px; line-height:1.7; color:var(--t-fg); }
body.preview .main .docs-section pre code { background:none; border:0; padding:0; font-size:inherit; }

/* §7 colored ranking legend — the ONLY inline colors left in docs content
   (three <strong style="color:#16a34a|#eab308|#ef4444">, near docs.html:419).
   Preview follows the M2b tier ruling (Alta #111 · Média #6b6b6b · Baixa
   amber); hooks are the additive v2-tier-* classes I add to those strongs
   (v1 ignores an unknown class — no behavior change there). The inline v1
   pinning makes !important required on color, exactly as in M3a §10/§14.
   NOTE: the surrounding copy still reads "(verde)/(amarelo)/(vermelho)" —
   content accuracy is an owner call (see final report), not something a
   class flip may rewrite. */
body.preview .main .docs-section strong.v2-tier-high { color:var(--t-fg) !important; }
body.preview .main .docs-section strong.v2-tier-mid  { color:#f59e0b !important; }
body.preview .main .docs-section strong.v2-tier-low  { color:#ef4444 !important; }

/* 2. Modals — GLOBAL by design: the Painel detail modal (dashboard.html) is a
   SIBLING of <main>, so every M3a rule (all scoped to `.main`) misses it;
   the superadmin token modal (superadmin_tenants.html) uses class="modal",
   which base.html never styles at all — only .modal-overlay/.modal-xl exist
   there. So both frames get rules here, scoped to body.preview (NOT .main) so
   any future modal anywhere inherits the same look. Counts:
      base .modal-overlay            (0,1,0) → mine (0,2,1)
      base .modal-xl                 (0,1,0) → mine (0,2,1) [the dashboard inline
                                                        max-width/position are not
                                                        declared here and stay]
      base `h1,h2,h3`                (0,0,1) + #modal-title's inline font-size:18px
      base .tabs                     (0,1,0) → mine (0,3,1)
      base .tabs .tab                (0,2,0) → mine (0,4,1)
      LEGACY button:not(.btn):not(.sidebar-action):not(.rp-presets button)
                                     (0,3,2) → my .tab/.tab.active selectors at
      base .tabs .tab.active         (0,3,0)   (0,4,1)/(0,5,1) — this legacy rule is
                                                what paints v1's modal tab buttons
                                                solid #2563eb; neutralized here by
                                                explicit background/margin/border. */
body.preview .modal-overlay { background:rgba(0,0,0,.5); }
body.preview .modal-xl, body.preview .modal { background:var(--t-card); border:1px solid var(--t-border); border-radius:12px; box-shadow:0 20px 50px rgba(0,0,0,.15); padding:24px; }
/* !important JUSTIFICATION: #modal-title pins font-size:18px inline for v1 —
   inline styles cannot be beaten by any specificity, and no other modal heading
   (e.g. the token modal's h3) carries a size pin, so one shared rule with the
   flag covers both without side effects. */
body.preview .modal-xl h2, body.preview .modal h2,
body.preview .modal-xl h3, body.preview .modal h3 { font-size:16px !important; font-weight:600; color:var(--t-fg); }

/* modal tabs — the M3a §7 look, re-declared for the #modal scope where those
   `.main`-scoped rules are inert by design */
body.preview .modal-xl .tabs, body.preview .modal .tabs { border-bottom:1px solid var(--t-border); }
body.preview .modal-xl .tabs .tab, body.preview .modal .tabs .tab { padding:8px 16px; margin:0; background:none; border:0; border-radius:0; color:var(--t-muted); font-size:13px; font-weight:600; cursor:pointer; }
body.preview .modal-xl .tabs .tab:hover, body.preview .modal .tabs .tab:hover { color:var(--t-fg); background:var(--t-chip); }
body.preview .modal-xl .tabs .tab.active, body.preview .modal .tabs .tab.active { color:var(--t-fg); border-bottom:2px solid #111; }
body.preview .modal-xl .tab-count, body.preview .modal .tab-count { background:var(--t-hairline); color:var(--t-muted); }

/* modal buttons — M3a §3 values verbatim (white pill / solid black / outlined
   danger), re-scoped for outside-`.main` modals. Inside the token modal
   (which IS within .main) both rule pairs match the Fechar button at equal
   (0,3,1); this one comes later in the file and wins — values are identical,
   so no drift either way. */
body.preview .modal-xl .btn, body.preview .modal .btn { background:var(--t-card); border:1px solid var(--t-border); color:var(--t-fg); box-shadow:none; border-radius:8px; padding:9px 18px; font-size:13.5px; font-weight:600; }
body.preview .modal-xl .btn:hover, body.preview .modal .btn:hover { background:var(--t-chip); border-color:var(--t-border); color:var(--t-fg); }
body.preview .modal-xl .btn-primary, body.preview .modal .btn-primary { background:#111; border-color:#111; color:#fff; }
body.preview .modal-xl .btn-danger, body.preview .modal .btn-danger { background:var(--t-card); border:1px solid var(--t-danger); color:var(--t-danger); }
body.preview .modal-xl .btn-danger:hover, body.preview .modal .btn-danger:hover { background:#fef2f2; border-color:var(--t-danger); color:var(--t-danger); }

/* modal tables (#tbl-apps / #tbl-sites) — M3a §2 look re-scoped.
   thead th: mine (0,3,2) vs base (0,1,2) and dark-theme [data-theme=dark]
   overrides (≤0,2,2) → win regardless of load order. position:static matches
   M3a §2/§5 (no sticky headers inside a scrolling modal). */
body.preview .modal-xl .table-wrap { border:1px solid var(--t-border); border-radius:8px; overflow-x:auto; overflow-y:hidden; }
body.preview .modal-xl .table thead th { background:#111; color:#fff; font-size:11px; font-weight:600; text-transform:uppercase; letter-spacing:.6px; padding:12px 16px; border:0; position:static; }
body.preview .modal-xl .table tbody td { border-top:1px solid var(--t-hairline); padding:12px 16px; font-size:13px; color:var(--t-fg); }
body.preview .modal-xl .table tbody tr:hover { background:var(--t-chip); }

/* modal form controls — the hora filter row (select#timeline-date, #start-h /
   #end-h) sits outside .main and was the one component M3a §4 could not
   reach; M3 values re-scoped. The focus rules kill base's blue box-shadow
   (`input:focus, select:focus` at (0,1,1) < mine). */
body.preview .modal-xl select, body.preview .modal select,
body.preview .modal-xl input[type=number], body.preview .modal input[type=number] { background:var(--t-card); border:1px solid var(--t-border); border-radius:8px; padding:9px 12px; font-size:13.5px; color:var(--t-fg); font-family:"DM Sans",system-ui,sans-serif; }
body.preview .modal-xl select:focus, body.preview .modal select:focus,
body.preview .modal-xl input[type=number]:focus, body.preview .modal input[type=number]:focus { outline:none; border-color:#111; box-shadow:none; }

/* 3. admin.html logo card — native file input (admin.html:23, inside .main).
   M3a §4 already frames the control itself (white / #d4d4d4 / 8px), so only
   the selector BUTTON and text tone are added here. No rule anywhere touches
   ::file-selector-button; margin-top:0 (via the shorthand) is insurance
   against base's legacy bare-button rule (0,3,2) should a UA expose it as
   <button>. font-size/color at (0,3,1) — equal to M3a §4's file-input entry
   but LATER in this file, so the 12.5px/#6b6b6b wins by order where both
   match; admin.html carries no inline styles on that input. */
body.preview .main input[type=file] { font-size:12.5px; color:var(--t-muted); }
body.preview .main input[type=file]::file-selector-button { background:var(--t-card); border:1px solid var(--t-border); border-radius:999px; padding:6px 16px; margin:0 10px 0 0; font-size:12.5px; color:var(--t-fg); font-family:"DM Sans",system-ui,sans-serif; cursor:pointer; }
body.preview .main input[type=file]::file-selector-button:hover { background:var(--t-chip); border-color:#111; color:var(--t-fg); }

/* ──────────────────────────────────────────────────────────────────────────
   Dark theme (mockup dark mode) — app.js sets data-theme="dark" on <html>
   and persists "x9-theme". Only the TOKEN LAYER of body.preview changes; the
   sidebar, .v2-login and the public-auth chrome are near-black in BOTH themes
   by design (every body.preview rule above that styles them keeps literal
   colors — verified: no var(--t-*) lands there).

   Ink policy (ORIGINAL "keep all other #111 near-black" ruling REVERSED by
   issue #5 — those #111 interactive accents die invisibly on the #161616
   card): `color:#111` text became var(--t-fg) everywhere in this file, so
   dark text goes near-white; near-black INTERACTIVE inks now flip to
   var(--t-fg) too — the same inversion .btn-primary already gets below —
   covering .v2-pill.active, the active tab/TOC indicator bars and the
   input/select focus borders; .btn-danger:hover washes dark red
   (rgba(220,38,38,.14)) instead of its near-white #fef2f2 flash. ONLY purely
   decorative #111 chips that carry their own white text keep literal
   near-black in dark: the column-8 badge and the copy buttons. */
[data-theme="dark"] body.preview {
  --t-bg:#0d0d0d; --t-card:#161616; --t-fg:#f5f5f5; --t-muted:#a3a3a3;
  --t-faint:#6b6b6b; --t-border:#2a2a2a; --t-hairline:#262626; --t-chip:#1c1c1c;
}

/* Colaboradores-table dark set (owner's mockup addendum). The added
   [data-theme] attribute step lifts every selector below over the literal
   rules above, regardless of load order. */

/* Header: a DARK bar (#1c1c1c on the #161616 card) with MUTED uppercase
   text — NOT a black bar with white text. */
[data-theme="dark"] body.preview .main .table thead th,
[data-theme="dark"] body.preview .modal-xl .table thead th { background:#1c1c1c; color:#a3a3a3; }

/* Secondary pill chrome — incl. the version pill in #modal-title: dark chip,
   border-tone text. (App Principal inverts to a light pill below and wins
   via its extra td:nth-child step.) */
[data-theme="dark"] body.preview .main #emp-table .badge,
[data-theme="dark"] body.preview #modal-title .badge { background:#1c1c1c; border-color:#2a2a2a; color:#a3a3a3; }

/* App Principal (column 8): inverted LIGHT pill — white-ish bg, black text. */
[data-theme="dark"] body.preview .main #emp-table td:nth-child(8) .badge { background:#f5f5f5; color:#111; border-color:#f5f5f5; }

/* Status cell: "Online" keeps its green — text AND dot go #22c55e (beats the
   inline #16a34a + the base rules' !important via the attribute step).
   "Offline" needs no rule: the tokenized base already gives gray dot +
   muted text (#a3a3a3 = --t-muted in dark). */
[data-theme="dark"] body.preview .main #emp-table tbody td:first-child > span[style*="#16a34a"] { color:#22c55e !important; }
[data-theme="dark"] body.preview .main #emp-table tbody td:first-child > span[style*="#16a34a"] > span { background:#22c55e !important; }

/* Productivity micro-bar: track #2a2a2a. The fill and the % text are inline
   JS tier colors — the dashboard.js worker is making those theme-aware
   (high → white in dark), so CSS only owns the track. */
[data-theme="dark"] body.preview .main #emp-table .progress { background:#2a2a2a; }

/* solid primary buttons flip to white-with-black-text (the mockup's dark
   buttons). Modal scopes re-declared for the #modal siblings of <main>. */
[data-theme="dark"] body.preview .main .btn-primary,
[data-theme="dark"] body.preview .modal-xl .btn-primary { background:var(--t-fg); color:#111; border-color:var(--t-fg); }

/* command block goes true-black on the dark card; the lime text stays. */
[data-theme="dark"] body.preview .main code#ti-cmd { background:#000 !important; }

/* "Alta" swatch reads as a white chip in dark (its #111 base is invisible on
   the #161616 card). */
[data-theme="dark"] body.preview .v2-tier-legend .sq-high { background:#f5f5f5; }

/* ISSUE #5 — five low-contrast nits on the dark card (owner decision, 2026-09):
   the four base rules hardcode near-black #111 for interactive accents —
   invisible where they land on the #161616 card — and .btn-danger:hover flashes
   near-white #fef2f2. Same fix as the .btn-primary inversion above: the ink
   becomes var(--t-fg); danger hovers wash dark red instead (rgba is already in
   this file — .modal-overlay, box-shadows). The decorative #111 chips that
   carry their own white text (column-8 badge, copy buttons) deliberately stay
   near-black. Every selector below gains the [data-theme] attribute step over
   its base rule, so it wins regardless of load/order; outside preview, or in
   light theme, none of these selectors match — zero behavior change there. */

/* 1. Active tab underline — .main scope is inert by design (M3a §7) and the
    live modal pair; only the color flips, width/track stay base's. */
[data-theme="dark"] body.preview .main .tabs .tab.active { border-bottom-color:var(--t-fg); }
[data-theme="dark"] body.preview .modal-xl .tabs .tab.active,
[data-theme="dark"] body.preview .modal .tabs .tab.active { border-bottom-color:var(--t-fg); }

/* 2. Active filter pill — the exact mirror of the .btn-primary dark treatment
    immediately above (solid near-white block, near-black text). */
[data-theme="dark"] body.preview div.v2-filterbar button.v2-pill.active { background:var(--t-fg); color:#111; border-color:var(--t-fg); }

/* 3. Docs TOC active bar — the 2px left indicator flips near-white. */
[data-theme="dark"] body.preview .docs-toc a.active { border-left-color:var(--t-fg); }

/* 4. Input focus borders, both base scope groups (mirror the exact selector
    lists): only the color changes — outline:none / box-shadow:none stay as
    base declares them, no property clash. */
[data-theme="dark"] body.preview .main input[type=text]:focus, [data-theme="dark"] body.preview .main input[type=password]:focus, [data-theme="dark"] body.preview .main input[type=email]:focus, [data-theme="dark"] body.preview .main input[type=number]:focus, [data-theme="dark"] body.preview .main input[type=date]:focus, [data-theme="dark"] body.preview .main input[type=time]:focus, [data-theme="dark"] body.preview .main input[type=file]:focus,
[data-theme="dark"] body.preview .main select:focus, [data-theme="dark"] body.preview .main textarea:focus { border-color:var(--t-fg); }
[data-theme="dark"] body.preview .modal-xl select:focus, [data-theme="dark"] body.preview .modal select:focus,
[data-theme="dark"] body.preview .modal-xl input[type=number]:focus, [data-theme="dark"] body.preview .modal input[type=number]:focus { border-color:var(--t-fg); }

/* 5. .btn-danger:hover over the dark card (all three base scopes re-covered
    with their EXACT selector shapes — the #emp-table pair keeps its id step):
    translucent red wash instead of the #fef2f2 flash; border/text unchanged
    as var(--t-danger). */
[data-theme="dark"] body.preview .main #emp-table .btn-danger:hover { background:rgba(220,38,38,.14); border-color:var(--t-danger); color:var(--t-danger); }
[data-theme="dark"] body.preview .main .btn-danger:hover { background:rgba(220,38,38,.14); border-color:var(--t-danger); color:var(--t-danger); }
[data-theme="dark"] body.preview .modal-xl .btn-danger:hover,
[data-theme="dark"] body.preview .modal .btn-danger:hover { background:rgba(220,38,38,.14); border-color:var(--t-danger); color:var(--t-danger); }

/* ── Sun/moon theme button (shell.html v2 branch only) ────────────────────
   Fixed top-right of the content area. Beating base.html's legacy bare-button
   rule (0,3,2) needs an id step: preview+v2-theme-btn=2 classes,
   #theme-icon-toggle=1 id, body+button=2 elements → (1,2,2); margin/padding
   reset explicitly as belt-and-braces. */
body.preview button#theme-icon-toggle.v2-theme-btn { position:fixed; top:18px; right:24px; z-index:90; width:38px; height:38px; border-radius:50%; background:var(--t-card); border:1px solid var(--t-border); color:var(--t-muted); display:flex; align-items:center; justify-content:center; cursor:pointer; font-size:16px; margin:0; padding:0; line-height:1; box-shadow:none; }
body.preview button#theme-icon-toggle.v2-theme-btn:hover { color:var(--t-fg); }

/* ──────────────────────────────────────────────────────────────────────────
   M5 — Painel responsivo (owner feedback 2026-09: "horizontal scroll too
   early" — the layout must ADAPT first; a 1024px window must NOT page-scroll).
   All rules body.preview-scoped → non-preview markup untouched. !important is
   used ONLY against inline grid-template-columns pinned in dashboard.html —
   inline styles cannot be beaten by any specificity, so the flag is the only
   route, and the selectors stay preview-scoped (owner-verified requirement).

   Breakpoint math for the 1024px floor (sidebar expanded at 240px): content
   width ≈ 1024 − 240 − 36 (padding under ≤1200px) ≈ 748px. Everything below a
   ~750px budget: KPI 2×2 (≈361px cells), single-column chart cards, wrapped
   filter bar + header controls — all fit; the Colaboradores table scrolls
   INSIDE its .table-wrap card (overflow-x:auto + max-width:100%, M3a §11)
   instead of pushing the page. Below ~900px scrolling is allowed if truly
   necessary. Collapsed-sidebar variant untouched: the existing
   body.preview.sidebar-collapsed .main { margin-left:64px } rule targets a
   different property than anything here, so it keeps working at every width.

   1200px — charts 2 col → 1 col (the canvases are maintainAspectRatio:false,
   so Chart.js reflows to the card's real width on the resize); KPI strip
   inline auto-fit → explicit 2×2; hairlines dropped under 1200px (owner-OK
   "keep it simple"); .main gutter tightens.
   700px — KPI strip single column, plain stacking (hairlines already gone,
   the zeroing rule above lives in the ≤1200 media block that encompasses it).
   ──────────────────────────────────────────────────────────────────────── */
/* ── Chart grid: shrink-chain guards + 2-col track pin, EVERY preview width ──
   M5 follow-up. dashboard.html's inline style="…grid-template-columns:1fr 1fr…"
   IS ACTIVE ABOVE 1200px too, and bare `1fr` keeps an auto (min-content)
   minimum — so the same collapse chain fixed below ≤1200px was inflating
   track 1 at a 1280px window: Playwright measured unequal 620px/480px tracks
   and a horizontal page scroll (scrollWidth 1328 vs clientWidth 1280), the
   c-prod canvas' Chart.js inline width being the card's min-content. The
   three guards below therefore live OUTSIDE the media block; none of them is
   duplicated inside it afterwards. They keep their exact preview scope:
   non-preview markup is untouched. */

/* Min-content guards for the rest of the shrink chain — without these the
   canvas' inline px width still propagates canvas → container → card → page:
   grid items (and the position:relative canvas holders) must be allowed to
   render below their content size. */
body.preview .main .card { min-width:0; }
body.preview .v2-charts-grid .card > div { min-width:0; }

/* Belt-and-braces: Chart.js writes an explicit `width` px style on the
   canvas itself. This `max-width` clamp is scoped (body.preview + this grid
   only) and documented here per owner requirement — it cannot leak to
   non-preview pages or other chart containers; the !important only exists
   to beat that canvas inline style, which no specificity can outweight. */
body.preview .v2-charts-grid canvas { max-width:100% !important; }

/* Two equal flexible tracks at every width: beats the INLINE `1fr 1fr` pin
   (an inline value no specificity can outrank — only !important does), and
   minmax(0,1fr) zeroes each track's minimum so an inflated canvas inline
   width cannot push a track wider than its share of the budget. Math at
   1201px (sidebar expanded 240 + .main padding 32px×2 = 64): 1201 − 240 − 64
   = 897 content; (897 − 16 gap) / 2 = 440.5px per track, both ≤ budget, and
   the canvas clamps to its card via the max-width:100% guard above. Under
   1200px the LATER single-column declaration in the media block below wins
   (both !important, same specificity → order), so this pin leaves that path
   unchanged. */
body.preview .v2-charts-grid { grid-template-columns:minmax(0,1fr) minmax(0,1fr) !important; }

/* Equal-height 2×2 rows + charts filling their cards (OWNER FEEDBACK, screenshot-verified:
   the bottom-row "Top 5 Sites" card rendered 5 bars top-packed with a void below while the
   donut card filled — the two cards in a row must be equal height and each chart fill its
   card). In preview:
   (1) rows STRETCH instead of start-aligning. The grid div carries inline
       align-items:start from dashboard.html — an inline value no specificity can outrank,
       so !important is the only override (same documented pattern as the
       grid-template-columns pin above; class hook .v2-charts-grid already exists there).
   (2) each card becomes a flex column: header block first (auto-sized), chart area after.
   (3) the three shared canvas boxes (c-prod / c-hourly / c-sites) wear a PREVIEW-ONLY
       .v2-chart-box class in dashboard.html — the inline height:260px stays in the markup
       because those wrappers render for BOTH modes and can't be conditionally stripped
       without branching, so this rule reverts it to `height:auto !important` (the
       !important exists solely to beat that one inline value) and flex-fills it, with
       min-height:260px so nothing collapses. Dashboard.js no longer sets these boxes'
       height in preview — the flex layout owns it now.
   (4) the donut card's .v2-donut-wrap flexes too (min-height:260px floor for its 56%-width
       canvas) and STAYS vertically centering donut+legend via the existing
       `align-items:center` rule above in this file — so the wrap absorbs whatever extra row
       height the taller card creates instead of pinning at content size. */
body.preview .v2-charts-grid { align-items:stretch !important; }
/* ISSUE #36 — tighter chart-card padding (16px vertical instead of base's 22px);
   same specificity as §3's `.main .card` rule but LATER in the file, so it wins
   on order. Only the four grid cards are affected — KPI strip, Colaboradores
   card and modal cards keep their padding. */
body.preview .v2-charts-grid .card { display:flex; flex-direction:column; min-width:0; padding:16px 20px; }

/* ── ISSUE #36 — chart boxes sized from the REMAINING viewport height (owner
   ruling: at ~1440×900 the 4 KPI cells + all four chart cards must be fully
   visible with no vertical scroll; Colaboradores table may stay below the
   fold). Replaces the fixed min-height:260px floors above — those made the two
   rows 355px+ tall and pushed the page past a 900px viewport.
   Calibration C(v) = max(180px, (100vh−410px)/2), UNBOUNDED above — anchored to
     the LIVE MEASURED data point at 1440×900 (Playwright post-render): with
     bottom(v) ≈ K + 2·C and v=900/C=240 measured → 879 ✓, K ≈ 398. Same K then
     solves the constant X in (100vh−X)/2: bottom(v) ≈ 398 +(v−X) = v −(X−398),
     so X = 410 puts the grid bottom a FIXED ~10-11px above ANY fold:
       v=700  → (700−410)/2=145 clamps to the 180 floor · bottom ≈ 758 —
                ~58px past the fold, short windows may scroll (owner-accepted);
       v=853  → C=221.5           bottom ≈ 841  (~12px to fold; owner's peek fix)
       v=900  → C=245             bottom ≈ 888  (~12px; was 878, approved, small
                shift to ~890 explicitly allowed)
       v=1080 → C=335             bottom ≈ 1068 (~12px)
       v=1400 → C=495             bottom ≈ 1388 (~12px) — cap gone, so the fold
                tracks up and the next section (filter bar / table) never peaks
                in from below at tall heights.
   Bounds:
     • 180px floor (KEPT) — binds below v ≈ 770px; both rows' floors are
       identical, so row heights stay equal and charts never crush below a
       renderable height;
     • 340px cap REMOVED — owner screenshot at ~853px CSS height: the cap's dead
       space plus the 22px margin let "the beginning of the chart below" peek
       in; max() (floor only) lets canvases keep growing with the viewport,
       any tall height included (4K), with no absurdity guard needed since the
       fold itself bounds the growth. */
body.preview .v2-charts-grid .card > .v2-chart-box { height:auto !important; flex:1 1 auto; min-height:max(180px, calc((100vh - 410px) / 2)); }
body.preview .v2-charts-grid .card > .v2-donut-wrap { flex:1 1 auto; min-height:max(180px, calc((100vh - 410px) / 2)); }
/* The donut's .v2-canvas carries an inline height:260px in dashboard.html (line 364)
   that is SHARED with the v1 branch — v1 must stay byte-identical, so instead of
   editing the markup we override it here (preview only): without this rule row 2
   stays pinned at 260+86 ≈ 346px and the (100vh−410px) calibration would be moot for that row. */
body.preview .v2-charts-grid .card > .v2-donut-wrap > .v2-canvas { height:max(180px, calc((100vh - 410px) / 2)) !important; }

/* ISSUE #4 (preview-only): the "Sem dados no período" empty state for the four chart cards.
   dashboard.js setChartEmpty() lazily appends ONE .chart-empty div per canvas parent
   (.v2-chart-box / .v2-canvas — all position:relative, height 260px+) and toggles it; the
   canvas itself is hidden via inline visibility, never removed. pointer-events:none so the
   overlay never intercepts touches on a hidden canvas.
   Dark theme: NO dedicated rule — like every other body.preview rule here it reuses the
   tokens that `[data-theme="dark"] body.preview` already redefines (--t-border #2a2a2a /
   --t-muted #a3a3a3), so both modes resolve correctly from this single block. */
body.preview .chart-empty { position:absolute; inset:0; z-index:1; display:flex; flex-direction:column; align-items:center; justify-content:center; gap:10px; text-align:center; pointer-events:none; }
body.preview .chart-empty-ico { width:48px; height:48px; flex:none; border:1px solid var(--t-border); border-radius:50%; color:var(--t-muted); display:flex; align-items:center; justify-content:center; }
body.preview .chart-empty-tx { font-size:13px; color:var(--t-muted); }

@media (max-width:1200px) {
  /* Chart grid single column: later declaration wins over the unconditional
     two-column pin above (both !important, same specificity → order), still
     beating dashboard.html's inline style="…grid-template-columns:1fr 1fr…"
     on the now-classed div (.v2-charts-grid) — an inline value no specificity
     can outrank. minmax(0,1fr) zeroes the track minimum so Chart.js' inline
     canvas width (bare `1fr` would keep an auto min-content floor) takes the
     ~748px budget from the math above instead of inflating (988 ≤ 1024). */
  body.preview .v2-charts-grid { grid-template-columns:minmax(0,1fr) !important; }

  /* KPI strip: beats inline repeat(auto-fit,minmax(180px,1fr)) → fixed 2×2.
     (Same specificity as the base §2 rule but LATER in the file — wins on
     order; only !important is needed for the property the inline pins.)
     The >1200 default keeps its inline repeat(auto-fit,minmax(180px,1fr)):
     4 cells × 180px floor = 720px stays under the >1200 content budget (e.g.
     897px at 1201px), so it needs no guard there. */
  body.preview .v2-kpi-strip { grid-template-columns:minmax(0,1fr) minmax(0,1fr) !important; padding-top:6px; padding-bottom:6px; }

  /* Hairlines — in a 2×2 wrap "border-left on every + sibling" would draw
     mid-row bars, and in single-column mode full-height separators. Dropping
     them entirely is the owner-blessed simple route (≤1200 → down to 0). */
  body.preview .v2-kpi-strip .kpi-card + .kpi-card { border-left:0; }
  /* Cells now rely on internal padding for separation instead of hairlines.
     Same specificity as base §2's .kpi-card rule (0,4,1) — later wins. */
  body.preview .v2-kpi-strip .kpi-card { box-sizing:border-box; padding:16px 24px; }

  /* Content gutter: 28px 32px (base.html .main) → 20px 16px. Specificity
     (0,2,0) vs base's (0,1,0) — wins on specificity alone even though
     base.html's <style> loads AFTER this <link>. margin-left untouched: the
     expanded 240px and collapsed 64px rules keep their exact behavior. */
  body.preview .main { padding:20px 16px; }

  /* page-header controls — the inline div pins display:flex/gap/align but NOT
     flex-wrap, so a plain declaration at (0,3,2) suffices here and also beats
     dashboard.html's local @media(≤900px) wrap rule ((0,2,1)) with identical
     intent — no order dependency either way. The h1 column already wraps via
     base's .page-header { flex-wrap:wrap }. */
  body.preview .page-header > div:last-child { flex-wrap:wrap; align-items:end; gap:10px; }
}

@media (max-width:700px) {
  /* KPI strip single column, stacked — separators already zeroed by the rule
     above (that media block covers this width too). minmax(0,1fr) for the same
     min-content reason as the 2×2 rule: unbroken app names ("App Mais Usado")
     must not pin the track wider than the card. */
  body.preview .v2-kpi-strip { grid-template-columns:minmax(0,1fr) !important; }
  body.preview .v2-kpi-strip .kpi-card { padding:14px 8px; }
}

/* ── ISSUE #30 — Machine → Usuário dropdown (spec §5): white-pill chrome in preview.
   v1 gets the plain native <select> as-is and NEVER matches a rule below (all scoped
   to body.preview). #modal is a SIBLING of <main>, so the modal-header variant follows
   the M4 pattern — body.preview + an id/class step, never .main; same for dark-theme
   focus re-points. Light/dark otherwise resolve from the [data-theme="dark"] body.preview
   tokens already redefined above (single set of base rules). */
     /* Alignment pass v2 (owner 2nd-round feedback): the pill was wider than the word and a few px
        right of the bare-text usernames. Full metric control of the select:
     - appearance:none (+ -webkit) kills the native chrome AND its internal UA text padding; the chevron
       is now drawn by US — an inline-SVG caret (muted #909090 stroke: readable on both the light #fff and
       dark #1c1c1c cards) as a background-image data: URI (CSP img-src allows 'self' data:), pinned right
       8px, vertically centered. Scoped to .user-select ONLY — the #modal-user-row select and every v1/
       non-preview rule keep their native chevrons unchanged.
     - Width: dashboard.js sizes each select via canvas measureText of the LONGEST option in the select's
       computed font (DM Sans 13px normal) + this rule's chrome — no more ch-of-longest overestimate
       (sizeUserSelect in dashboard.js). Zone = right padding 24px = 6px buffer + 10px caret + 8px outer
       inset, so the text can NEVER touch the chevron (caret left edge is >=6px past the content edge).
     - Left: padding-left 0 → after appearance:none the only inset before the text is the 1px border;
       margin-left:-1px absorbs it so the selected word's first glyph sits at EXACTLY the td content edge —
       the same x as bare-text usernames in neighboring rows (td rule: padding 14px 16px).
     - Vertical: tight vertical padding + line-height:1 + inline-block/vertical-align:middle keep the
       grouped row no taller than plain rows (pill total ~20.5px vs ~16-18px text).
     - Text metrics matched to the td rule: 13px, inherited weight — quiet like the cell text. */
 body.preview .main #emp-table select.user-select { background-color:var(--t-card); border:1px solid var(--t-border); border-radius:999px; padding:3px 24px 3px 8px; background-image:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' fill='none' stroke='%23909090' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E"); background-repeat:no-repeat; background-position:right 8px center; appearance:none; -webkit-appearance:none; line-height:1; display:inline-block; vertical-align:middle; min-width:0; margin:0 0 0 -9px; /* width is set per-select inline by dashboard.js sizeUserSelect (longest option, canvas-measured) — no width/min-width here */ font-size:13px; color:var(--t-fg); font-family:"DM Sans",system-ui,sans-serif; cursor:pointer; box-shadow:none; }
body.preview .main #emp-table select.user-select:focus { outline:none; border-color:#111; box-shadow:none; }
body.preview .main #emp-table select.user-select option { background:#fff; color:#111; font-family:"DM Sans",system-ui,sans-serif; }
body.preview #modal-user-row select { background:var(--t-card); border:1px solid var(--t-border); border-radius:999px; padding:8px 30px 8px 14px; /* same chevron-room fix as the table cell above (issue #30) */ font-size:13px; color:var(--t-fg); font-family:"DM Sans",system-ui,sans-serif; cursor:pointer; margin:0; box-shadow:none; }
body.preview #modal-user-row select:focus { outline:none; border-color:#111; box-shadow:none; }
/* dark card: #111 focus ink would die (same ruling as the issue-#5 focus overrides above) */
[data-theme="dark"] body.preview .main #emp-table select.user-select:focus { border-color:var(--t-fg); }
[data-theme="dark"] body.preview #modal-user-row select:focus { border-color:var(--t-fg); }
[data-theme="dark"] body.preview .main #emp-table select.user-select option,
[data-theme="dark"] body.preview #modal-user-row option { background:#1c1c1c; color:#f5f5f5; }

/* ── ⚙ "Atividade por Hora" per-user window (0016, issue #31 follow-up) ─
   Markup ONLY in dashboard.html's preview card branch (#hourly-win-card >
   .v2-hdr): a gear trigger + one popover with De/Até (final state: gear, .v2-pico, and the popover all resolve against #hourly-win-card
    position:relative — the gear sits top:10px/right:10px from the card border, per the owner's corner ruling)
   selects and Salvar/Fechar. body.preview-scoped → zero v1 leakage.
   Specificity against base.html's legacy bare-button rule (0,3,2) — every
   button selector below carries an id step (#hourly-win-btn /
   #hourly-win-pop), which beats (0,3,x) outright; explicit margin/padding
   resets are belt-and-braces, per the .v2-brand-menu / range-picker notes. */
/* ── ⚙ "Atividade por Hora" — positioning context ─ The corner-anchored gear /
   .v2-pico / popover now resolve against the CARD itself (owner: the gear sits
   ~10px from the card's visible border, not inset by the card's 16/20px padding),
   so the header is left static and the card becomes the positioning context.
   Scoped to #hourly-win-card only → no other card's layout changes. */
body.preview .main #hourly-win-card { position:relative; }

body.preview .v2-hdr button#hourly-win-btn { position:absolute; top:10px; right:10px; width:30px; height:30px; border-radius:8px; background:none; border:0; color:var(--t-muted); font-size:14px; cursor:pointer; display:flex; align-items:center; justify-content:center; margin:0; padding:0; box-shadow:none; }
body.preview .v2-hdr button#hourly-win-btn:hover { background:rgba(128,128,128,.14); color:var(--t-fg); }

body.preview div#hourly-win-pop { position:absolute; top:46px; right:10px; z-index:120; min-width:212px; background:var(--t-card); border:1px solid var(--t-border); border-radius:8px; padding:10px; box-shadow:0 12px 32px rgba(0,0,0,.18); }
body.preview div#hourly-win-pop .v2-win-field { display:flex; align-items:center; gap:8px; margin-bottom:8px; }
body.preview div#hourly-win-pop .v2-win-field label { flex:none; width:24px; font-size:12px; color:var(--t-muted); }
body.preview div#hourly-win-pop .v2-win-field select { flex:1; min-width:0; border:1px solid var(--t-border); border-radius:6px; background:transparent; color:var(--t-fg); font-family:inherit; font-size:12.5px; padding:6px 8px; margin:0; }
[data-theme="dark"] body.preview div#hourly-win-pop .v2-win-field select { background:#1c1c1c; color:#f5f5f5; }
body.preview div#hourly-win-pop .v2-win-actions { display:flex; gap:8px; margin-top:2px; }
body.preview div#hourly-win-pop button.v2-win-save { flex:1.2; background:var(--t-fg); color:var(--t-card); border:1px solid var(--t-fg); border-radius:6px; font-family:inherit; font-size:12.5px; font-weight:600; padding:7px 12px; margin:0; cursor:pointer; box-shadow:none; }
body.preview div#hourly-win-pop button.v2-win-save:hover { opacity:.88; }
body.preview div#hourly-win-pop button.v2-win-close { flex:1; background:none; color:var(--t-muted); border:1px solid var(--t-border); border-radius:6px; font-family:inherit; font-size:12.5px; padding:7px 12px; margin:0; cursor:pointer; box-shadow:none; }
body.preview div#hourly-win-pop button.v2-win-close:hover { color:var(--t-fg); border-color:var(--t-fg); }

/* ── Issue #19 — ⚙ Config menu + Expediente popover (detail modal header row) ─
   Shared markup in dashboard.html (#cfg-wrap gear + #cfg-menu dropdown +
   #cfg-exp-panel popover, all anchored to #cfg-wrap's position:relative). The
   detail modal is a SIBLING of <main> → scope like the M4 modal rules: body.preview
   + the .modal-xl frame step (the dashboard markup uses .modal-xl; no other #cfg-*
   ids exist on the page, so the id steps keep this inert elsewhere).
   Dark-popover chrome reuses the sidebar's v2-brand-menu family above (lines 57–60) —
   near-black card (#1c1c1c), #2a2a2a border, muted #d4d4d4 item text, #2a2a2a hover —
   identical in both themes, exactly like the existing preview dropdowns. The danger
   row washes dark red on hover (same rgba(220,38,38,.14) as the issue-#5 modal rule).
   Popover CONTENT gets its own dark treatment so the v1-derived .card/.exp-row/input
   rules (light tokens in dashboard.html's <style>) read correctly on #1c1c1c. */
body.preview .modal-xl #cfg-menu { background:#1c1c1c; border:1px solid #2a2a2a; box-shadow:0 14px 36px rgba(0,0,0,.5); }
/* Round-2 fix (screenshot defect: rows rendered as SOLID BLUE buttons in preview
   light AND dark too): the first pass overrode only `color` — but base.html's
   legacy bare-button rule (0,3,2) still owned background (#2563eb), padding and
   margin-top, because v1's .cfg-item (background:none, 0,1,0 and 0,2,0 on hover)
   loses to (0,3,2). The #cfg-menu id step puts every selector below at
   (1,3,1)+ → wins the class/element races outright, so this one rule now pins the
   full quiet-row chrome (v2-brand-menu pattern, lines 57–61) instead of just ink. */
body.preview .modal-xl #cfg-menu .cfg-item { color:#d4d4d4; margin:0; padding:9px 12px; background:none; border:0; border-radius:7px; box-shadow:none; cursor:pointer; text-align:left; }
body.preview .modal-xl #cfg-menu .cfg-item:hover { background:#2a2a2a; color:#fff; }
body.preview .modal-xl #cfg-menu .cfg-item.cfg-danger { color:#f87171; }
body.preview .modal-xl #cfg-menu .cfg-item.cfg-danger:hover { background:rgba(220,38,38,.14); color:#f87171; }

body.preview .modal-xl #cfg-exp-panel { background:#1c1c1c; border:1px solid #2a2a2a; box-shadow:0 16px 44px rgba(0,0,0,.5); color:#e5e5e5; }
body.preview .modal-xl #cfg-exp-panel .card { background:#161616; border:1px solid #2a2a2a; box-shadow:none; }
body.preview .modal-xl #cfg-exp-panel .kpi-label,
body.preview .modal-xl #cfg-exp-panel .exp-hours { color:#a3a3a3; }
body.preview .modal-xl #cfg-exp-panel .exp-row { background:#161616; border-color:#2a2a2a; }
body.preview .modal-xl #cfg-exp-panel .exp-day { color:#f5f5f5; }
body.preview .modal-xl #cfg-exp-panel input[type=text] { background:#111; border:1px solid #2a2a2a; border-radius:6px; color:#f5f5f5; box-shadow:none; }
body.preview .modal-xl #cfg-exp-panel input[type=text]:focus { outline:none; border-color:#737373; }
/* #exp-split carries an INLINE var(--text-muted) (v1 light gray — low contrast on the
   dark panel); one scope-bound !important is the only rule that can reach it. */
body.preview .modal-xl #cfg-exp-panel #exp-split { color:#a3a3a3 !important; }
