/*
 * dxg-letterbox-v1 — components.css
 * NEW semantic objects authored IN the design system for the DXG frontend
 * generator (StepFormRenderer, LibraryNav, CaseView, ActionBar). These are
 * the frozen v1 role classes referenced by packages/dxg-cds-letterbox/manifest.json
 * "roles" map — app code never spells these class names, it resolves them
 * through useCds().cls(role).
 *
 * Dense per design discipline: tight tables, minimal vertical gaps, no
 * hero/dashboard chrome.
 */

/* ---- nav.* — library nav tree ---- */

.dxg-nav-root {
  display: flex;
  flex-direction: column;
  gap: 1px;
  font-family: var(--dxg-font-ui);
  font-size: 12.5px;
  width: 280px;
  flex-shrink: 0;
}

/* A group's own children indent slightly, so nesting reads as a real tree
   rather than a flat list with occasional uppercase dividers (Task 15,
   2026-07-20 — real value-stream/hub grouping replaces the old flat
   555-item list). */
.dxg-nav-group {
  padding: 10px 10px 4px;
  font-family: var(--dxg-font-mono);
  font-weight: 500;
  font-size: 10px;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--dxg-ink-mute);
}
.dxg-nav-group > .dxg-nav-group,
.dxg-nav-group > .dxg-nav-item {
  margin-left: 6px;
  border-left: 1px solid var(--dxg-hairline);
}

/* Task 15 (2026-07-20, Ben-flagged): a real reset. Before this, `<button
   class="dxg-nav-item">` had no `background`/`border`/`appearance`/`width`
   declared at all, so every nav item (all ~560 processes) rendered as
   native OS button chrome under a thin text-color skin — the boxy grey-
   bordered look an independent UX review flagged. `all: unset` clears the
   full native `<button>` UA stylesheet (border, background, font,
   min-height) in one line before the real declarations below apply. */
.dxg-nav-item {
  all: unset;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: 1px;
  padding: 6px 10px;
  color: var(--dxg-ink-soft);
  line-height: 1.3;
  cursor: pointer;
  width: 100%;
  border-left: 2px solid transparent;
  font-family: var(--dxg-font-ui);
  font-size: 12.5px;
  /* Ben-flagged (2026-07-20, second pass): nested inside `.dxg-nav-group`
     (which sets `text-transform: uppercase` for its own mono eyebrow
     label), `text-transform` is an inherited CSS property -- without this
     explicit reset every item title/subtitle silently inherited SHOUTING
     caps from its ancestor group, even though neither `.dxg-nav-item`,
     `.dxg-nav-item-title`, nor `.dxg-nav-item-subtitle` ever declared it. */
  text-transform: none;
}
.dxg-nav-item:hover { background: var(--dxg-paper-3); }

/* Modifier: applied together with .dxg-nav-item on the active entry. */
.dxg-nav-item-active {
  border-left-color: var(--dxg-baobab);
  background: var(--dxg-paper);
  font-weight: 600;
  color: var(--dxg-ink);
}

.dxg-nav-item-title {
  display: block;
}

/* Task 15 (2026-07-20): the real, human-authored description line — only
   rendered when the process actually has one (see `cleanDescription`,
   fm-gateway-client.ts). Muted + smaller than the title, one line, clipped
   rather than wrapped (density over completeness in a 280px column). */
