/*
 * This is a manifest file that'll be compiled into application.css.
 *
 * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
 * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
 * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
 * depending on specificity.
 *
 * Consider organizing styles into separate files for maintainability.
 */

/* (O override de `--color-yellow-500` que existia aqui foi removido em 07/ago:
   ele empurrava o amarelo para o dourado do Tailwind, e agora a família inteira
   é servida pela rampa âmbar do @theme. Um override neste arquivo venceria o
   tema por ordem de cascata e reabriria a divergência que a virada fechou.) */

/* Brand palette (--color-brand-*) is defined in the Tailwind @theme block
   (app/assets/tailwind/application.css), which emits the :root variables AND
   the bg-brand-… / text-brand-… utilities. The var(--color-brand-*) references
   throughout this stylesheet resolve against those generated variables — same
   mechanism already used by blueprint-500 below. */

/* Active-tool indicator for the facade sketchpad toolbar. Uses the brand
   tan CSS variable because Tailwind doesn't expose a `tan-500` utility and
   inline styles fall afoul of the app's CSP. */
.facade-tool-active {
  background-color: var(--color-brand-tan);
  color: #ffffff;
  border-color: var(--color-brand-tan);
}

/* Active-segment indicator for the image-provider toggle (Gemini / Grok).
   Same brand tan as the tool buttons so the highlight stays consistent. */
.provider-toggle-active {
  background-color: var(--color-brand-tan);
  color: #ffffff;
  border-color: var(--color-brand-tan);
  font-weight: 600;
}


/* Blueprint Grid — minor 16px + major 80px lines with radial fade.
   Uses ::before pseudo-element so the mask fades only the grid,
   not the page content sitting on top. */
.blueprint-grid {
  position: relative;
}
.blueprint-grid::before {
  content: "";
  position: absolute;
  inset: -24px 0 0 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(107, 102, 90, 0.09) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(107, 102, 90, 0.09) 1px, transparent 1px),
    linear-gradient(to right, rgba(107, 102, 90, 0.05) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(107, 102, 90, 0.05) 1px, transparent 1px);
  background-size: 80px 80px, 80px 80px, 16px 16px, 16px 16px;
  mask-image: linear-gradient(to bottom, black 80%, transparent 100%);
  -webkit-mask-image: linear-gradient(to bottom, black 80%, transparent 100%);
}

.dark .blueprint-grid::before {
  background-image:
    linear-gradient(to right, rgba(107, 102, 90, 0.14) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(107, 102, 90, 0.14) 1px, transparent 1px),
    linear-gradient(to right, rgba(107, 102, 90, 0.08) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(107, 102, 90, 0.08) 1px, transparent 1px);
  background-size: 80px 80px, 80px 80px, 16px 16px, 16px 16px;
}

/* Blueprint Title — company name rendered as an architectural cartouche,
   stamped into the blueprint grid at the same visual weight as the major
   grid lines. Uses the same steel-slate palette so it reads as part of
   the drawing sheet, not a UI heading. */
.blueprint-title {
  font-size: clamp(1.25rem, 2.2vw, 2rem);
  font-weight: 800;
  text-transform: uppercase;
  letter-spacing: 0.25em;
  color: rgba(107, 102, 90, 0.13);
  line-height: 1;
}
.dark .blueprint-title {
  color: rgba(107, 102, 90, 0.18);
}

/* Price History - Expand/Collapse animation */
.expanded {
  grid-template-rows: 1fr !important;
}

.details-collapsed {
  grid-template-rows: 0fr;
}

.rotated {
  transform: rotate(90deg);
}

.field-packet-watermark {
  color: rgba(107, 102, 90, 0.2);
}

.popover-panel {
  opacity: 0;
  transform: translateY(-4px) scale(0.98);
  pointer-events: none;
  transition: opacity 100ms ease-out, transform 100ms ease-out;
}