.dxg-nav-item-subtitle {
  display: block;
  width: 100%;
  font-size: 10.5px;
  font-weight: 400;
  color: var(--dxg-ink-mute);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ---- form.* — StepFormRenderer ---- */

.dxg-form-section {
  padding: 10px 0;
  border-bottom: 1px solid var(--dxg-hairline);
  /* ONE grid for the whole section (2026-07-30), so every row shares a single
     caption column sized to the widest caption present — capped at the old
     250px ceiling so a long caption still wraps rather than squeezing the
     controls. Previously each row was its own grid with a FIXED 250px caption
     column, sized back when captions were long description sentences; once
     the real short authored captions started rendering ("Your name",
     "Urgency"), that fixed width left ~190px of dead space beside every row
     and pushed the controls and the related-list table into a needlessly
     narrow measure. Rows use `display: contents` so their label and control
     land in this grid's columns and stay aligned down the whole section. */
  display: grid;
  /* `fit-content(250px)`, NOT `min(max-content, 250px)`: a CSS math function
     cannot take an intrinsic keyword, so that spelling is invalid and the
     browser drops the whole declaration — which collapsed every form to a
     single column with the caption stacked above its control (observed live
     2026-07-30 before this correction). `fit-content()` is the primitive that
     actually means "size to content, capped here". */
  grid-template-columns: fit-content(250px) minmax(0, 1fr);
  column-gap: 14px;
  row-gap: 8px;
  align-items: start;
}

/* The title is not a label/control pair — it spans the full section. */
.dxg-form-section-title { grid-column: 1 / -1; }
.dxg-form-section:last-child { border-bottom: none; }

.dxg-form-section-title {
  font-family: var(--dxg-font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 14px;
  color: var(--dxg-ink);
  margin: 0 0 6px;
}

/* The row is a pass-through: its label and control become direct children of
   the section grid above, which owns the two columns. The 250px ceiling that
   used to live here is now that grid's cap. */
.dxg-form-row {
  display: contents;
}

.dxg-form-label {
  font-family: var(--dxg-font-ui);
  font-weight: 600;
  font-size: 12px;
  color: var(--dxg-ink-mute);
  padding-top: 6px;
}

.dxg-form-control {
  font-family: var(--dxg-font-ui);
  font-size: 13px;
  color: var(--dxg-ink);
  background: var(--dxg-paper);
  border: 1px solid var(--dxg-hairline);
  border-radius: 3px;
  padding: 6px 8px;
  width: 100%;
  line-height: 1.3;
}
/* Focus ring is neutral ink (2026-07-29, adversarial round-2 UX finding:
   red was overloaded across required-marks, focus and errors — a focused
   VALID field must not read as an error). */
.dxg-form-control:focus-visible { outline: 2px solid var(--dxg-ink); outline-offset: 1px; }

/* Modifier: applied together with .dxg-form-control when the engine reports
   an unresolvable field (unknown type, unresolvable options_source). Submit
   stays disabled for this row — no silent skip. */
.dxg-form-control-blocked {
  border-color: var(--dxg-baobab);
  background: var(--dxg-baobab-bg);
  color: var(--dxg-ink-mute);
  cursor: not-allowed;
}

.dxg-form-required-mark {
  color: var(--dxg-baobab);
  font-weight: 700;
  margin-left: 3px;
}

.dxg-form-lineitems-table {
  width: 100%;
  border-collapse: collapse;
  font-family: var(--dxg-font-ui);
  font-size: 12.5px;
  margin-top: 4px;
}
.dxg-form-lineitems-table th {
  text-align: left;
  padding: 5px 8px;
  border-bottom: 1px solid var(--dxg-hairline);
  font-family: var(--dxg-font-mono);
  font-weight: 500;
  font-size: 10px;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--dxg-ink-mute);
}

.dxg-form-lineitems-row td {
  padding: 4px 8px;
  border-bottom: 1px solid var(--dxg-hairline);
  vertical-align: middle;
}
.dxg-form-lineitems-row:last-child td { border-bottom: none; }

/* ---- presentation overlay (2026-08-12) — data-dxg-* attribute hooks ----
 *
 * The optional presentation overlay (packages/dxg-core/src/presentation/
 * overlay.ts) emits inert `data-*` attributes rather than CDS role classes,
 * because cls() THROWS on a role missing from the active manifest and there
 * is more than one manifest in play. Everything below is therefore ADDITIVE:
 * a form with no overlay matches none of these selectors and renders exactly
 * as it did before this block existed, and a CDS that never adopts these
 * rules is not broken by an overlay — it just ignores it.
 *
 * There are deliberately no rules for `.dxg-form-control-blocked` here:
 * BlockedField is never handed an overlay by the renderer, and no overlay
 * colour or width may ever make a blocked field read as a normal one. */

/* A section declared as a two-column band: the SAME caption-beside-control
   grid as every other section, doubled — caption | control | caption |
   control. Rows stay `display: contents` pass-throughs exactly as they are
   in the default layout, so a `half` row's caption and control land in one
   pair of columns and two of them share a line.

   The first spelling of this (2026-08-12, replaced same day) made the band
   `repeat(2, 1fr)` and turned each row into a flex column, stacking the
   caption ABOVE its control. Measured against the real IT-01 intake step it
   was worse on both counts it was meant to improve: the section grew from
   168px to 210px, because four stacked captions cost more height than
   pairing two fields saves, and the paired inputs OVERLAPPED by 4px (see the
   box-sizing note below). This spelling measures 129px on the same step —
   shorter than the 168px it started from, which is the whole point — and
   leaves every caption where a reader already expects it. */
.dxg-form-section[data-dxg-column-count="2"] {
  grid-template-columns: fit-content(250px) minmax(0, 1fr) fit-content(250px) minmax(0, 1fr);
}
/* A row that is not explicitly `half` spans the whole band, so it renders
   identically to how it renders with no overlay at all: caption in column 1,
   control filling the rest. This is what keeps `BlockedField` — which never
   receives a width — full width and unmistakable inside a band. Both rules
   are needed because the row itself is `display: contents`: it is the
   row's CHILDREN that are the grid items, not the row. */
.dxg-form-section[data-dxg-column-count="2"] > .dxg-form-row:not([data-dxg-width="half"]) > .dxg-form-label {
  grid-column: 1;
}
.dxg-form-section[data-dxg-column-count="2"] > .dxg-form-row:not([data-dxg-width="half"]) > :not(.dxg-form-label) {
  grid-column: 2 / -1;
}
/* `.dxg-form-control` is content-box (padding 6px 8px + 1px border), so it
   renders ~18px WIDER than the grid column it sits in. In the single-column
   layout that overhang lands in the section's right margin and nobody has
   ever seen it; put two controls side by side and it becomes a visible
   4px overlap between them. Corrected inside the band only — changing
   `.dxg-form-control` itself would resize every control on every form in the
   product, which is precisely the kind of change an opt-in cosmetic overlay
   must not make. The wider issue is real and reported separately. */
.dxg-form-section[data-dxg-column-count="2"] .dxg-form-control {
  box-sizing: border-box;
}

/* Alternating row tint for long, scan-heavy sections. Applied to the row's
   CHILDREN, not the row: `.dxg-form-row` is `display: contents` in the
   default (single-column) layout and so paints no background of its own.
   `nth-of-type` counts div siblings, which the optional `<h3>` section title
   does not disturb. */
.dxg-form-section[data-dxg-banded="true"] > .dxg-form-row:nth-of-type(even) > * {
  background: var(--dxg-paper-2);
}

/* Right-aligned control — the honest use is a numeric/currency field whose
   digits should line up, never decorative alignment. */
.dxg-form-row[data-dxg-align="end"] .dxg-form-control {
  text-align: right;
}

/* Caption weight only. Emphasis never changes a control's colour: red/baobab
   is already spoken for by required-marks and blocked fields, and a second
   meaning for the same ink is exactly the overload the round-2 UX review
   flagged. */
.dxg-form-row[data-dxg-emphasis="strong"] .dxg-form-label {
  font-weight: 700;
  color: var(--dxg-ink);
}
.dxg-form-row[data-dxg-emphasis="muted"] .dxg-form-label {
  font-weight: 400;
}

/* ---- actionbar.* — available_actions[] rendering ---- */

.dxg-actionbar-root {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 10px 0 0;
  margin-top: 10px;
  border-top: 1px solid var(--dxg-hairline);
}

.dxg-actionbar-primary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: 4px;
  font-family: var(--dxg-font-ui);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.02em;
  cursor: pointer;
  border: 1px solid var(--dxg-ink);
  background: var(--dxg-ink);
  color: var(--dxg-paper);
  transition: all 0.14s ease;
}
.dxg-actionbar-primary:hover:not(:disabled) { background: var(--dxg-baobab); border-color: var(--dxg-baobab); }
.dxg-actionbar-primary:disabled { cursor: not-allowed; opacity: 0.5; }