.popover-panel.open {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

/* ============================================================
   Cost Code Table V2 — Depth gauge & density styles
   ============================================================ */

/* Depth gauge: left-edge hierarchy markers (status-aware) */
.depth-L1-completed  { border-left: 4px solid #35a077; } /* emerald-500 — all services done */
.depth-L1-in-progress    { border-left: 4px solid #c4831f; } /* amber-500 — some quotes received or pending */
.depth-L1-vendor-pending { border-left: 4px solid #c4831f; } /* yellow-500 — internal done, vendor pending */
.depth-L1-not-started { border-left: 4px solid #c94a3c; } /* red-500 — needs attention */
.depth-L1-muted      { border-left: 4px solid #cfc8b7; } /* slate-300 — no material services */
.depth-L2 { border-left: 4px solid transparent; }
.depth-L3 { border-left: 4px solid transparent; }

/* Permit stages indicators (monitoring tables) */
.permit-stages-complete  { border-left: 4px solid #35a077; background-color: rgb(241 248 244 / 0.5); } /* emerald-500 — all received */
.permit-stages-pending   { border-left: 4px solid #c4831f; } /* amber-500  — pending stages */
.permit-stages-cancelled { border-left: 4px solid #c94a3c; } /* red-500    — cancelled */
.dark .permit-stages-complete { background-color: rgb(16 59 45 / 0.1); }

/* Permit management global status indicators */
.permit-status-none       { border-left: 4px solid #97907f; } /* gray-400   — not started */
.permit-status-applied    { border-left: 4px solid #c4831f; } /* amber-500  — applied */
.permit-status-issued     { border-left: 4px solid #35a077; } /* emerald-500 — issued */
.permit-status-cancelled  { border-left: 4px solid #c94a3c; } /* red-500    — cancelled */

/* Row density tiers */
.row-L1 td { padding-top: 10px; padding-bottom: 10px; }
.row-L2 td { padding-top: 7px; padding-bottom: 7px; }
.row-L3 td { padding-top: 5px; padding-bottom: 5px; }

/* Chevron expand/collapse animation */
.chevron { transition: transform 120ms ease-out; }
.chevron-open { transform: rotate(90deg); }

/* Table row expand/collapse animation */
.row-reveal {
  animation: rowReveal 150ms ease-out forwards;
}
@keyframes rowReveal {
  from { opacity: 0; }
  to   { opacity: 1; }
}

/* Tabular numbers for aligned columns */
.tabnum { font-variant-numeric: tabular-nums; }

/* Search highlight */
mark.search-hit {
  background: #e0a352;
  color: #161511;
  padding: 0 2px;
  border-radius: 2px;
  font-weight: 500;
}
.dark mark.search-hit {
  background: #c4831f;
  color: #161511;
}

/* Summary bar gradient */
.summary-bar {
  background: linear-gradient(90deg, rgba(53,160,119,0.08) 0%, rgba(53,160,119,0.02) 100%);
}
.dark .summary-bar {
  background: linear-gradient(90deg, rgba(53,160,119,0.12) 0%, rgba(53,160,119,0.03) 100%);
}

/* Grid pattern for empty state */
.grid-pattern {
  background-image:
    linear-gradient(rgba(53,160,119,0.06) 1px, transparent 1px),
    linear-gradient(90deg, rgba(53,160,119,0.06) 1px, transparent 1px);
  background-size: 20px 20px;
}
.dark .grid-pattern {
  background-image:
    linear-gradient(rgba(53,160,119,0.08) 1px, transparent 1px),
    linear-gradient(90deg, rgba(53,160,119,0.08) 1px, transparent 1px);
}

/* Status pulse animation for in-progress indicators */
@keyframes status-pulse {
  0%, 100% { opacity: 1; }
  50% { opacity: 0.5; }
}
.status-in-progress { animation: status-pulse 2.5s ease-in-out infinite; }

/* Panel rewritten by a morph refresh (agent tool call) — two short pulses so the
   eye registers that the numbers changed. See refresh_pulse_controller.js. */
.panel-refreshing { animation: panelRefreshPulse 520ms ease-in-out 2; }
@keyframes panelRefreshPulse {
  0%, 100% { opacity: 1; }
  50%      { opacity: 0.45; }
}
@media (prefers-reduced-motion: reduce) {
  .panel-refreshing { animation: none; }
}

.contract-change-pulse { animation: contractChangePulse 700ms ease-out 2; }
@keyframes contractChangePulse {
  0%, 100% { background-color: transparent; box-shadow: none; }
  45% { background-color: rgb(16 185 129 / 0.10); box-shadow: inset 3px 0 0 rgb(16 185 129 / 0.75); }
}
@media (prefers-reduced-motion: reduce) {
  .contract-change-pulse { animation: none; }
}


/* Empty-state ghost of the breakdown table: real cost group names, dashes where
   the money goes, fading out down the card (owner request, 21/ago/2026). Mask
   instead of a gradient overlay so it works on both themes without knowing the
   panel color. Rows are aria-hidden — the card's own heading carries the state. */
.ghost-fade {
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 34%, rgba(0, 0, 0, 0.16) 100%);
  mask-image: linear-gradient(to bottom, #000 0%, #000 34%, rgba(0, 0, 0, 0.16) 100%);
}

/* Subtle row hover */
tbody tr:hover td { background-color: rgba(53, 160, 119, 0.04); }
.dark tbody tr:hover td { background-color: rgba(53, 160, 119, 0.06); }

/* Confirm dialog animations */
@keyframes confirm-backdrop-in {
  from { opacity: 0 }
  to   { opacity: 1 }
}
@keyframes confirm-card-in {
  from { opacity: 0; transform: scale(0.95) translateY(-8px) }
  to   { opacity: 1; transform: scale(1) translateY(0) }
}

/* Concept notes timeline — staggered card entrance (delay set per position
   by the list container, see _frame_content.html.erb) */
@keyframes note-card-in {
  from { opacity: 0; transform: translateY(10px) }
  to   { opacity: 1; transform: translateY(0) }
}

/* ============================================================
   Top Navbar
   ============================================================ */
/* Solid background via ::before (keeps the ::before layer the paint surface) */
.top-navbar {
  position: fixed;
  top: 0;
  z-index: 50;
  width: 100%;
  border-bottom: 1px solid transparent;
  transition: border-color 0.3s ease;
}
.top-navbar::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: var(--color-brand-navy);
}

/* Scrolled state — subtle bottom edge appears */
.top-navbar--scrolled {
  border-bottom-color: rgba(255, 255, 255, 0.08);
}

/* User dropdown — warm multi-layer shadow + entry animation */
.navbar-dropdown {
  opacity: 0;
  transform: translateY(-6px) scale(0.97);
  pointer-events: none;
  transition: opacity 180ms ease-out, transform 180ms ease-out;
  box-shadow:
    0 -1px 1px 0 rgba(173, 151, 121, 0.06),
    0 1px 3px -0.5px rgba(173, 151, 121, 0.12),
    0 3px 6px -1px rgba(60, 50, 30, 0.10),
    0 6px 12px -2px rgba(60, 50, 30, 0.12),
    0 16px 36px -8px rgba(60, 50, 30, 0.18);
}
.navbar-dropdown.open {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

/* Arrow nub — rotated square peeking above the dropdown */
.navbar-dropdown::before {
  content: "";
  position: absolute;
  top: -5px;
  right: 16px;
  width: 10px;
  height: 10px;
  background: white;
  border-radius: 1px 0 0 0;
  transform: rotate(45deg);
  z-index: 1;
  box-shadow:
    -1px -1px 2px -1px rgba(60, 50, 30, 0.08);
}

/* Arrow nub — centered variant for mid-navbar dropdowns */
.navbar-dropdown--center::before {
  right: auto;
  left: 50%;
  transform: translateX(-50%) rotate(45deg);
}

/* Arrow nub — start (left) variant for the mobile navigation menu */
.navbar-dropdown--start::before {
  right: auto;
  left: 16px;
}

/* Dropdown item hover */
.navbar-dropdown-item {
  transition: background-color 120ms ease;
}
.navbar-dropdown-item:hover {
  background-color: rgba(173, 151, 121, 0.08);
}

/* ---------------------------------------------------------------------------
   CHAT DRAWER — the direct-messages panel floating over the right edge
   (layouts/application.html.erb, data-turbo-permanent). One column: the list OR
   one thread. Slides in from the right; state is the `.open` class the
   chat-drawer controller toggles on turbo:frame-load / Hide / Escape.
   z-index 45: under the navbar and its dropdowns (50), over the page itself.
   Dark is the app's `.dark` CLASS (design-system-patterns/09), never
   prefers-color-scheme. */
/* --page-vw is "how wide the page may be" — 100vw normally, minus the drawer's
   lane while it is open. Everything that used to measure the WINDOW to reach the
   page's edges (.process-strip's 50vw math, the stepper's gutter arrows) reads
   this instead, so narrowing the content column moves those edges with it.
   --chat-drawer-space is the lane itself: the drawer's width plus its two gaps. */
:root {
  --page-vw: 100vw;
  --chat-drawer-space: calc(min(420px, 100vw - 1.5rem) + 1.5rem);

  /* --page-stage is "how wide the CONTENT column may be" — the width stage the
     page asked PageLayoutComponent for (1400 / 1800 / 2100 / full), set on the
     shell by one of the .page-stage-* classes below. This is the fallback for
     anything rendered outside a shell.
     ⚠️ THE GUTTER IS `--page-vw` MINUS THE STAGE, NEVER MINUS 1400 (owner,
     04/set/2026). The stepper's side arrows hardcoded 1400, so on a `:wide`
     (1800) page they claimed a lane 200px wider than the real gutter on each
     side and stood on the content. */
  --page-stage: 1400px;

}
/* The content shrinks only where there is room to shrink into. Below 1280px the
   drawer floats over the page as before and Hide is the way back to the work. */
@media (min-width: 1280px) {
  html.chat-drawer-open { --page-vw: calc(100vw - var(--chat-drawer-space)); }
  html.chat-drawer-open .chat-drawer-inset { padding-right: var(--chat-drawer-space); }
  html.chat-drawer-open .page-gutter-right { right: var(--chat-drawer-space); }
  /* Page-level controls anchored to the window's right edge (the sketchpad's
     Pencil and Undo). The inset narrows the FLOW; anything position:fixed still
     measures the window and would sit under the drawer, so it steps aside too. */
  html.chat-drawer-open .drawer-aware-right { margin-right: var(--chat-drawer-space); }
}
.drawer-aware-right { transition: margin-right 220ms cubic-bezier(0.2, 0.8, 0.2, 1); }

/* The width stage the page asked Process::PageLayoutComponent for, as a class on
   the shell (STAGE_CLASSES). A class and not `style="--page-stage: …"` because
   the CSP has no `unsafe-inline` for style-src and a nonce cannot cover a style
   attribute — the inline form is silently dropped and the gutter measures the
   fallback instead of the page. `:full` reports the whole page, so its gutter
   computes to zero: a full-bleed page has none. */
.page-stage-default { --page-stage: 1400px; }
.page-stage-wide { --page-stage: 1800px; }
.page-stage-extra-wide { --page-stage: 2100px; }
.page-stage-full { --page-stage: var(--page-vw); }

/* THE STEP GUTTER — the lane the stepper's previous/next chevrons live in
   (Process::StepperNavigationComponent), one on each side of the content column.
   ⚠️ IT HAS NO BREAKPOINT, AND THAT IS THE POINT (owner, 04/set/2026: "não há
   navegação horizontal na tela do laptop"). The arrows were `hidden 2xl:flex`,
   and Tailwind's 2xl is 1536px while a 16" MacBook Pro reports 1512 — 24px short,
   so on the owner's own screen they never existed. A breakpoint was the wrong
   instrument anyway: what decides whether an arrow fits is not the window, it is
   the GUTTER, and the gutter is the window minus the page's width stage. So the
   lane is that subtraction (`max(0px, …)` — a page wider than the window has no
   gutter, not a negative one) and the lane asks ITSELF whether its content fits,
   through a container query.
   The consequence, at the 1400 stage: 0px at 1280 (nothing, as before), 44px at
   1488 and 56px at 1512 (chevron, no label — the laptop this was reported from),
   164px at 1728 (chevron + label). On an 1800 stage
   page the same formula yields 0 until the window really is wider than 1800,
   which is the invasion bug the hardcoded 1400 used to cause.
   ⚠️ The lane is `pointer-events: none` and the link is `display: none` until the
   query passes — a 20px lane that is invisible but clickable is a strip at the
   edge of the window that silently navigates. */
.step-gutter {
  position: fixed;
  inset-block: 0;
  z-index: 30;
  width: max(0px, calc((var(--page-vw) - var(--page-stage)) / 2));
  container-type: inline-size;
  pointer-events: none;
}
.step-gutter-left { left: 0; }
.step-gutter-right { right: 0; }
/* ⚠️ HIDING IS DONE HERE, NEVER WITH TAILWIND'S `hidden`. tailwind.css is linked
   AFTER application.css precisely so utilities win the cascade
   (ApplicationHelper#inline_stylesheet says so), and `.hidden`'s `display: none`
   would beat this file's `display: block` at equal specificity — the label would
   never come back. Both halves of the toggle live in this file. */
.step-gutter__link { display: none; }
.step-gutter__label { display: none; }
/* 40px: the chevron lockup is a 20px glyph in px-2 — 36px of ink, plus air. The
   floor is deliberately low: the first screen this had to reach is a 16" laptop,
   whose gutter against the 1400 stage is only ~40-56px, and a threshold
   calibrated for comfort would have reproduced the bug it was written to fix. */
@container (min-width: 40px) {
  .step-gutter__link { display: flex; pointer-events: auto; }
}
/* 108px: the destination's name at 11px plus the same px-2. Below it the label is
   ABSENT, never clipped — a half-word under an arrow reads as breakage. */
@container (min-width: 108px) {
  .step-gutter__label { display: block; }
}
/* The inset is padding on the SHELL, never on #app or a transform: the shell
   paints the canvas, so the beige keeps running under the drawer instead of
   leaving a strip of #app's own grey around it — and a transform on an ancestor
   would re-anchor every position:fixed descendant in the app. */
.chat-drawer-inset { transition: padding-right 220ms cubic-bezier(0.2, 0.8, 0.2, 1); }

.chat-drawer {
  --chat-drawer-w: min(420px, calc(100vw - 1.5rem));
  position: fixed;
  top: calc(3.5rem + 0.75rem);
  bottom: 0.75rem;
  right: 0.75rem;
  width: var(--chat-drawer-w);
  z-index: 45;
  display: flex;
  flex-direction: column;
  overflow: hidden;
  border-radius: 1rem;
  border: 1px solid var(--color-steel-200);
  background: var(--color-steel-50);
  box-shadow:
    0 -1px 1px 0 rgba(173, 151, 121, 0.06),
    0 1px 3px -0.5px rgba(173, 151, 121, 0.12),
    0 3px 6px -1px rgba(60, 50, 30, 0.10),
    0 6px 12px -2px rgba(60, 50, 30, 0.12),
    0 16px 36px -8px rgba(60, 50, 30, 0.18);
  transform: translateX(calc(100% + 1rem));
  opacity: 0;
  pointer-events: none;
  transition: transform 220ms cubic-bezier(0.2, 0.8, 0.2, 1), opacity 160ms ease-out;
}
.chat-drawer.open { transform: none; opacity: 1; pointer-events: auto; }
.dark .chat-drawer {
  background: var(--color-steel-900);
  border-color: var(--color-steel-800);
  box-shadow: 0 6px 12px -2px rgba(0, 0, 0, 0.5), 0 16px 36px -8px rgba(0, 0, 0, 0.7);
}
/* The frame is the column; its content (list or thread) fills it. */
.chat-drawer turbo-frame { display: flex; flex-direction: column; flex: 1; min-height: 0; }
.chat-drawer turbo-frame[busy] { opacity: 0.6; }
.chat-drawer turbo-frame { transition: opacity 120ms ease-out; }
/* The toast lane steps aside while the drawer is open — toasts are the one thing
   that would otherwise be covered by it. */
html.chat-drawer-open #toast-container { right: var(--chat-drawer-space); }
#toast-container { transition: right 220ms cubic-bezier(0.2, 0.8, 0.2, 1); }
/* Thread bubbles. Three grounds, not two: the thread floor is recessed
   (steel-100), an incoming line is a white card lifted off it, and MY line is
   tinted navy. Before this, both sides wore the tracker's comment card — a hair
   off the drawer's own ground — and a conversation read as one column of
   identical boxes (owner, 03/set). Side alone is not enough contrast. */
.chat-thread { background: var(--color-steel-100); }
.dark .chat-thread { background: var(--color-steel-950); }
.chat-msg {
  max-width: 78%;
  padding: 7px 11px;
  border-radius: 0.5rem;
  border: 1px solid var(--color-steel-200);
  background: #ffffff;
}
.chat-msg--mine {
  background: var(--color-blueprint-50);
  border-color: var(--color-blueprint-200);
}
.chat-msg__meta {
  margin: 0;
  font-family: var(--font-mono);
  font-size: 10.5px;
  font-weight: 700;
  color: var(--color-steel-500);
}
.chat-msg__meta b { color: var(--color-steel-900); }
.chat-msg--mine .chat-msg__meta { color: var(--color-blueprint-600); }
.chat-msg--mine .chat-msg__meta b { color: var(--color-blueprint-800); }
.chat-msg__body {
  margin: 3px 0 0;
  font-size: 13px;
  line-height: 1.45;
  color: var(--color-steel-900);
  white-space: pre-line;
  overflow-wrap: anywhere;
}
.dark .chat-msg { background: var(--color-steel-800); border-color: var(--color-steel-700); }
.dark .chat-msg--mine { background: var(--color-blueprint-900); border-color: var(--color-blueprint-700); }
.dark .chat-msg__meta { color: var(--color-steel-400); }
.dark .chat-msg__meta b { color: var(--color-steel-100); }
.dark .chat-msg--mine .chat-msg__meta { color: var(--color-blueprint-300); }
.dark .chat-msg--mine .chat-msg__meta b { color: var(--color-blueprint-100); }
.dark .chat-msg__body { color: var(--color-steel-100); }

/* THE PRIORITY BANDS of an assistant answer (qa_answer_html). A band is a bold
   line that opens a list, and only a label the prompt fixed gets painted — an
   off-script answer keeps the bold and loses the colour instead of claiming a
   state it did not earn. The three tones are the app's own state colours
   (.badge-red / -amber / -idle), foreground only: a filled stamp here would
   compete with the badges the answer is talking about. */
.qa-band {
  font-size: 11.5px;
  font-weight: 600;
  letter-spacing: .03em;
}
.qa-band--now  { color: #b3362b; }
.qa-band--wait { color: #a8650f; }
.qa-band--cold { color: #6b665a; }
.dark .qa-band--now  { color: #e0705f; }
.dark .qa-band--wait { color: #e0a352; }
.dark .qa-band--cold { color: #a8a294; }

/* The drawer bubble has no `space-y` wrapper the way the three embedded chats
   do, so paragraphs, bands and lists would run together into one slab. */
.chat-msg__body > * + * { margin-top: 7px; }

/* Conversation rows (popover + drawer list). Hover is a soft fill — the owner
   vetoed the hub-card inset bar here (03/set). */
.chat-row { transition: background-color 120ms ease, box-shadow 120ms ease; }
.chat-row:hover { background-color: rgba(173, 151, 121, 0.10); }
.chat-row.chat-row--unread:hover { background-color: #ffffff; box-shadow: 0 0 0 1px var(--color-steel-300); }
.dark .chat-row.chat-row--unread:hover { background-color: var(--color-steel-800); box-shadow: 0 0 0 1px var(--color-steel-700); }
@media (prefers-reduced-motion: reduce) {
  .chat-drawer, .chat-drawer turbo-frame, .chat-drawer-inset, #toast-container { transition: none; }
}

/* Recipient picker rows (conversations/new). One highlight for mouse AND keyboard:
   the Stimulus controller sets .is-active on hover and on ↑/↓, so the two never
   disagree about which row Enter will pick. */
.people-picker-row { transition: background-color 100ms ease; }
.people-picker-row.is-active { background-color: rgba(173, 151, 121, 0.12); }
.dark .people-picker-row.is-active { background-color: rgba(255, 255, 255, 0.06); }

/* Hub dropdown rows — inset left-accent on hover (the hub itself has no hover) */
.hub-card {
  transition: background-color 150ms ease, box-shadow 200ms ease;
}
.hub-card:hover {
  box-shadow: inset 2px 0 0 0 var(--color-blueprint-500);
}

/* Dialog modal system — transparent wrapper for stacking modals via <dialog> */
dialog.modal-dialog {
  margin: 0;
  padding: 0;
  border: none;
  background: transparent;
  width: 100vw;
  height: 100vh;
  max-width: none;
  max-height: none;
  overflow: visible;
}
dialog.modal-dialog::backdrop {
  background: transparent;
}

/* ============================================================
   Global scrollbar — subtle, Basecamp-inspired
   ============================================================ */

/* Firefox / standard */
* {
  scrollbar-width: thin;
  scrollbar-color: rgba(151, 144, 127, 0.45) transparent;  /* slate-400 @ 45% */
}
.dark * {
  scrollbar-color: rgba(82, 78, 68, 0.5) transparent;     /* slate-600 @ 50% */
}

/* WebKit (Chrome, Edge, Safari) */
::-webkit-scrollbar {
  width: 8px;
  height: 8px;
}
::-webkit-scrollbar-track {
  background: transparent;
}
::-webkit-scrollbar-thumb {
  background-color: rgba(151, 144, 127, 0.35);
  border-radius: 9999px;
  border: 2px solid transparent;
  background-clip: content-box;
}
::-webkit-scrollbar-thumb:hover {
  background-color: rgba(151, 144, 127, 0.6);
}
::-webkit-scrollbar-corner {
  background: transparent;
}

/* Dark mode — WebKit */
.dark ::-webkit-scrollbar-thumb {
  background-color: rgba(82, 78, 68, 0.4);
}
.dark ::-webkit-scrollbar-thumb:hover {
  background-color: rgba(82, 78, 68, 0.65);
}

/* Utility: extra-thin scrollbar (for small containers, modals) */
.scrollbar-thin {
  scrollbar-width: thin;
  scrollbar-color: #97907f transparent;
}
.scrollbar-thin::-webkit-scrollbar {
  width: 5px;
  height: 5px;
}
.scrollbar-thin::-webkit-scrollbar-track {
  background: transparent;
}
.scrollbar-thin::-webkit-scrollbar-thumb {
  background-color: rgba(151, 144, 127, 0.4);
  border-radius: 9999px;
  border: 1px solid transparent;
  background-clip: content-box;
}
.dark .scrollbar-thin {
  scrollbar-color: #524e44 transparent;
}
.dark .scrollbar-thin::-webkit-scrollbar-thumb {
  background-color: rgba(82, 78, 68, 0.45);
}

/* Note item expand/collapse chevron */
details[open] > summary .chevron-icon {
  transform: rotate(90deg);
}

.action-menu {
  opacity: 0;
  transform: translateY(-4px) scale(0.97);
  pointer-events: none;
  transition: opacity 100ms ease-out, transform 100ms ease-out;
}
.action-menu.open {
  opacity: 1;
  transform: translateY(0) scale(1);
  pointer-events: auto;
}

.table-row-removing {
  opacity: 0.5;
  transition: opacity 0.3s ease;
}

.table-row-entering {
  animation: tableRowEnter 300ms ease-out forwards;
}

.table-row-collapsing {
  animation: tableRowCollapse 200ms ease-in forwards;
}

@keyframes tableRowEnter {
  from {
    opacity: 0;
    transform: translateY(-10px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

@keyframes tableRowCollapse {
  from {
    opacity: 1;
    transform: translateY(0);
  }
  to {
    opacity: 0;
    transform: translateY(-5px);
  }
}

/* ============================================================
   Hub Directory — the /home process list (two clicks to enter)
   ============================================================ */

/* NO HOVER HERE, on purpose: the pointer resting on a row says nothing, because
   the FIRST CLICK is what previews the process in the panel. The 3px accent bar
   that already marked the selected row is the same piece that grows to fill the
   row once it is armed — the gesture is built out of an element the page had.
   (The navbar dropdown keeps its hover: a menu has no panel to preview into.) */
/* The panel's mesh is NOT .blueprint-grid: that one lays a 16px fine grid under
   the 80px one and fades at the bottom. Here it is a single 80px grid that runs
   under the whole panel and fades out on a slight diagonal as it passes under
   the brief — the texture sits behind the title and is gone before the flags,
   so no line of text ever sits on a grid line. */
.hub-panel::before {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  pointer-events: none;
  background-image:
    linear-gradient(to right, rgba(107, 102, 90, 0.14) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(107, 102, 90, 0.14) 1px, transparent 1px);
  background-size: 80px 80px;
  mask-image: linear-gradient(105deg, black 0%, black 28%, rgba(0, 0, 0, 0.55) 44%, rgba(0, 0, 0, 0.12) 60%, transparent 74%);
  -webkit-mask-image: linear-gradient(105deg, black 0%, black 28%, rgba(0, 0, 0, 0.55) 44%, rgba(0, 0, 0, 0.12) 60%, transparent 74%);
}
.dark .hub-panel::before {
  background-image:
    linear-gradient(to right, rgba(107, 102, 90, 0.2) 1px, transparent 1px),
    linear-gradient(to bottom, rgba(107, 102, 90, 0.2) 1px, transparent 1px);
}

.hub-row {
  position: relative;
  overflow: hidden;
  cursor: pointer;
  background: var(--color-steel-100);
  transition: background-color 150ms ease;
}
.dark .hub-row {
  background: var(--color-steel-850);
}
.hub-row[aria-disabled="true"] {
  cursor: default;
}
.hub-row:focus-visible {
  outline: 2px solid var(--color-blueprint-600);
  outline-offset: -3px;
}
.hub-row[data-selected="true"] {
  background: var(--color-steel-50);
}
.dark .hub-row[data-selected="true"] {
  background: var(--color-steel-900);
}

.hub-row__face {
  position: absolute;
  inset: 0;
  display: grid;
  grid-template-columns: 26px 1fr auto;
  align-items: center;
  gap: 12px;
  padding: 0 20px;
  transition: opacity 110ms ease;
}
.hub-row[data-selected="true"] .hub-row__face {
  opacity: 0;
}
.hub-row__index {
  color: var(--color-steel-400);
}
.hub-row[data-selected="true"] .hub-row__index {
  color: var(--color-blueprint-600);
}
.dark .hub-row[data-selected="true"] .hub-row__index {
  color: var(--color-blueprint-400);
}

.hub-row__accent {
  position: absolute;
  left: 0;
  top: 0;
  bottom: 0;
  width: 3px;
  background: var(--color-blueprint-600);
  opacity: 0;
  transition: opacity 110ms ease;
}
/* blueprint-500, not -400: this bar grows into a full band with light text on
   top, and -400 is bright enough to wash that text out. The 3px selected state
   and the armed band are the same element, so the darker value serves both. */
.dark .hub-row__accent {
  background: var(--color-blueprint-500);
}
.hub-row[data-selected="true"] .hub-row__accent {
  width: 100%;
  opacity: 1;
}

.hub-row__confirm {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 14px;
  padding: 0 20px;
  opacity: 0;
  pointer-events: none;
  transition: opacity 110ms ease;
}
.hub-row[data-selected="true"] .hub-row__confirm {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .hub-row__face,
  .hub-row__confirm,
  .hub-row__accent {
    transition: none;
  }
}

/* Avatar rings pick up whatever surface they sit on — row, selected row, panel. */
.hub-avatar {
  border-color: var(--color-steel-100);
}
.dark .hub-avatar {
  border-color: var(--color-steel-850);
}
.hub-row[data-selected="true"] .hub-avatar,
.hub-panel .hub-avatar {
  border-color: var(--color-steel-50);
}
.dark .hub-row[data-selected="true"] .hub-avatar,
.dark .hub-panel .hub-avatar {
  border-color: var(--color-steel-900);
}

/* Two pips in the panel: how many of the two clicks are spent. */
.hub-pip {
  display: block;
  width: 7px;
  height: 7px;
  border: 1px solid var(--color-blueprint-600);
  border-radius: 50%;
}
.dark .hub-pip {
  border-color: var(--color-blueprint-400);
}
.hub-pip.is-on {
  background: var(--color-blueprint-600);
}
.dark .hub-pip.is-on {
  background: var(--color-blueprint-400);
}

/* The brief — "needs attention" for the selected process. No card, no stripe:
   each flag is ONE statement in the page's own sans, the severity shows only in
   the mono label and in the one figure the sentence turns on. The flag's detail
   is stored but not drawn — the owner cut it as noise (22/ago); the assistant
   reads it per turn. Mockup: docs/mockups/hub-agentic.html (variant C1a). */
.hub-brief {
  display: flex;
  flex-direction: column;
}
.hub-brief__head {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  margin-bottom: 22px;
}
.hub-brief__empty {
  font-size: 14.5px;
  color: var(--color-steel-500);
}
.dark .hub-brief__empty {
  color: var(--color-steel-400);
}
.hub-brief__flags {
  display: flex;
  flex-direction: column;
  gap: 34px;
  max-width: 36em;
}
.hub-brief__flag {
  --sev: var(--color-blueprint-500);
  display: block;
  color: inherit;
  text-decoration: none;
}
.hub-brief__flag[data-severity="critical"] { --sev: #b4432f; }
.hub-brief__flag[data-severity="watch"]    { --sev: #b8811c; }
.dark .hub-brief__flag                      { --sev: var(--color-blueprint-300); }
.dark .hub-brief__flag[data-severity="critical"] { --sev: #e8856f; }
.dark .hub-brief__flag[data-severity="watch"]    { --sev: #dcab52; }
.hub-brief__flag h4 {
  margin: 0;
  font-size: 25px;
  line-height: 1.22;
  font-weight: 500;
  letter-spacing: -0.02em;
  color: var(--color-steel-900);
  text-wrap: balance;
}
.dark .hub-brief__flag h4 {
  color: var(--color-steel-100);
}
.hub-brief__flag h4 em {
  font-style: normal;
  font-weight: 600;
  color: var(--sev);
  font-variant-numeric: tabular-nums;
}
.hub-brief__flag:hover h4 {
  text-decoration: underline;
  text-decoration-color: var(--sev);
  text-decoration-thickness: 1px;
  text-underline-offset: 5px;
}
.hub-brief__flag:focus-visible {
  outline: 2px solid var(--sev);
  outline-offset: 6px;
}

/* THE ASSISTANT IN THE HUB PANEL — concept C: the brief column becomes the
   conversation (docs/95). At rest it is one line, the composer; the moment
   somebody sends, .is-talking moves on the COLUMN, the briefs are hidden and the
   thread takes the height they were using.

   ⚠️ The thread grows by taking the column's spare height (flex: 1 1 auto), not
   by a max-height in percent: this column is pinned to the viewport at lg and a
   percentage of a flex-basis:auto parent resolves against nothing. */
/* ⚠️ THE TWO SIDES TRADE HEIGHT, AND THAT IS WHY IT IS A GRID. At rest the
   briefs take what they need and the assistant is one line; talking, the ratio
   flips. `grid-template-rows` is the only property that animates between a
   measured height and one nobody measured — a flex column would need JS holding
   a pixel number, and that number is wrong the moment the brief is rewritten by
   a broadcast. Both children clip and carry min-height: 0, or the 0fr side
   refuses to collapse. */
.hub-column {
  display: grid;
  grid-template-rows: 1fr auto;
  gap: 16px;
  transition: grid-template-rows 320ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
.hub-column.is-talking { grid-template-rows: 0fr 1fr; }
.hub-column__briefs {
  min-height: 0;
  overflow-y: auto;
  opacity: 1;
  transform: none;
  transition: opacity 160ms ease 60ms, transform 300ms cubic-bezier(0.2, 0.8, 0.2, 1);
}
/* The brief does not just vanish — it recedes upward while it goes, so the eye
   follows the swap instead of finding a different screen. */
.hub-column.is-talking .hub-column__briefs {
  overflow: hidden;
  opacity: 0;
  transform: translateY(-10px);
  transition-delay: 0ms;
}

.hub-assistant {
  display: flex;
  flex-direction: column;
  gap: 8px;
  min-height: 0;
}

.hub-assistant__thread {
  min-height: 0;
  overflow-y: auto;
  flex: 1 1 auto;
  /* Closed it occupies nothing at all — not a collapsed box, not a hairline. */
  max-height: 0;
  opacity: 0;
  transition: opacity 200ms ease;
}
.is-talking .hub-assistant__thread {
  max-height: none;
  opacity: 1;
  transition-delay: 120ms;
  /* The seam. The bubbles dissolve into the panel at the top instead of meeting
     a border — the drafting mesh keeps running underneath either way. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 26px);
  mask-image: linear-gradient(to bottom, transparent 0, #000 26px);
}
/* No pinned height below lg — the page scrolls there, so the thread needs a
   ceiling of its own or it would push the composer off the bottom. */
@media (max-width: 1023px) {
  .is-talking .hub-assistant__thread { max-height: 60vh; }
}

.hub-assistant__empty {
  margin: auto 0 0;
  padding: 10px 2px;
  font-size: 13px;
  color: var(--color-steel-400);
}
.dark .hub-assistant__empty { color: var(--color-steel-500); }

/* The recap slides in with the swap rather than appearing on top of it. */
.hub-assistant__recap[hidden] { display: none; }
.hub-assistant__recap {
  animation: hubRecapIn 260ms cubic-bezier(0.2, 0.8, 0.2, 1) both;
}
@keyframes hubRecapIn {
  from { opacity: 0; transform: translateY(-6px); }
  to   { opacity: 1; transform: none; }
}

/* The way back, and the only thing that says what was put away. A rule, not a
   card edge: it is the same hairline the panel already divides with. */
.hub-assistant__recap {
  display: flex;
  align-items: center;
  gap: 6px;
  flex: none;
  padding: 0 0 9px;
  border-bottom: 1px solid var(--color-steel-200);
  background: none;
  border-top: 0;
  border-left: 0;
  border-right: 0;
  cursor: pointer;
  font-family: var(--font-mono, ui-monospace, monospace);
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--color-steel-500);
  transition: color 150ms ease;
}
.hub-assistant__recap:hover { color: var(--color-steel-900); }
.hub-assistant__recap:focus-visible {
  outline: 2px solid var(--color-blueprint-600);
  outline-offset: 3px;
}
.hub-assistant__count { color: var(--color-steel-400); letter-spacing: 0.1em; }
.dark .hub-assistant__recap { border-bottom-color: var(--color-steel-700); color: var(--color-steel-400); }
.dark .hub-assistant__recap:hover { color: var(--color-steel-100); }

/* ⚠️ THE TRACKER SKIN DOES NOT REACH HERE. Every .trk-* rule is scoped under
   .process-tracker, which the drawer wears and this panel does not — and adding
   the class would be worse than useless, because it paints a background and a
   radius, which is the card edge this concept exists to avoid. So the shared
   composer gets dressed in the panel's own tokens instead. */
.hub-assistant .trk-composer { background: none; border: 0; }
.hub-assistant .trk-composer form { padding: 0; gap: 10px; }
.hub-assistant .trk-input {
  width: 100%;
  padding: 9px 11px;
  font-size: 13px;
  border: 1px solid var(--color-steel-200);
  background: var(--color-steel-50);
  color: var(--color-steel-900);
}
.hub-assistant .trk-input::placeholder { color: var(--color-steel-400); }
.hub-assistant .trk-input:focus {
  outline: 2px solid var(--color-blueprint-600);
  outline-offset: 1px;
  border-color: var(--color-blueprint-600);
}
.hub-assistant .trk-act {
  display: inline-flex;
  align-items: center;
  padding: 9px 16px;
  font-size: 13px;
  font-weight: 650;
  border: 1px solid var(--color-brand-navy);
  background: var(--color-brand-navy);
  color: var(--color-steel-50);
  cursor: pointer;
  transition: background-color 150ms ease;
}
.hub-assistant .trk-act:hover { background: var(--color-blueprint-800); }
.hub-assistant .trk-act:focus-visible { outline: 2px solid var(--color-blueprint-600); outline-offset: 2px; }
.dark .hub-assistant .trk-input { background: var(--color-steel-900); border-color: var(--color-steel-700); color: var(--color-steel-100); }
.dark .hub-assistant .trk-act { background: var(--color-steel-100); border-color: var(--color-steel-100); color: var(--color-steel-900); }

@media (prefers-reduced-motion: reduce) {
  .hub-column, .hub-column__briefs, .hub-assistant__thread, .hub-assistant__recap { transition: none; }
  .hub-assistant__recap { animation: none; }
}

/* OPENERS above the composer, in both windows. They are sentences, not labels,
   so they are set in the page's own sans at a small size — a mono uppercase pill
   would read as a filter, which is the one thing they are not.

   They WRAP rather than scroll: the drawer is 420px and four openers do not fit
   on a line there, and a horizontal scroller inside a narrow panel is a worse
   answer than a second row. */
.agent-chips { display: contents; }
.agent-chips__set {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  /* Room above so the openers do not sit on the last bubble, and room below so
     they do not sit on the field. */
  padding: 12px 0 10px;
}
/* ⚠️ The openers live OUTSIDE the form, so they do not inherit its padding. In
   the drawer the form is p-3 and they would hang 12px to its left; the hub box
   strips that padding, so there they are already flush. */
.chat-drawer .agent-chips__set { padding-left: 12px; padding-right: 12px; }
.agent-chips__set[hidden] { display: none; }

.agent-chip {
  padding: 5px 11px;
  font-size: 12px;
  line-height: 1.3;
  text-align: left;
  color: var(--color-steel-500);
  background: transparent;
  border: 1px solid var(--color-steel-200);
  border-radius: 999px;
  cursor: pointer;
  transition: color 140ms ease, border-color 140ms ease, background-color 140ms ease;
}
.agent-chip:hover {
  color: var(--color-steel-900);
  border-color: var(--color-steel-400);
  background: var(--color-steel-50);
}
.agent-chip:focus-visible {
  outline: 2px solid var(--color-blueprint-600);
  outline-offset: 2px;
}
.dark .agent-chip { color: var(--color-steel-400); border-color: var(--color-steel-700); }
.dark .agent-chip:hover {
  color: var(--color-steel-100);
  border-color: var(--color-steel-500);
  background: var(--color-steel-800);
}

@media (prefers-reduced-motion: reduce) {
  .agent-chip { transition: none; }
}

@keyframes cardIn {
  from {
    opacity: 0;
    transform: translateY(12px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.card-enter {
  animation: cardIn 0.4s cubic-bezier(0.16, 1, 0.3, 1) both;
}

.card-enter[data-card-seq="1"] { animation-delay: 70ms; }
.card-enter[data-card-seq="2"] { animation-delay: 140ms; }
.card-enter[data-card-seq="3"] { animation-delay: 210ms; }
.card-enter[data-card-seq="4"] { animation-delay: 280ms; }
.card-enter[data-card-seq="5"] { animation-delay: 350ms; }
.card-enter[data-card-seq="6"] { animation-delay: 420ms; }
.card-enter[data-card-seq="7"] { animation-delay: 490ms; }

.budget-bar-fill {
  background-size: 200% 100%;
  animation: shimmer 2s ease-in-out infinite;
}

@keyframes shimmer {
  0%, 100% { background-position: 0% 0; }
  50% { background-position: 100% 0; }
}

/* Kanban cards */
.kanban-card {
  border: 1px solid #524e44;
  margin: 0.75rem 0;
  background-color: #fefefe;
  box-shadow: 0 2px 4px 0 rgba(0, 0, 0, 0.1);
  transition: all 0.2s ease-in-out;
}

/* Hover é interação, logo acompanha a navy da ação (07/ago). Os três #35a077
   acima — completed / all received / issued — são ESTADO e seguem verdes. */
.kanban-card:hover {
  border-color: #3a4d75;
  box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.12);
  transform: translateY(-1px);
}

.kanban-card .text-gray-900 {
  color: #3d3931 !important;
  font-weight: 600;
}

.kanban-card .text-gray-600 {
  color: #3d3931 !important;
}

.kanban-card .border-gray-200 {
  border-color: #e6e1d5 !important;
}

.kanban-card-content {
  padding: 1rem;
}

.kanban-card-content-compact {
  padding: 0.75rem;
}

/* Workflow card avatars use stylesheet classes instead of inline styles so
   they remain compatible with the app CSP (`style-src 'self' https:`).
   CATEGÓRICO, NÃO SEMÂNTICO (07/ago): estas oito cores existem para separar
   PESSOAS, então não colapsam nas quatro cores de estado — são oito matizes do
   arco quente (terracota, argila, areia, ouro-oliva, oliva, sálvia, pedra,
   taupe), todos em luminosidade parecida para a inicial branca ler igual em
   todos. Metade delas era ardósia fria e virou. Espelhadas em
   `app/helpers/home_helper.rb::AVATAR_COLORS` — as duas listas têm de andar
   juntas. */
.avatar-color-0 { background-color: #8A5F52; }
.avatar-color-1 { background-color: #8B7D6B; }
.avatar-color-2 { background-color: #8A6A4F; }
.avatar-color-3 { background-color: #6E7A5A; }
.avatar-color-4 { background-color: #7F7550; }
.avatar-color-5 { background-color: #9A8B7A; }
.avatar-color-6 { background-color: #5F7A6B; }
.avatar-color-7 { background-color: #87796B; }

/* Keep a CSP-safe navigation indicator instead of Turbo's inline-style progress bar. */
html.turbo-progress-visible::before {
  content: "";
  position: fixed;
  inset: 0 auto auto 0;
  width: 100%;
  height: 3px;
  z-index: 2147483647;
  pointer-events: none;
  background:
    linear-gradient(90deg, rgba(53, 160, 119, 0) 0%, rgba(53, 160, 119, 0.95) 50%, rgba(53, 160, 119, 0) 100%);
  background-size: 35% 100%;
  background-repeat: no-repeat;
  animation: turboProgressSlide 1s linear infinite;
}

@keyframes turboProgressSlide {
  from { background-position: -35% 0; }
  to   { background-position: 135% 0; }
}

/* Document Register — accordion sections */
.section-chevron { transition: transform 150ms ease; }
.section-open .section-chevron { transform: rotate(90deg); }
.section-body { display: none; }
.section-open .section-body { display: block; }
.section-accent-active { border-left: 3px solid #2d3d5f; } /* marca onde você está = navegação, logo navy */
.section-accent-empty { border-left: 3px solid #e6e1d5; }

@media (prefers-color-scheme: dark) {
  .section-accent-empty { border-left-color: #3d3931; }
}

/* ── Spec Sheet ── */
/* Sticky command bar: bg is set via Tailwind (bg-steel-100 dark:bg-steel-900) so
   it follows the app's class-based dark toggle. This only adds the separating
   shadow. The app uses class-based dark mode (<html class="dark">), NOT
   prefers-color-scheme — so all dark variants below use the `.dark` selector. */
.spec-command-bar {
  box-shadow: 0 1px 0 rgb(230 225 213), 0 6px 12px -10px rgba(22, 21, 17, 0.25);
}
.dark .spec-command-bar {
  box-shadow: 0 1px 0 rgb(61 57 49), 0 6px 12px -10px rgba(0, 0, 0, 0.5);
}

/* ── One component's row ─────────────────────────────────────────────────────
   The sheet renders 194 of these. Same reasoning as `.spec-field` further down:
   the class strings were the payload. Nothing in process_tracker.css overrides
   these hooks, so the tokens named here are exactly what the panel already
   showed. */
.spec-row {
  border-bottom: 1px solid var(--color-steel-100);
  transition: background-color 150ms, border-color 150ms;
}
.spec-row:last-child { border-bottom-width: 0; }
.dark .spec-row { border-bottom-color: var(--color-steel-800); }

.spec-row-grid { display: grid; grid-template-columns: 1fr; }
@media (width >= 40rem) {
  .spec-row-grid { grid-template-columns: 280px 1fr; }
  .spec-row-subject { border-right: 1px solid var(--color-steel-100); }
  .dark .spec-row-subject { border-right-color: var(--color-steel-800); }
}

.spec-row-subject {
  display: flex;
  flex-direction: column;
  justify-content: flex-start;
  gap: 0.25rem;
  padding: 0.75rem 1rem;
}
.spec-row-head { display: flex; align-items: center; gap: 0.5rem; }
.spec-row-name { font-size: 13px; font-weight: 500; color: var(--color-steel-800); }
.dark .spec-row-name { color: var(--color-steel-100); }

.spec-row-code {
  font-family: var(--font-mono);
  font-size: 10px;
  letter-spacing: 0.04em;
  color: var(--color-steel-400);
}
.spec-row-code--none {
  letter-spacing: normal;
  font-style: italic;
  color: var(--color-steel-300);
}

.spec-row-add {
  margin-top: 0.375rem;
  align-self: flex-start;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  font-family: var(--font-mono);
  font-size: 10px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--color-steel-400);
  transition: color 150ms;
}
.spec-row-add svg { width: 0.75rem; height: 0.75rem; }
.spec-row-add:hover { color: var(--color-blueprint-600); }
.dark .spec-row-add:hover { color: var(--color-blueprint-400); }

.spec-row-lines { min-width: 0; }
.spec-row-lines > :not(:last-child) { border-bottom: 1px solid var(--color-steel-100); }
.dark .spec-row-lines > :not(:last-child) { border-bottom-color: var(--color-steel-800); }

/* Filled row: tan left accent + a soft tan wash fading to the right. */
.spec-row { position: relative; }
.spec-row.is-filled {
  background-image: linear-gradient(to right, rgba(173, 151, 121, 0.09), transparent 38%);
}
.spec-row.is-filled::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 2px;
  background: var(--color-brand-tan);
}

/* Product Approval row: blueprint left accent. Declared after the filled rule so it
   overrides the tan ::before bar; the filled tan wash (on the element) stays, so a
   PA+filled row still reads as filled. */
.spec-row.is-pa::before {
  content: "";
  position: absolute;
  left: 0; top: 0; bottom: 0;
  width: 3px;
  background: var(--color-blueprint-500);
}

/* Collapsible category */
.spec-cat.is-collapsed .spec-cat-rows { display: none; }
.spec-cat.is-collapsed .spec-cat-chev { transform: rotate(-90deg); }

/* Completed category: swap fraction for the "Complete" pill, bar turns emerald. */
.spec-cat-done { display: none; }
.spec-cat.is-done .spec-cat-done { display: inline-flex; }
.spec-cat.is-done .spec-cat-frac { display: none; }
.spec-cat.is-done .spec-cat-bar { background-color: rgb(44 138 103); } /* emerald-600 */

/* ── One line of a component ─────────────────────────────────────────────────
   Same reasoning as the cell below: ~390 lines a sheet, one class string each. */
.spec-line {
  display: grid;
  grid-template-columns: 1fr;
  transition: background-color 150ms;
}
@media (width >= 40rem) {
  .spec-line { grid-template-columns: 1fr 1fr auto; }
}
.spec-line:hover { background-color: var(--color-steel-50); }
.dark .spec-line:hover { background-color: color-mix(in oklab, var(--color-steel-800) 40%, transparent); }

.spec-line-actions {
  display: flex;
  align-items: center;
  gap: 0.125rem;
  padding: 0.5rem 0.5rem 0.5rem 0.25rem;
  flex-shrink: 0;
}
.spec-line-remove {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 1.25rem;
  height: 1.25rem;
  border-radius: 0.25rem;
  color: var(--color-steel-300);
  transition: color 150ms, background-color 150ms;
}
.spec-line-remove svg { width: 0.75rem; height: 0.75rem; }
.spec-line-remove:hover {
  color: var(--color-red-600);
  background-color: var(--color-red-50);
}
.dark .spec-line-remove:hover {
  background-color: color-mix(in oklab, var(--color-red-900) 30%, transparent);
}

/* ── The editable cell ───────────────────────────────────────────────────────
   REAL CSS BECAUSE THE SHEET IS 194 COMPONENTS. These rules were utility classes
   on the element, and the `.spec-field` box alone carried some 600 characters of
   them; at ~390 cells that was half a megabyte of identical class strings in one
   response. A utility repeated four hundred times has stopped being a utility.

   Written against the theme's own custom properties (`--color-steel-*`,
   `--color-blueprint-*`), which Tailwind emits on :root — so the warm-palette
   turn keeps reaching these cells without this file naming a single colour.

   ⚠️ SPECIFICITY IS THE CONTRACT WITH THE PANEL SKIN. `process_tracker.css`
   restates all of this in the tracker's own values as `.process-tracker
   .spec-field` (0,2,0), and it must keep winning. Everything here stays at one
   class (0,1,0) except the dark and state variants, and those tie at (0,2,0) —
   ties go to `process_tracker.css`, which `stylesheet_link_tag :app` serves
   after this file. Do not raise the specificity of anything below. */
.spec-cell {
  display: flex;
  align-items: flex-start;
  gap: 0.375rem;
  padding: 0.5rem 0.75rem;
}
@media (width >= 40rem) {
  .spec-cell--divided { border-right: 1px solid var(--color-steel-100); }
  .dark .spec-cell--divided { border-right-color: var(--color-steel-800); }
}

.spec-field {
  display: flex;
  align-items: flex-start;
  gap: 0.5rem;
  flex: 1 1 0%;
  min-width: 0;
  padding: 0.375rem 0.625rem;
  border: 1px solid var(--color-steel-200);
  border-radius: 0.375rem;
  background-color: color-mix(in oklab, var(--color-steel-50) 70%, transparent);
  transition: color 150ms, background-color 150ms, border-color 150ms, box-shadow 150ms;
}
.spec-field:hover {
  background-color: #ffffff;
  border-color: var(--color-steel-300);
}
/* A cell that HAS content lifts to white, so filled and empty read apart at a
   glance without anyone having to focus them. */
.spec-field:has(textarea:not(:placeholder-shown)) { background-color: #ffffff; }
.spec-field:focus-within {
  background-color: #ffffff;
  border-color: var(--color-blueprint-400);
  box-shadow: 0 0 0 2px color-mix(in oklab, var(--color-blueprint-400) 25%, transparent);
}

.dark .spec-field {
  border-color: var(--color-steel-700);
  background-color: color-mix(in oklab, var(--color-steel-800) 40%, transparent);
}
.dark .spec-field:hover {
  background-color: var(--color-steel-800);
  border-color: var(--color-steel-600);
}
.dark .spec-field:has(textarea:not(:placeholder-shown)) { background-color: var(--color-steel-850); }
.dark .spec-field:focus-within { background-color: var(--color-steel-850); }

/* The leading icon. It used to ride Tailwind's `group` on the field; the parent
   selector says the same thing without the marker class. */
.spec-field-icon {
  width: 0.875rem;
  height: 0.875rem;
  margin-top: 0.125rem;
  flex-shrink: 0;
  color: var(--color-steel-400);
  transition: color 150ms;
}
.spec-field:focus-within .spec-field-icon { color: var(--color-blueprint-500); }

/* `overflow-y: hidden` IS LOAD-BEARING, not tidiness. Without it the textarea
   keeps the user-agent's `overflow: auto`, and spec_sheet_cell_controller's
   `_autoResize` then measures the cell at the WRONG WIDTH. Measured in Chromium
   against this file, at a cell 370px wide holding "GAF Timberline HDZ
   Architectural shingle | Charcoal": the line box is 19.5px while one line of
   text reports `scrollHeight` 20, so the `height: auto` phase of the measure
   always overflows by a hair, the textarea grows its own 10px scrollbar for the
   duration, and `clientWidth` drops 302 -> 292 — under the wrap boundary, which
   sits between 302 and 312. The height that gets written is therefore the height
   of a text reflowed to a width the cell will not have once the write lands. With
   this rule the same measure reads 302 in both phases. A cell sized to its content
   by JS must never be scrollable: the scrollbar is pure user-agent default, it is
   the thing drawn inside the field in the flicker reports, and it is the only way
   a height write in that controller can move a width. */
.spec-input {
  width: 100%;
  resize: none;
  overflow-y: hidden;
  background-color: transparent;
  font-size: 13px;
  color: var(--color-steel-800);
}
.spec-input--mono {
  font-family: var(--font-mono);
  font-size: 12px;
}
.spec-input::placeholder { color: var(--color-steel-300); }
.spec-input:focus { outline: none; }
.spec-input:disabled { opacity: 0.6; }
.dark .spec-input { color: var(--color-steel-100); }

/* Rail (trade index) */
.spec-rail-item.is-active {
  border-left-color: var(--color-brand-tan);
  background-color: #ffffff;
}
.spec-rail-item.is-active .spec-rail-name { color: var(--color-brand-navy); }
.spec-rail-item.is-complete .spec-rail-bar { background-color: rgb(44 138 103); }
.spec-rail-item.is-complete .spec-rail-frac { color: rgb(36 118 90); } /* emerald-700 */

.dark .spec-rail-item.is-active { background-color: rgb(42 39 31); } /* steel-800 */
.dark .spec-rail-item.is-active .spec-rail-name { color: rgb(247 244 238); }
.dark .spec-rail-item.is-complete .spec-rail-frac { color: rgb(92 184 144); } /* emerald-400 */

/* Legend swatches */
.spec-legend-swatch { display: inline-block; width: 18px; height: 10px; }
.spec-legend-swatch--filled {
  background-image: linear-gradient(to right, rgba(173, 151, 121, 0.5), transparent);
  border-left: 2px solid var(--color-brand-tan);
}
.spec-legend-swatch--pa {
  background-color: rgb(247 244 238);          /* steel-100 */
  border-left: 3px solid var(--color-blueprint-500);
}
.spec-legend-swatch--open {
  background-color: rgb(247 244 238);          /* steel-100 */
  border: 1px solid rgb(230 225 213);          /* steel-200 */
}
.dark .spec-legend-swatch--open {
  background-color: rgb(42 39 31);             /* steel-800 */
  border-color: rgb(61 57 49);                 /* steel-700 */
}

/* Print-friendly chrome — hides app shell when the user prints or saves as PDF */
@page {
  size: letter;
  margin: 0.5in;
}

@media print {
  /* Force solid white across the entire page — removes steel-100/50 tints */
  html, body,
  .bg-steel-100, .bg-steel-50, .bg-steel-150,
  .dark\:bg-steel-900, .dark\:bg-steel-850 {
    background: white !important;
    background-color: white !important;
  }

  .top-navbar,
  [data-controller~="feedback-widget"] {
    display: none !important;
  }

  /* Hide anything explicitly marked as screen-only during print */
  .print\:hidden {
    display: none !important;
  }

  /* Collapse the 3+2 grid to a single column so the contract uses full page width */
  .print\:grid-cols-1 {
    grid-template-columns: repeat(1, minmax(0, 1fr)) !important;
  }
  .print\:col-span-full {
    grid-column: 1 / -1 !important;
  }
  .print\:gap-4 {
    gap: 1rem !important;
  }

  /* Softer card borders on paper */
  article.bg-white {
    box-shadow: none !important;
    break-inside: avoid;
  }
}

/* Hidden scrollbar — scrollable but no visible track/thumb */
.scroll-hidden {
  overflow-y: auto;
  scrollbar-width: none;          /* Firefox */
  -ms-overflow-style: none;       /* IE/Edge */
}
.scroll-hidden::-webkit-scrollbar {
  display: none;                  /* Chrome/Safari */
}

/* Ledger scrollbar — visible scroll affordance for the Conceptual Building
   Breakdown. Thin steel tick (zero radius) sliding in a faint channel. */
.breakdown-scroll {
  overflow-y: auto;
  overscroll-behavior: contain;
  scrollbar-gutter: stable;
  padding-right: 0.75rem;        /* breathing room between content and scrollbar */

  /* Firefox */
  scrollbar-width: thin;
  scrollbar-color: rgba(22, 21, 17, 0.14) rgba(22, 21, 17, 0.03);
}
.dark .breakdown-scroll {
  scrollbar-color: rgba(255, 255, 255, 0.12) rgba(255, 255, 255, 0.025);
}
.breakdown-scroll::-webkit-scrollbar {
  width: 10px;
}
.breakdown-scroll::-webkit-scrollbar-track {
  background: rgba(22, 21, 17, 0.025);
  border-left: 1px solid rgba(22, 21, 17, 0.05);
}
.dark .breakdown-scroll::-webkit-scrollbar-track {
  background: rgba(255, 255, 255, 0.02);
  border-left-color: rgba(255, 255, 255, 0.05);
}
.breakdown-scroll::-webkit-scrollbar-thumb {
  background-color: rgba(22, 21, 17, 0.14);
  border: 2px solid transparent;
  background-clip: padding-box;
  transition: background-color 0.15s ease;
}
.dark .breakdown-scroll::-webkit-scrollbar-thumb {
  background-color: rgba(255, 255, 255, 0.12);
  border: 2px solid transparent;
  background-clip: padding-box;
}
.breakdown-scroll::-webkit-scrollbar-thumb:hover {
  background-color: rgba(22, 21, 17, 0.28);
}
.dark .breakdown-scroll::-webkit-scrollbar-thumb:hover {
  background-color: rgba(255, 255, 255, 0.24);
}

/* Service code callouts (PER-001, LND-002…). Slashed-zero distingue 0/O em
   IBM Plex Mono pequeno; tracking apertado mantém o tick compacto. */
.font-code-callout {
  font-variant-numeric: tabular-nums slashed-zero;
  font-feature-settings: "zero" 1;
  letter-spacing: -0.01em;
}

/* Currency input — $ prefix integrado via ::before, sem span irmão.
   O input filho recebe padding-left e text-left para o $ colar no número. */
.input-currency {
  position: relative;
  display: inline-block;
}
.input-currency::before {
  content: "$";
  position: absolute;
  left: 0.375rem;
  top: 50%;
  transform: translateY(-50%);
  font-size: 11px;
  color: rgb(151 144 127); /* steel-400 */
  pointer-events: none;
  font-family: "IBM Plex Mono", ui-monospace, monospace;
}
.dark .input-currency::before {
  color: rgb(107 102 90); /* steel-500 */
}
.input-currency > input {
  padding-left: 1.125rem;
}

/* Hover-link entre a stripe do footer e o sticky header da stage acima.
   O Stimulus draw_schedule_stripe_controller marca [data-stripe-highlight]
   no wrapper do stage; o seletor pinta o sticky header em warm tan/steel. */
[data-stripe-highlight="true"] > .sticky {
  background-color: rgb(254 252 247);  /* warm tint do brand-tan-50 */
}
.dark [data-stripe-highlight="true"] > .sticky {
  background-color: rgb(42 39 31);     /* steel-800 */
}

/* ============================================================================
   Login page (Direction A · Split) — utilitarian, IBM Plex, zero radius
   Tokens scoped under .login-page so they don't leak to the app.
   Paired with app/views/sessions/new.html.erb.
   ============================================================================ */
.login-page {
  --lk-navy: #0E2A3B;
  --lk-navy-hover: #14364A;
  --lk-gold: #C9A66B;
  --lk-cream: #F5F1EA;
  --lk-off-white: #FAF7F2;
  --lk-ember: #C0392B;

  display: grid;
  grid-template-columns: 1fr 1fr;
  min-height: 100vh;
  width: 100%;
  background: var(--lk-off-white);
  font-family: 'IBM Plex Sans', sans-serif;
  color: var(--lk-navy);
}

/* ---- Left panel (brand) -------------------------------------------------- */
.login-page__brand {
  background: var(--lk-navy);
  padding: 32px 40px;
  color: var(--lk-cream);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 32px;
}

.login-page__brand-head {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  gap: 16px;
}

.login-page__brand-body { max-width: 440px; }

.login-page__logo {
  color: var(--lk-cream);
  height: 28px;
  width: auto;
}

.login-page__brand-title {
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 28px;
  font-weight: 400;
  line-height: 1.2;
  color: var(--lk-cream);
  margin: 12px 0 0;
  max-width: 400px;
}

.login-page__brand-meta {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: rgba(245, 241, 234, 0.4);
  display: flex;
  gap: 24px;
  flex-wrap: wrap;
}

/* ---- Right panel (form) -------------------------------------------------- */
.login-page__form {
  padding: 32px 56px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 32px;
}

.login-page__form-top { min-height: 24px; }

.login-page__form-inner {
  max-width: 360px;
  width: 100%;
  align-self: center;
}

.login-page__eyebrow {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--lk-gold);
  letter-spacing: 0.02em;
  margin: 0 0 14px;
}

.login-page__heading {
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 24px;
  font-weight: 500;
  letter-spacing: -0.01em;
  color: var(--lk-navy);
  margin: 0 0 10px;
}

.login-page__hint {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: rgba(14, 42, 59, 0.6);
  line-height: 1.5;
  margin: 0 0 36px;
}

/* ---- Form fields --------------------------------------------------------- */
.login-page__fields {
  display: flex;
  flex-direction: column;
  gap: 24px;
}

.login-page__field {
  display: flex;
  flex-direction: column;
  gap: 6px;
}

.login-page__field-label {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: 0.02em;
  color: rgba(14, 42, 59, 0.7);
  text-transform: lowercase;
}

.login-page__field-label--error { color: var(--lk-ember); }

.login-page__field-wrapper {
  display: flex;
  align-items: center;
  background: #fff;
  border: 1px solid rgba(14, 42, 59, 0.18);
  transition: border-color 120ms ease, box-shadow 120ms ease;
}

.login-page__field-wrapper:focus-within {
  border-color: var(--lk-gold);
  box-shadow: 0 0 0 3px rgba(201, 166, 107, 0.18);
}

.login-page__field-wrapper--error { border-color: var(--lk-ember); }
.login-page__field-wrapper--error:focus-within {
  border-color: var(--lk-ember);
  box-shadow: 0 0 0 3px rgba(192, 57, 43, 0.15);
}

.login-page__input {
  flex: 1;
  background: transparent;
  border: none;
  outline: none;
  padding: 11px 13px;
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 14px;
  color: var(--lk-navy);
  min-width: 0;
}

.login-page__input::placeholder { color: rgba(14, 42, 59, 0.35); }

.login-page__toggle {
  background: transparent;
  border: none;
  cursor: pointer;
  padding: 8px 12px;
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: rgba(14, 42, 59, 0.55);
}
.login-page__toggle:hover { color: var(--lk-navy); }

.login-page__error-text {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: var(--lk-ember);
  margin-top: 6px;
}

/* ---- Remember-me row ---------------------------------------------------- */
.login-page__row {
  display: flex;
  justify-content: space-between;
  align-items: center;
  gap: 12px;
}

.login-page__checkbox-label {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 13px;
  color: rgba(14, 42, 59, 0.8);
  user-select: none;
}

.login-page__checkbox-box {
  position: relative;
  width: 15px;
  height: 15px;
  border: 1.5px solid rgba(14, 42, 59, 0.3);
  background: transparent;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  transition: all 120ms ease;
}

.login-page__checkbox-input {
  position: absolute;
  opacity: 0;
  width: 1px;
  height: 1px;
  margin: 0;
  padding: 0;
  pointer-events: none;
}

.login-page__checkbox-input:checked ~ .login-page__checkbox-box {
  background: var(--lk-navy);
  border-color: var(--lk-navy);
}
.login-page__checkbox-input:focus-visible ~ .login-page__checkbox-box {
  box-shadow: 0 0 0 3px rgba(201, 166, 107, 0.18);
}

.login-page__checkbox-tick {
  opacity: 0;
  stroke: var(--lk-gold);
  transition: opacity 80ms ease;
}
.login-page__checkbox-input:checked ~ .login-page__checkbox-box .login-page__checkbox-tick {
  opacity: 1;
}

.login-page__forgot {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 12px;
  color: var(--lk-navy);
  text-decoration: underline;
}
.login-page__forgot:hover { color: var(--lk-gold); }

/* ---- Submit button ------------------------------------------------------ */
.login-page__btn {
  width: 100%;
  margin-top: 12px;
  background: var(--lk-navy);
  color: var(--lk-cream);
  border: none;
  padding: 12px 16px;
  font-family: 'IBM Plex Sans', sans-serif;
  font-size: 14px;
  font-weight: 500;
  cursor: pointer;
  transition: background 120ms ease;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 10px;
}

.login-page__btn:hover:not(:disabled) { background: var(--lk-navy-hover); }
.login-page__btn:focus-visible {
  outline: none;
  box-shadow: 0 0 0 3px rgba(201, 166, 107, 0.35);
}
.login-page__btn:disabled { cursor: wait; }

.login-page__btn[aria-busy="true"]::before {
  content: "";
  width: 13px;
  height: 13px;
  border-radius: 50%;
  border: 2px solid var(--lk-cream);
  border-top-color: transparent;
  animation: lk-spin 0.7s linear infinite;
}

@keyframes lk-spin { to { transform: rotate(360deg); } }

/* ---- Footer ------------------------------------------------------------- */
.login-page__footer {
  font-family: 'IBM Plex Mono', monospace;
  font-size: 11px;
  color: rgba(14, 42, 59, 0.5);
  display: flex;
  justify-content: space-between;
  gap: 16px;
}
.login-page__footer a { color: inherit; text-decoration: underline; }
.login-page__footer a:hover { color: var(--lk-navy); }

/* ---- Responsive --------------------------------------------------------- */
@media (max-width: 768px) {
  .login-page {
    grid-template-columns: 1fr;
    grid-template-rows: auto 1fr;
  }

  .login-page__brand {
    padding: 20px 24px;
    gap: 16px;
  }

  .login-page__brand-body,
  .login-page__brand-meta { display: none; }

  .login-page__form {
    padding: 32px 24px;
    gap: 32px;
  }

  .login-page__btn,
  .login-page__toggle { min-height: 44px; }
}


/* The Architectural Workspace's disclosure choreography lived here — focus
   dimming for the closed cards' mini tracks, and the ::details-content height
   animation for the expanding bodies. Both went out with the cards themselves:
   the screen is a rail plus one panel now, so there is no second card to dim and
   nothing expands in place. The `interpolate-size` opt-in went with them because
   those rules were its only consumers in the app; the tracker's popups are
   <dialog>, which needs none of it. */

/* ---------------------------------------------------------------------------
   Process pages · deep canvas
   Standard background for every Process::PageLayoutComponent page (the home
   opts out — it has its own identity). Approved 2026-06-11 on the
   Architectural Workspace: deep canvas with a light top gradient, settling into
   its floor by ~720px (px stop so long pages keep the same depth). Flat depth,
   no texture, no shadows.

   PAPEL, NÃO ARDÓSIA (07/ago). Este gradiente é o FUNDO DE TODA PÁGINA DE
   PROCESSO e não passa por token nenhum — foi o último azul de pé depois da
   virada da paleta, e o que o dono viu quando disse "o fundo azul ainda
   continua". Os três degraus viraram creme, calibrados contra o painel branco
   (#fffdf9) para manter a mesma diferença de profundidade que a versão fria
   tinha: canvas mais fundo que a superfície, sem sombra para separar.
--------------------------------------------------------------------------- */
/* ONE GROUND, NO FADE (owner, 21/ago: "remover transição gradual… essa cor").
   The canvas used to lighten toward the top, so the same surface read as two
   colours depending on scroll and the seam under the process band moved with
   it. It is now flat at the gradient's own end colour — the tone the page
   already settled on below the fold. */
.process-canvas {
  background: #eee9dd;
}

.dark .process-canvas {
  background: #131210;
}

/* ---------------------------------------------------------------------------
   THE BADGE — one stamp for the whole app (PDS-416).

   TWO STYLES USED TO COEXIST and the owner kept the newer one: "os badges do
   pre-permit estão mais modernos e superiores aos demais badges da aplicação…
   vamos obsoletar o antigo e manter apenas o novo estilo globalmente". The old
   one was sans, semibold, `tracking-wider`, with a 1px BORDER and sharp corners;
   this one is MONO, bold, wider tracking, roomier, fill-only and softly rounded —
   a stamp rather than a boxed label, which is what made it read as the modern of
   the two.

   IT IS A CLASS AND NOT ONLY A COMPONENT because both call styles exist and must
   not drift: Ui::Base::BadgeComponent renders it, and the helpers that return a
   class NAME (triage_answer_word_class and friends) hand it back as a string.
   It replaced `.trk-word`, which was the same look scoped under `.process-tracker` — the
   tracker's tokens were the app's warm tokens under different names, value for
   value, so this is one spelling surviving rather than a new design.

   FOUR TONES, AND THEY ARE STATES. Green closed, amber waiting, red blocked,
   idle no state at all. The old vocabulary's `info` and `progress` collapse into
   idle: "Active" and "Accepted" are not points on a scale that runs from waiting
   to blocked, and six colours competing on one screen is what the collapse buys
   its way out of.
--------------------------------------------------------------------------- */
.badge {
  display: inline-flex;
  align-items: center;
  font-family: ui-monospace, "SF Mono", Consolas, monospace;
  font-size: 10.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .1em;
  padding: 3px 9px;
  border-radius: 3px;
  white-space: nowrap;
}

.badge-green { background: #e5f0ea; color: #1e6b50; }
.badge-amber { background: #f9efdd; color: #a8650f; }
.badge-red   { background: #f9e7e4; color: #b3362b; }
.badge-idle  { background: #efebe0; color: #6b665a; }
/* The two "somebody else is holding it" states. One hue in two steps, matching
   the Contract Development pipeline mark for mark — see the vocabulary block in
   process_tracker.css for why they are not two unrelated hues. */
.badge-wait  { background: #edf0f6; color: #51608a; }
.badge-owed  { background: #e4e8f0; color: #2d3d5f; }
/* PDS: the tracker's "one distinct human-origin marker" (PDS-424) — a PM-typed
   value overriding a computed one. Added for the Estimate restyle's manual
   price-override pill (previously plain blue, which the warm palette no
   longer treats as a status color at all). */
.badge-plum  { background: #f3e9f4; color: #7a4b7d; }

/* The dark values are the tracker's own, so a badge inside `.process-tracker` and one
   outside it stay identical in both themes. */
.dark .badge-green { background: #1d3a2f; color: #5cb890; }
.dark .badge-amber { background: #3a2d17; color: #e0a352; }
.dark .badge-red   { background: #3c1f1b; color: #e0705f; }
.dark .badge-idle  { background: #2a271f; color: #a8a294; }
.dark .badge-wait  { background: #262f42; color: #aeb8d0; }
.dark .badge-owed  { background: #1e2942; color: #8592b0; }
.dark .badge-plum  { background: #33233a; color: #c79ad0; }

/* ---------------------------------------------------------------------------
   THE WORK BUTTON — three weights, ONE geometry.

   Born inside the Pre-permit/Triage skin as `trk-act`, `trk-act-soft` and
   `trk-act-ghost` (process_tracker.css), where it is the only button the owner
   ever approved. Those rules are scoped under `.process-tracker` and read
   `--trk-*` tokens, so nothing outside the skin could use them — and the screens
   outside it kept spelling the old V2 pattern (uppercase, `bg-blueprint-800`,
   square) or `Ui::Base::ButtonComponent`'s rounded sentence-case button. Three
   spellings of "the button", which is how the Documents index ended up with one
   the owner did not recognise (21/ago/2026).

   WHAT CARRIES HIERARCHY IS THE INK, NOT THE SHAPE. Same padding, same type,
   same radius in all three: `.act` commits (filled), `.act-soft` opens or
   navigates (outline), `.act-ghost` is the secondary act that gives up its fill
   and its border until you point at it. A disabled button must LOOK disabled —
   dashed border and faint ink, keeping its place in the layout (PDS-419).

   Values are the tracker's, re-expressed in the app's own tokens: blueprint-800
   is the navy the skin calls `--trk-navy`. Inside the skin keep using `trk-act*`
   — same look, tokens already in scope.
   --------------------------------------------------------------------------- */
.act,
.act-soft,
.act-ghost,
.act-danger {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 8px 15px;
  font-size: 13px;
  font-weight: 650;
  line-height: 1.2;
  border-radius: 3px;
  border: 1px solid transparent;
  cursor: pointer;
  width: fit-content;
  transition: background-color .15s, color .15s, border-color .15s, filter .15s;
}
.act {
  background: var(--color-blueprint-800);
  border-color: var(--color-blueprint-800);
  color: #ffffff;
}
.act:hover { filter: brightness(1.07); }
.act-soft {
  color: var(--color-blueprint-800);
  border-color: var(--color-blueprint-800);
  opacity: .9;
}
.act-soft:hover { opacity: 1; background: var(--color-blueprint-50); }
.act-ghost { color: var(--color-steel-500); }
.act-ghost:hover { color: var(--color-steel-800); border-color: var(--color-steel-200); }

/* O QUARTO DEGRAU É UM MATIZ, NÃO UM PESO (21/ago/2026). Void envelope e Cancel
   não são "menos importantes" que Resend — são de outra natureza: destroem
   trabalho. A escada `.act / .act-soft / .act-ghost` gradua compromisso e não
   tem onde encaixar isso, então o destrutivo herda a geometria de `.act-soft`
   (contorno, nunca sólido: um botão vermelho preenchido convida o clique que
   ele deveria desencorajar) e troca só a tinta. */
.act-danger { color: var(--color-red-700); border-color: var(--color-red-300); }
.act-danger:hover { background: var(--color-red-50); border-color: var(--color-red-700); }
.dark .act-danger { color: var(--color-red-400); border-color: var(--color-red-800); }
.dark .act-danger:hover { background: color-mix(in oklab, var(--color-red-400) 12%, transparent); border-color: var(--color-red-400); }

.dark .act {
  background: var(--color-blueprint-400);
  border-color: var(--color-blueprint-400);
  color: #10201a;
}
.dark .act-soft { color: var(--color-blueprint-400); border-color: var(--color-blueprint-400); }
.dark .act-soft:hover { background: color-mix(in oklab, var(--color-blueprint-400) 14%, transparent); }
.dark .act-ghost { color: var(--color-steel-400); }
.dark .act-ghost:hover { color: var(--color-steel-100); border-color: var(--color-steel-700); }

.act:disabled,
.act-soft:disabled,
.act-ghost:disabled,
.act-danger:disabled {
  color: var(--color-steel-400);
  background: none;
  border: 1px dashed var(--color-steel-200);
  cursor: not-allowed;
  opacity: 1;
  filter: none;
}
.dark .act:disabled,
.dark .act-soft:disabled,
.dark .act-ghost:disabled,
.dark .act-danger:disabled { border-color: var(--color-steel-700); color: var(--color-steel-500); }