.dxg-actionbar-secondary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 7px 14px;
  border-radius: 4px;
  font-family: var(--dxg-font-ui);
  font-weight: 600;
  font-size: 12px;
  letter-spacing: 0.02em;
  cursor: pointer;
  border: 1px solid var(--dxg-ink);
  background: transparent;
  color: var(--dxg-ink);
  transition: all 0.14s ease;
}
.dxg-actionbar-secondary:hover:not(:disabled) { background: var(--dxg-ink); color: var(--dxg-paper); }
.dxg-actionbar-secondary:disabled { cursor: not-allowed; opacity: 0.5; }

/* Revealed inline when the chosen action has requires_reason: true. */
.dxg-actionbar-reason-reveal {
  width: 100%;
  margin-top: 8px;
  padding: 10px;
  background: var(--dxg-paper-2);
  border: 1px solid var(--dxg-hairline);
  border-radius: 4px;
  font-family: var(--dxg-font-ui);
  font-size: 12.5px;
}

/* ---- case.* — CaseView ---- */

.dxg-case-header {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  padding-bottom: 8px;
  margin-bottom: 10px;
  border-bottom: 1px solid var(--dxg-hairline);
}

.dxg-case-step-title {
  font-family: var(--dxg-font-display);
  font-style: italic;
  font-weight: 600;
  font-size: 18px;
  color: var(--dxg-ink);
  margin: 0;
}

.dxg-case-history {
  display: flex;
  flex-direction: column;
  gap: 3px;
  font-family: var(--dxg-font-mono);
  font-size: 11px;
  color: var(--dxg-ink-mute);
}

/* ---- error.* — no fallbacks, structural error surfaces ---- */

.dxg-error-surface {
  padding: 12px 14px;
  border: 1px solid var(--dxg-hairline);
  border-left: 3px solid var(--dxg-baobab);
  background: var(--dxg-baobab-bg);
  font-family: var(--dxg-font-ui);
  font-size: 12.5px;
  color: var(--dxg-ink-soft);
}

/* Modifier: applied together with .dxg-error-surface for the "authoring
   gap" case (human step with no presentation view). Never a guessed form. */
.dxg-error-authoring-gap {
  /* Solid, same as every other error card (2026-07-29 round-3 UX finding:
     dashed read as a different severity for the same class of failure). */
  border-left-style: solid;
}

/* Modifier: applied together with .dxg-error-surface for an unmapped
   principal. No guest fallback. */
.dxg-error-access-denied {
  border-left-color: var(--dxg-ink);
  background: var(--dxg-paper-2);
}

/* The PRIMARY human-readable line inside .dxg-error-surface — resolved from
   the gateway's own error code, never the raw FmRequestError string. */
.dxg-error-message {
  margin: 0 0 8px;
  font-family: var(--dxg-font-ui);
  font-size: 12.5px;
  color: var(--dxg-ink-soft);
}

/* Collapsed-by-default operator detail (op/status/raw body) — a native
   <details>, no JS state needed to keep it closed until asked for. */
.dxg-error-technical {
  margin: 0 0 10px;
}

.dxg-error-technical-summary {
  cursor: pointer;
  font-family: var(--dxg-font-mono);
  font-size: 10px;
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--dxg-ink-mute);
}

.dxg-error-technical-body {
  margin: 6px 0 0;
  font-family: var(--dxg-font-mono);
  font-size: 11px;
  color: var(--dxg-ink-mute);
  white-space: pre-wrap;
  word-break: break-word;
}

/* ---- login.* — /login sign-in surface (Task 13, MMDB-1525 rung-5 fix) ---- */

.dxg-login-shell {
  min-height: calc(100vh - 96px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 24px;
}

.dxg-login-card {
  background: var(--dxg-paper);
  border: 1px solid var(--dxg-hairline);
  border-radius: 4px;
  padding: 28px;
  width: 100%;
  max-width: 340px;
  display: flex;
  flex-direction: column;
  gap: 10px;
}

.dxg-login-title {
  font-family: var(--dxg-font-display);
  font-style: italic;
  font-weight: 700;
  font-size: 20px;
  color: var(--dxg-ink);
  margin: 0;
}

.dxg-login-subtitle {
  font-family: var(--dxg-font-ui);
  font-size: 12.5px;
  color: var(--dxg-ink-mute);
  margin: 0 0 8px;
}

/* ---- library.* — root library page: nav + content area, side by side ---- */

.dxg-library-shell {
  display: flex;
  align-items: flex-start;
  /* The content column stays in view alongside the independently
     scrolling nav (round-11 UX finding). */
  gap: 24px;
  max-width: 1080px;
  padding: 20px 24px;
  margin: 0 auto;
}

.dxg-library-nav-column {
  display: flex;
  flex-direction: column;
  gap: 8px;
  flex-shrink: 0;
  /* 2026-07-29 (round-11 UX finding): the nav is the full process catalogue
     (700+ entries), so the PAGE used to scroll as one — clicking an item far
     down the list left the result panel stranded above the fold with no
     visual cue. The nav scrolls inside its own column between the fixed
     shell bars, so the content panel stays where the user is looking. */
  max-height: calc(100vh - 140px);
  position: sticky;
  top: 20px;
}

/* 2026-07-29 (round-20 UX finding): only the LIST scrolls, not the whole
   column. Before this the filter box lived inside the scroller, so (a) it
   scrolled out of sight as soon as the user moved down the 700-entry
   catalogue, and (b) the scroll container itself took the first Tab stop —
   a keyboard user tabbing past the wordmark landed on the column, typed,
   and watched the characters go nowhere. With the filter pinned outside,
   the first Tab after the wordmark reaches the real input. */
.dxg-library-nav-scroll {
  flex: 1;
  min-height: 0;
  overflow-y: auto;
}

/* Task 15 (2026-07-20, Ben-flagged): a real max-width. Before this, the
   content area was a bare `flex: 1` with no cap — on any wide viewport a
   9-field form's inputs (`.dxg-form-row`'s `1fr` column) stretched to the
   full remaining browser width, the "edge-to-edge, no padding" defect an
   independent UX review flagged. 640px keeps a form row's input column at
   a readable measure regardless of viewport. */
.dxg-library-content {
  flex: 1;
  min-width: 0;
  max-width: 640px;
}

.dxg-library-no-results {
  padding: 10px;
  font-family: var(--dxg-font-ui);
  font-size: 12.5px;
  color: var(--dxg-ink-mute);
}

/* ---- case.* (shell) — the case page's own content frame, same measure
   discipline as library.content: a real max-width + page padding, so a
   launched case never runs edge-to-edge either (Task 15, 2026-07-20). ---- */

.dxg-case-shell {
  max-width: 680px;
  padding: 20px 24px;
  margin: 0 auto;
}

/* ---- empty.* — the honest v1 default content-area surface: no case
   active, no dashboard, no fabricated data, one real instruction. ---- */

.dxg-empty-state {
  display: flex;
  flex-direction: column;
  gap: 6px;
  padding: 20px;
  border: 1px dashed var(--dxg-hairline);
  border-radius: 4px;
  max-width: 420px;
}

.dxg-empty-state-title {
  font-family: var(--dxg-font-display);
  font-style: italic;
  font-weight: 700;
  font-size: 16px;
  color: var(--dxg-ink);
  margin: 0;
}

.dxg-empty-state-subtitle {
  font-family: var(--dxg-font-ui);
  font-size: 12.5px;
  color: var(--dxg-ink-mute);
  margin: 0;
}

/* Completed-case block (2026-07-29, adversarial round-1 UX findings 3+4):
   terminal confirmation with the submitted values and the one route back. */
.dxg-case-completed {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 6px;
}
.dxg-case-completed-note {
  font-family: var(--dxg-font-display);
  font-style: italic;
  font-size: 17px;
  color: var(--dxg-ink);
}
.dxg-case-kv {
  display: grid;
  /* Capped key column (2026-07-30, live completed-case defect): with
     `max-content` a long key consumed the whole row and crushed the value
     into a one-character column at the screen edge. The cap makes a long key
     wrap while the value keeps a readable measure; `minmax(0, 1fr)` lets the
     value column actually shrink instead of overflowing. */
  grid-template-columns: fit-content(250px) minmax(0, 1fr);
  gap: 2px 14px;
  margin: 0;
}
.dxg-case-kv-key {
  font-family: var(--dxg-font-mono);
  font-size: 11px;
  color: var(--dxg-ink-mute);
  text-transform: uppercase;
  letter-spacing: 0.04em;
  align-self: baseline;
}
.dxg-case-kv-value {
  font-family: var(--dxg-font-ui);
  font-size: 13px;
  color: var(--dxg-ink);
  margin: 0;
  overflow-wrap: anywhere;
  /* Submitted prose keeps the line breaks the person typed (2026-08-12).
     `text`/`textarea` fields are edited in a multi-line textarea, but the
     confirmation rendered them through the HTML default `white-space:
     normal`, which collapses every newline — so a four-line description read
     back as one run-on line, and the reader could not tell the record from a
     single-line answer. `pre-wrap` keeps the breaks AND keeps wrapping long
     lines; `overflow-wrap: anywhere` above still handles an unbroken string.
     Same declaration and the same reason as `.dxg-error-technical-body`. */
  white-space: pre-wrap;
}
.dxg-case-back-link {
  font-family: var(--dxg-font-mono);
  font-size: 11px;
  color: var(--dxg-ink-soft);
  text-decoration: underline;
  text-underline-offset: 3px;
  width: max-content;
}
.dxg-case-back-link:hover { color: var(--dxg-baobab); }

/* The back-link role also serves a <button> (library error dismiss). */
button.dxg-case-back-link {
  background: none;
  border: 0;
  padding: 0;
  cursor: pointer;
  text-align: left;
}

/* Field-level constraint note (2026-07-29): a real capability limit stated
   next to the control it affects — never decorative explanation chrome. */
.dxg-form-note {
  font-family: var(--dxg-font-mono);
  font-size: 11px;
  color: var(--dxg-ink-mute);
}

/* Read-only related records inside a form (2026-07-30): a requester's own
   previous tickets, an asset's history — context the user reads but never
   edits. Dense table ergonomics, no card chrome, matches .dxg-form-row
   rhythm so it sits inside a form without breaking the grid. */
.dxg-related-list {
  border: 1px solid var(--dxg-hairline);
  border-radius: 3px;
  overflow-x: auto;
}

.dxg-related-list-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.78rem;
}

.dxg-related-list-head {
  font-family: var(--dxg-font-mono);
  font-size: 0.66rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--dxg-ink-mute);
  text-align: left;
  padding: 5px 8px;
  border-bottom: 1px solid var(--dxg-hairline);
  white-space: nowrap;
}

.dxg-related-list-cell {
  padding: 4px 8px;
  border-bottom: 1px solid var(--dxg-hairline);
  vertical-align: top;
}

.dxg-related-list-empty {
  font-family: var(--dxg-font-mono);
  font-size: 0.7rem;
  color: var(--dxg-ink-mute);
  padding: 6px 8px;
}

/* ---- presentation overlay extensions (TASK-34.1R, 2026-08-14) ----
 *
 * Same additive contract as the 2026-08-12 block above: every rule keys on a
 * data-dxg-* attribute the overlay emits; no overlay -> no match -> the
 * byte-identical default render. */

/* Monospace value treatment — ids, hashes, code-like values. Control only,
   never the caption. */
.dxg-form-row[data-dxg-monospace="true"] .dxg-form-control {
  font-family: var(--dxg-font-mono);
}

/* Grouped/fixed-decimal numbers: the VALUES are formatted by the renderer
   (formatting is data work CSS cannot do); the attribute earns tabular
   numerals so grouped digits line up down a column. */
.dxg-form-row[data-dxg-format-grouped] .dxg-form-control,
.dxg-form-row[data-dxg-format-decimals] .dxg-form-control {
  font-variant-numeric: tabular-nums;
}

/* Dense section: tighter row rhythm for long, scan-heavy steps. */
.dxg-form-section[data-dxg-density="dense"] {
  row-gap: 2px;
}
.dxg-form-section[data-dxg-density="dense"] .dxg-form-control {
  padding-top: 2px;
  padding-bottom: 2px;
}

/* Banded line-item table rows — same even-row tint the banded section uses. */
.dxg-form-lineitems-table[data-dxg-banded-rows="true"] .dxg-form-lineitems-row:nth-of-type(even) > * {
  background: color-mix(in srgb, var(--dxg-ink) 4%, transparent);
}

/* A row matched by a highlight rule. Tint only — highlighting flags a value
   for attention, it never changes meaning; red stays reserved for
   required/blocked. */
.dxg-form-lineitems-row[data-dxg-highlight="true"] > * {
  background: color-mix(in srgb, var(--dxg-accent, var(--dxg-ink)) 9%, transparent);
}

/* Sticky action bar: pinned to the viewport bottom while the form scrolls. */
.dxg-actionbar-root[data-dxg-action-position="sticky_bottom_bar"] {
  position: sticky;
  bottom: 0;
  background: var(--dxg-paper, inherit);
  border-top: 1px solid var(--dxg-hairline);
  padding-top: 8px;
  padding-bottom: 8px;
  z-index: 2;
}

