/* Aspen Default Styles
   Loads last in the theme cascade (ScreenThemeResource sequenceNum=40), after
   bootstrap.min.css (3), BasaltDefault.css (30). Overrides and additions only —
   never duplicate rules or values from earlier layers.

   Governed by the Aspen UI/UX Contract: runtime/component/aspen/design/DESIGN.md
   (color/typography values are documented there; every documented color token
   has a matching --aspen-* variable here — contract v1.5.0).

   Patterns promoted through the contract amendment process get their classes
   here, plus a preview card in design/components/ and a DESIGN.md entry — all
   in the same PR. */

/* The full token set from DESIGN.md § Color tokens; every documented token has
   a variable so var() references always resolve. Values are the canonical token
   values documented in the contract; do not fork them. */
:root {
    --aspen-brand: #5876A6;
    --aspen-brand-hover: #465F85;
    --aspen-bg: #FFF6E8;
    --aspen-header: #EBE0D0;
    --aspen-stripe: #C6D7EC;
    --aspen-stripe-hover: #A9C3E3;
    --aspen-stripe-hover-odd: #E5EAF1;
    --aspen-th-bg: #EEEEEE;
    --aspen-th-border: #BBBBBB;
    --aspen-text: #222222;
    --aspen-muted: #A0A0A0;
    --aspen-panel-border: #DDDDDD;
    --aspen-divider: #E5E5E5;
    --aspen-tab-active: #E8F1FF;
    --aspen-danger: #D9534F;
    --aspen-success: #DFF0D7;
    --aspen-success-border: #D6E9C6;
    --aspen-navbar: #0C1B29;
}

/* ==========================================================================
   General utilities
   ========================================================================== */

/* BasaltDefault.css pins i.mdi to 12px; the vendored MDI stylesheet itself uses
   font-size:inherit. Restore inheritance so mdi scales with the surrounding text
   the way .glyphicon does (contract v1.5.0 — mixed icon families on dashboard
   buttons). Every current mdi-floppy sits inside a .btn-sm (12px), so those
   render unchanged. */
i.mdi { font-size: inherit; }

/* Section-level note: a de-emphasized, normal-weight inline label placed next to
   a box-header title to annotate the whole section (e.g. a "Direct Ship — no tags"
   qualifier on the header instead of repeating a badge on every row). The
   "Dense but calm" alternative to a per-row badge — one indicator per section.
   Spacing is intrinsic left padding (inline-block) so the gap holds even when the
   header title emits no trailing whitespace before it. */
.aspen-section-note { display: inline-block; padding-left: 12px; font-weight: normal; font-size: 13px; color: var(--aspen-muted); }

/* Grouped form-list totals (DESIGN.md § Tables → Totals). Reusable row classes that implement the
   already-documented totals styling for grids whose subtotal/grand-total rows are synthetic — e.g. a
   live-recalc entry grid whose sums are maintained client-side and so can't use the stock form-list
   show-total row. Assign the class to the row's <tr>; cells inherit the treatment.
   - subtotal: keep the row's normal alternating stripe, bold black text.
   - grand total: table-header gray background, bold black text, 2px th-border top rule. */
tr.aspen-grid-subtotal > td { font-weight: 700; color: var(--aspen-text); }
tr.aspen-grid-total > td { color: var(--aspen-text); font-weight: 700; border-top: 2px solid var(--aspen-th-border); }
/* The grand-total background must beat the zebra-stripe cascade, whose selector
   (.bg-light .table-striped>tbody>tr:nth-child(odd)>td) is specificity (0,0,3,3). Match that
   specificity here; AspenDefault.css loads after BasaltDefault.css so it wins the grand-total row. */
.bg-light .table-striped > tbody > tr.aspen-grid-total > td { background-color: var(--aspen-th-bg); }
/* Same problem one level down, found when card #1037 became this class's first real user: the 2px rule
   above is (0,0,1,2) and loses to Bootstrap's own .table>tbody>tr>td border-top (0,0,1,3), so the row
   drew the stock 1px #ddd rule instead of the 2px th-border the contract specifies. Re-stated here at
   (0,0,2,3) so the documented border actually renders. */
.table > tbody > tr.aspen-grid-total > td { border-top: 2px solid var(--aspen-th-border); }

/* ==========================================================================
   Boxes — Summary strip (DESIGN.md § Boxes → Summary strip; card #1152)
   A box that totals a few measures per entity lays the entities out side by
   side as blocks. The strip may carry the panel-body class itself (preview
   markup) or sit inside a box-body's panel-body (container style= in screen
   XML); both lose the 7px body padding.
   ========================================================================== */
.aspen-summary-strip {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(260px, 1fr));
    gap: 0;
    overflow-y: auto;
    /* Three rows of blocks, then scroll. Every block in a strip carries the same
       measures, so a row's height is known from its line count: 16px padding,
       the name (24px min, 4px padding, 1px rule, 2px margin) and one line plus
       a 3px gap per measure. The line count (1-4) is read off the blocks below;
       3 is the fallback where :has() is unsupported. */
    --aspen-summary-lines: 3;
    max-height: calc(3 * (18px + max(24px, 1.4286em + 5px) + var(--aspen-summary-lines) * (1.4286em + 3px)));
    max-height: calc(3 * (18px + max(24px, 1lh + 5px) + var(--aspen-summary-lines) * (1lh + 3px)));
}
.aspen-summary-strip:has(.aspen-summary-line) { --aspen-summary-lines: 1; }
.aspen-summary-strip:has(.aspen-summary-line + .aspen-summary-line) { --aspen-summary-lines: 2; }
.aspen-summary-strip:has(.aspen-summary-line + .aspen-summary-line + .aspen-summary-line) { --aspen-summary-lines: 3; }
.aspen-summary-strip:has(.aspen-summary-line + .aspen-summary-line + .aspen-summary-line + .aspen-summary-line) { --aspen-summary-lines: 4; }
.panel-body.aspen-summary-strip { padding: 0; }
.panel-body > .aspen-summary-strip { margin: -7px; }
/* Bootstrap's panel-body clearfix pseudo-elements would become empty grid cells. */
.aspen-summary-strip::before, .aspen-summary-strip::after { content: none; display: none; }
.aspen-summary-block {
    display: flex;
    flex-direction: column;
    gap: 3px;
    padding: 8px 14px;
    border-right: 1px solid var(--aspen-panel-border);
}
.aspen-summary-total { font-weight: 700; background-color: var(--aspen-tab-active); }
.aspen-summary-name {
    font-weight: 700;
    min-height: 24px;
    padding-bottom: 4px;
    margin-bottom: 2px;
    border-bottom: 1px solid var(--aspen-panel-border);
}
.aspen-summary-name a { color: var(--aspen-brand); }
.aspen-summary-line {
    display: flex;
    justify-content: space-between;
    gap: 12px;
    font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   Dashboard patterns — Charts and Stat Cards (DESIGN.md § Charts and Stat Cards)
   Reusable across data-bearing dashboards. Wrap the dashboard body in
   .aspen-dashboard; compose the stat-card row, single-metric bar chart, and
   leaderboard from the classes below. First shipped: Sales dashboard.
   ========================================================================== */

/* Dashboard chrome: neutralize the stock collapsible-panel affordances on a
   data dashboard (headers are labels, not toggles) and bold the box titles. */
.aspen-dashboard .panel-heading { pointer-events: none; }
.aspen-dashboard .panel-heading .panel-toolbar { pointer-events: auto; }
.aspen-dashboard .panel-heading h5 .fa { display: none; }
.aspen-dashboard .panel-heading h5 { color: var(--aspen-text); font-weight: 700; }
/* Header-less box (e.g. the quick-nav column block): hide its empty tan bar. */
.aspen-dashboard .aspen-navcol-box .panel-heading { display: none; }

/* Stat cards: a responsive row of KPI tiles. Cream tile with a 1px panel border
   that turns brand blue on hover, brand-blue value number, bold black Title-Case
   title, muted sub. The whole tile may be a link (click-through). Add .warn to
   the tile to flag an alert: the value text goes danger red, border/bg unchanged. */
.aspen-stat-row { display: flex; flex-wrap: wrap; gap: 20px; margin-bottom: 22px; }
.aspen-stat-card { flex: 1 1 200px; display: block; background: var(--aspen-bg); border: 1px solid var(--aspen-panel-border); border-radius: 4px; padding: 14px 16px; color: var(--aspen-brand); text-decoration: none; }
.aspen-stat-card:hover { border-color: var(--aspen-brand); }
.aspen-stat-title { font-size: 12px; color: var(--aspen-text); font-weight: 700; }
.aspen-stat-value { font-size: 30px; font-weight: 700; color: var(--aspen-brand); line-height: 1.2; margin: 8px 0 4px 0; }
/* Trailing unit (e.g. "CTNs") sits inline after the value; sized to sit proportionally
   under the 30px value (was 24px against the former 44px value — scaled with it). */
.aspen-stat-unit { font-size: 16px; font-weight: 700; }
.aspen-stat-sub { font-size: 12px; color: var(--aspen-muted); }
.aspen-stat-card.warn .aspen-stat-value { color: var(--aspen-danger); }

/* Signed values (DESIGN.md § Signed values). Color a number by its own sign — brand blue
   for zero-or-positive, danger red for negative. Assigned at render time in form-list cells,
   stat values, and the JS-built AFS/dashboard tooltip + detail tables (the sanctioned
   render-mode exception) so no screen carries an inline hex for a signed value. */
.aspen-val-pos { color: var(--aspen-brand); }
.aspen-val-neg { color: var(--aspen-danger); }
/* Static brand value — emphasis for a value whose sign carries no meaning (ids, product
   labels, read-only summary values in dialogs and JS-built panels). Use instead of inline
   hex or .aspen-val-pos, which asserts sign semantics. */
.aspen-text-brand { color: var(--aspen-brand); }
/* Error text on the danger token (card 916): inline validation/error messages in dialogs and
   JS-built panels. Bootstrap's .text-danger is an off-token red (#a94442) — use this instead.
   Spec half owed to the contract via Trello (same bundle as the AFS class codification card). */
.aspen-text-danger { color: var(--aspen-danger); }

/* AFS grid support (basalt Sales Order AFS tab). The hover tooltip is a single detached
   node on document.body, so its chrome lives here as one class rather than inline cssText;
   base text is the text token so row labels read black and only signed values (.aspen-val-*)
   carry color. Neutral tooltip chrome (white surface, gray hairlines) stays literal — there
   is no token for it and it never varies. `.aspen-afs-cell` marks a clickable grid cell;
   `.aspen-afs-ctn` is the smaller-font carton sub-row. */
.aspen-afs-tip { display: none; position: fixed; z-index: 999999; background: #FFFFFF; border: 1px solid #888; padding: 4px 8px; box-shadow: 1px 1px 4px rgba(0,0,0,.25); font-size: 12px; color: var(--aspen-text); pointer-events: none; font-family: Arial, Helvetica, sans-serif; }
.aspen-afs-cell { cursor: pointer; position: relative; }
.aspen-afs-ctn { font-size: 0.95em; }

/* Two-column dashboard shape (DESIGN.md § Dashboard layout). Data dashboards use a
   fixed-width left rail holding only the operational button panel, and a flex-fill
   right column carrying the KPI stat-card row on top and widgets stacked 16px below.
   Button-menu-only dashboards keep the stock single-column pattern and don't use these.
   Rail width is a fixed em value (not a % of viewport) so buttons don't stretch wide
   on large screens; it can still shrink below that on narrow viewports. */
.aspen-dash-2col { display: flex; gap: 16px; align-items: flex-start; }
.aspen-dash-rail { flex: 0 1 280px; }
.aspen-dash-main { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 16px; }
/* Below 768px (no other breakpoint is established elsewhere in this contract, so this
   is the first) the rail can no longer sit beside the main column at a readable width,
   so the two stack: rail's button panel on top, KPI/widget column below. */
@media (max-width: 768px) {
  .aspen-dash-2col { flex-direction: column; }
  .aspen-dash-rail { flex: 1 1 auto; width: 100%; }
}
/* Inside the two-column shape the column's own 16px gap provides the spacing, so the
   stat row's standalone 22px bottom margin would double-stack — zero it here rather
   than inline on each dashboard (the single-column use keeps its 22px). */
.aspen-dash-main .aspen-stat-row { margin-bottom: 0; }
/* Rail panel: a cream, 1px-bordered card with no title bar and no collapse control,
   holding labeled groups of stacked full-width primary buttons. 20px gap separates
   the groups; buttons stretch full-width (color comes from bootstrap/Basalt btn-primary,
   not restated here) with 8px between a label and its buttons and between buttons. */
.aspen-dash-railpanel { background: var(--aspen-bg); border: 1px solid var(--aspen-panel-border); border-radius: 4px; padding: 14px; display: flex; flex-direction: column; gap: 20px; }
.aspen-dash-rail-label { font-size: 16px; font-weight: 500; }
.aspen-dash-railpanel .btn { display: block; width: 100%; margin-top: 8px; }
/* The framework's link macro omits the space between icon and text for disabled
   links (permission-gated buttons render <i>icon</i>text with no separator, vs
   <i>icon</i> text for enabled ones) — DefaultScreenMacros.html.ftl's disableLink
   branch. Patch it here rather than in the shared macro, which every disabled
   link app-wide goes through. */
.aspen-dash-railpanel .btn.disabled .glyphicon,
.aspen-dash-railpanel .btn.disabled i.mdi { margin-right: 6px; }
/* Planned rail button (DESIGN.md § Buttons, contract 1.11.0): a stock label widget styled
   "btn aspen-btn-planned" for a screen that isn't built yet — a span, so nothing to
   click. Unfilled (bg ground, 1px panel-border, muted icon and text), no hover change,
   default cursor. Geometry replicates the rail's btn-sm links (the link macro emits
   btn btn-primary btn-sm; Basalt widens btn-sm's left padding to 9px) so a rail column
   stays even when live and planned buttons are mixed. Right padding reserves room for
   the trailing "Coming Soon" tag, which sits at the button's right edge. */
.aspen-btn-planned { position: relative; display: block; width: 100%; background: var(--aspen-bg); border: 1px solid var(--aspen-panel-border); color: var(--aspen-muted); cursor: default; padding: 5px 88px 5px 9px; font-size: 12px; line-height: 1.5; border-radius: 3px; text-align: center; white-space: normal; }
.aspen-btn-planned:hover, .aspen-btn-planned:focus, .aspen-btn-planned:active { background: var(--aspen-bg); border-color: var(--aspen-panel-border); color: var(--aspen-muted); box-shadow: none; outline: none; }
.aspen-btn-planned .glyphicon, .aspen-btn-planned i.mdi { color: var(--aspen-muted); margin-right: 6px; }
.aspen-btn-planned-tag { position: absolute; right: 10px; top: 50%; transform: translateY(-50%); font-size: 11px; font-weight: 400; color: var(--aspen-muted); white-space: nowrap; }

/* Bar chart: a simple single-metric bar chart with a nice y-axis, a $/# style
   toggle, and a per-bar hover that darkens to brand-hover and shows the exact
   value in a tooltip above the bar. Bars/axis/tooltip are built by the screen's
   renderChart JS using these classes. */
/* Widget toolbar controls (DESIGN.md § Charts and stat cards, contract 1.11.0). The
   dropdown is a stock drop-down (select2) carrying .aspen-chart-select; the select2
   container it renders is the element that actually shows, so the spacing that clears
   the box edge is applied to both the raw select and its select2 sibling. */
.aspen-chart-select { display: inline-block; width: auto; max-width: 220px; margin: 0 10px 0 4px; }
select.aspen-chart-select + span.select2.select2-container { margin: 0 10px 0 4px; vertical-align: middle; }
/* The stock drop-down lives in a form-single, which the form macro renders as
   form > fieldset.form-horizontal > .form-group > .col-sm-12 > .big-row-item > .form-group —
   a block chain that pushes the toggle group beside it onto a second line. Inside a
   widget box-toolbar flatten that chain to inline so the dropdown and the toggle sit on
   one line with the box title, vertically centred and clear of the box edge. */
.aspen-dashboard .panel-toolbar > form,
.aspen-dashboard .panel-toolbar > form > fieldset.form-horizontal,
.aspen-dashboard .panel-toolbar > form .form-group,
.aspen-dashboard .panel-toolbar > form .col-sm-12,
.aspen-dashboard .panel-toolbar > form .big-row-item { display: inline-block; vertical-align: middle; float: none; width: auto; margin: 0 !important; padding: 0; }
/* Joined segmented toggle: ONE rounded brand outline around the group (inline-flex, so
   the inline whitespace between the buttons collapses and there is no gap), overflow
   hidden so only the outer corners are rounded; each segment is borderless with
   standard btn-sm padding (5px 10px / 12px), so the group reads as a single control
   rather than two separately bordered boxes. */
.aspen-toggle-group { display: inline-flex; align-items: stretch; border: 1px solid var(--aspen-brand); border-radius: 4px; overflow: hidden; white-space: nowrap; margin-right: 8px; vertical-align: middle; }
.aspen-toggle { border: none; border-radius: 0; margin: 0; background: var(--aspen-bg); color: var(--aspen-brand); padding: 5px 10px; font-size: 12px; line-height: 1.5; font-weight: 700; }
.aspen-toggle:focus { outline: none; }
.aspen-toggle-on { background: var(--aspen-brand); color: #fff; }
.aspen-barchart-body { display: flex; align-items: stretch; padding: 4px 2px; }
.aspen-barchart-yaxis { display: flex; flex-direction: column; justify-content: space-between; align-items: flex-end; padding: 0 8px 18px 0; font-size: 11px; color: var(--aspen-muted); }
.aspen-barchart-plot { position: relative; flex: 1 1 auto; display: flex; align-items: flex-end; gap: 6px; border-left: 1px solid var(--aspen-th-border); border-bottom: 1px solid var(--aspen-th-border); padding: 0 4px; }
.aspen-barchart-col { flex: 1 1 0; display: flex; flex-direction: column; align-items: center; min-width: 0; cursor: pointer; }
.aspen-barchart-slot { display: flex; align-items: flex-end; height: 210px; width: 100%; }
.aspen-barchart-bar { width: 100%; background: var(--aspen-brand); border-radius: 3px 3px 0 0; min-height: 1px; transition: background .12s ease; }
.aspen-barchart-col:hover .aspen-barchart-bar { background: var(--aspen-brand-hover); }
.aspen-barchart-label { font-size: 11px; color: var(--aspen-muted); padding-top: 4px; white-space: nowrap; }
.aspen-barchart-col:hover .aspen-barchart-label { color: var(--aspen-text); font-weight: 700; }
.aspen-barchart-tip { display: none; position: absolute; background: var(--aspen-brand-hover); color: #fff; font-size: 12px; font-weight: 700; padding: 5px 10px; border-radius: 4px; white-space: nowrap; pointer-events: none; z-index: 10; }
.aspen-barchart-tip:after { content: ""; position: absolute; top: 100%; left: var(--aspen-arrow-left, 50%); transform: translateX(-50%); width: 0; height: 0; border-left: 6px solid transparent; border-right: 6px solid transparent; border-top: 6px solid var(--aspen-brand-hover); }

/* Category bar chart (card #1051, Production dashboard; spec half owed to the contract via Trello). A variant of
   the bar chart above for a handful of named categories on a fixed scale: add .aspen-barchart-category to
   .aspen-barchart-body. The plot and a .aspen-barchart-labelrow of bold category names under the axis baseline
   stack in .aspen-barchart-plotcol; bars are capped at 96px wide with each bar's value printed above it
   (.aspen-barchart-value); .aspen-barchart-gridline rules sit behind the bars at the tick values, and the script
   places each tick label on its gridline (no label at zero). 40px of headroom keeps the hover tooltip clear of
   the tallest bar and its value. Every rule that restyles an existing piece is scoped to the modifier, so the
   single-metric chart is unchanged. Gridline and tick offsets are set from script, like the bar heights. */
.aspen-barchart-category .aspen-barchart-yaxis { position: relative; display: block; flex: 0 0 40px; padding: 0; }
.aspen-barchart-category .aspen-barchart-yaxis > span { position: absolute; right: 8px; transform: translateY(50%); line-height: 1; }
.aspen-barchart-plotcol { flex: 1 1 auto; display: flex; flex-direction: column; min-width: 0; }
.aspen-barchart-category .aspen-barchart-plot { padding-top: 40px; }
.aspen-barchart-category .aspen-barchart-col { position: relative; z-index: 1; }
.aspen-barchart-category .aspen-barchart-slot { justify-content: center; }
.aspen-barchart-category .aspen-barchart-bar { position: relative; max-width: 96px; }
.aspen-barchart-gridline { position: absolute; left: 0; right: 0; border-top: 1px solid var(--aspen-divider); pointer-events: none; z-index: 0; }
.aspen-barchart-value { position: absolute; bottom: 100%; left: -6px; right: -6px; text-align: center; font-size: 11px; font-weight: 700; color: var(--aspen-text); padding-bottom: 3px; white-space: nowrap; }
.aspen-barchart-col:hover .aspen-barchart-value { color: var(--aspen-brand-hover); }
/* Label row: same 4px side padding and 6px gap as the plot (plus the plot's 1px axis line) so each name centres
   under its bar. A name hovers with its bar (.is-hover, set by script) and is itself a click target. */
.aspen-barchart-labelrow { display: flex; gap: 6px; padding: 0 4px 0 5px; }
.aspen-barchart-labelrow > .aspen-barchart-label { flex: 1 1 0; min-width: 0; text-align: center; cursor: pointer; font-weight: 700; color: var(--aspen-text); overflow: hidden; text-overflow: ellipsis; }
.aspen-barchart-labelrow > .aspen-barchart-label:hover,
.aspen-barchart-labelrow > .aspen-barchart-label.is-hover { color: var(--aspen-brand-hover); }
/* Empty state when no category has data for the selected range. */
.aspen-barchart-empty { color: var(--aspen-muted); padding: 8px 4px; }

/* Leaderboard: ranked rows with a name, a brand-blue progress fill on a
   stripe-blue track, and a right-aligned amount. Scrolls within a fixed height. */
.aspen-leaderboard-scope { font-size: 11px; color: var(--aspen-muted); letter-spacing: .05em; }
.aspen-leaderboard { max-height: 300px; overflow-y: auto; }
.aspen-leaderboard-row { display: flex; align-items: center; gap: 10px; padding: 7px 4px; border-bottom: 1px solid var(--aspen-header); }
.aspen-leaderboard-rank { flex: 0 0 18px; text-align: right; color: var(--aspen-muted); font-size: 12px; }
.aspen-leaderboard-main { flex: 1 1 auto; min-width: 0; }
.aspen-leaderboard-name { display: block; font-weight: 600; color: var(--aspen-text); font-size: 13px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.aspen-leaderboard-track { display: block; background: var(--aspen-stripe); height: 8px; border-radius: 4px; margin-top: 3px; }
.aspen-leaderboard-fill { display: block; background: var(--aspen-brand); height: 8px; border-radius: 4px; }
.aspen-leaderboard-amt { flex: 0 0 auto; font-weight: 700; color: var(--aspen-text); font-size: 13px; }
.aspen-leaderboard-empty { color: var(--aspen-muted); padding: 8px 4px; }
/* Footer link under the ranked rows (e.g. "View all customers") — its own line with
   breathing room above, so it reads as the list's footer rather than a sixth row. */
.aspen-leaderboard-viewall { display: block; margin-top: 10px; }

/* Job progress: a determinate bar for an asynchronous batch job — commodity product
   creation and Delete Unused Products (card 1020) — fed by ReportProgressPercentage
   notifications over the websocket. Same brand-fill-on-a-stripe-track idiom as the
   leaderboard; the caption below names the operation and the percentage. Hidden via the
   `hidden` attribute until the first notification arrives (or on render when a run is
   already in flight). The fill's width is the one thing JS sets inline, because it is the
   live value, not a style choice. Deliberately does NOT reuse Bootstrap's .progress /
   .progress-bar-message: BasaltDefault.css styles the latter as fixed-position white
   overlay text for the Payroll bar, which is wrong for an in-panel bar. */
.aspen-job-progress { margin-bottom: 10px; }
.aspen-job-progress[hidden] { display: none; }
.aspen-job-progress-track { display: block; background: var(--aspen-stripe); height: 10px; border-radius: 5px; overflow: hidden; }
.aspen-job-progress-fill { display: block; background: var(--aspen-brand); height: 10px; border-radius: 5px; width: 0; transition: width .2s ease; }
.aspen-job-progress-message { font-size: 12px; color: var(--aspen-muted); padding-top: 3px; }

/* Nav columns: equal-width columns of uniform full-width brand-blue buttons,
   for the all-users quick-nav strip at the foot of a dashboard. Neutralizes the
   Bootstrap grid floats the stock link widget emits so buttons stack full-width. */
.aspen-navcol-grid { display: flex; flex-wrap: wrap; gap: 24px; }
.aspen-navcol { flex: 1 1 0; min-width: 180px; }
.aspen-navcol h5 { font-size: 16px; font-weight: 700; color: var(--aspen-text); margin: 0 0 18px 0; display: block !important; text-align: left; }
.aspen-navcol .row { margin-left: 0; margin-right: 0; }
.aspen-navcol .row [class*="col-"] { float: none; width: 100%; padding-left: 0; padding-right: 0; }
.aspen-navcol a, .aspen-navcol .btn { display: block; width: 100%; box-sizing: border-box; margin: 0 0 10px 0; padding: 11px 16px; background: var(--aspen-brand); color: #fff; border: 1px solid var(--aspen-brand); border-radius: 4px; text-align: center; font-weight: 500; text-decoration: none; }
.aspen-navcol a:hover, .aspen-navcol .btn:hover { background: var(--aspen-brand-hover); border-color: var(--aspen-brand-hover); color: #fff; }

/* ==========================================================================
   AFS filter row and view options
   ========================================================================== */

/* The AFS saved-view filter grid and its Display & Calc option list carried every
   rule in a style attribute — `.afs-option-row` and the filter table were class
   names with no stylesheet behind them, so each header cell, select and checkbox
   row repeated its own inline declarations. Card 893 replaced the UofM / UofM Group
   filter columns with a single Packaging Type column, which meant touching that
   markup; these classes give the row one definition instead of adding a seventh
   copy of the inline styling.

   Not a data grid: the filter table is a row of input controls, so it deliberately
   does NOT take the striped `.table` treatment used for result grids. Header cells
   use the muted token rather than the off-palette #555 they carried inline. */
.aspen-afs-filter-table { width: 100%; border-collapse: collapse; }
.aspen-afs-filter-table thead tr { border-bottom: 1px solid var(--aspen-panel-border); text-align: left; }
.aspen-afs-filter-table th { padding: 4px; font-size: 11px; font-weight: normal; color: var(--aspen-muted); }
.aspen-afs-filter-table td { padding: 4px; }
.aspen-afs-filter-table th.aspen-afs-filter-action { width: 30px; }
.aspen-afs-filter-select { width: 100%; font-size: 11px; padding: 2px; }

/* One option per row in the AFS view-options dialog: a normal-weight label with an
   enlarged checkbox, matching the dialog's existing scale. */
.aspen-afs-option-row { margin-bottom: 8px; }
.aspen-afs-option-row label { font-weight: normal; font-size: 15px; }
.aspen-afs-option-row input[type="checkbox"],
.aspen-afs-option-row input[type="radio"],
.aspen-afs-option-label input[type="radio"] { transform: scale(1.5); margin-right: 12px; }
/* Same label treatment for an option that sits outside a row wrapper (the "Specific Dates:"
   radio that is followed inline by its own date inputs). */
.aspen-afs-option-label { font-weight: normal; font-size: 15px; }
/* Wider gap for an option group that stands apart from the checkbox/radio stack
   (a labelled input rather than a toggle). */
.aspen-afs-option-row.aspen-afs-option-group { margin-bottom: 15px; }
.aspen-afs-option-row.aspen-afs-option-spaced { margin-top: 15px; }

/* Unit sub-header inside the AFS Details popup's summary table: the small centred
   caption naming the Actual and Equivalent column groups' packaging types (card 893). */
.aspen-afs-unit-head { text-align: center; font-size: 11px; border-bottom: 1px solid var(--aspen-th-border); padding-bottom: 2px; }

/* Footnote row inside the AFS summary tables (card 895's all-dates disclosure). A td version
   of the section-note treatment: .aspen-section-note is display:inline-block, which on a table
   cell breaks it OUT of table layout — the cell collapses to word width and the note stacks in
   a narrow column under the table. This keeps the cell a table cell. */
td.aspen-afs-note { text-align: left; font-size: 11px; font-weight: normal; color: var(--aspen-muted); white-space: normal; padding-top: 3px; }

/* Drag-to-reorder handle on the Sales Order Detail Product tab (card #904): the grip a user
   drags to re-arrange order lines (jQuery UI Sortable posts the new order server-side).
   NEW PATTERN — no contract section covers drag affordances yet; spec-half amendment card
   for Edward/Ian is raised with the ship (dev-half only here, per the amendment workflow). */
.aspen-drag-handle { cursor: grab; color: var(--aspen-muted); padding: 0 4px; display: inline-block; }
.aspen-drag-handle:active { cursor: grabbing; }
/* the row while it is being dragged (jQuery UI helper clone) */
tr.ui-sortable-helper { background: var(--aspen-stripe); }

/* ==========================================================================
   Status stepper (card #676) — horizontal lifecycle rail rendered by basalt
   template/basic/StatusWidgets.xml § StatusStepperSection (which builds the
   markup; consumers only set context inputs and section-include it).
   NEW PATTERN — no contract section covers status steppers yet; spec-half
   amendment card for Edward/Ian is raised with the ship (dev-half only here,
   per the amendment workflow). Existing tokens only: brand for reached steps,
   danger for the cancelled marker/badge, muted + panel-border for future
   steps and the dimmed terminated rail.
   ========================================================================== */
.aspen-stepper { margin: 2px 0 10px 0; }
.aspen-stepper-badgerow { display: flex; justify-content: flex-end; margin-bottom: 4px; }
.aspen-stepper-badge { font-size: 12px; font-weight: 700; padding: 2px 10px; border-radius: 10px; border: 1px solid; }
.aspen-stepper-badge.is-danger { color: var(--aspen-danger); border-color: var(--aspen-danger); background: transparent; }
.aspen-stepper-badge.is-note { color: var(--aspen-text); border-color: var(--aspen-th-border); background: var(--aspen-header); }
/* rail: steps keep their size, connectors flex; horizontal scroll inside the box is the
   narrow-width fallback (never the page). Vertical padding leaves room for the current
   step's outline ring, which overflow-x would otherwise clip. */
.aspen-stepper-rail { display: flex; align-items: flex-start; overflow-x: auto; padding: 5px 2px; }
.aspen-stepper-step { display: flex; flex-direction: column; align-items: center; flex: 0 0 auto; min-width: 44px; }
.aspen-stepper-dot { width: 26px; height: 26px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 12px; font-weight: 700; box-sizing: border-box; }
.aspen-stepper-label { margin-top: 4px; font-size: 11px; max-width: 76px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
/* connector centered on the 26px dot: 12px top margin + 2px border ≈ dot midline */
.aspen-stepper-line { flex: 1 1 24px; min-width: 10px; height: 0; border-top: 2px solid var(--aspen-panel-border); margin-top: 12px; }
.aspen-stepper-line.is-done { border-top-color: var(--aspen-brand); }
.aspen-stepper-step.is-done .aspen-stepper-dot { background: var(--aspen-brand); border: 2px solid var(--aspen-brand); color: #fff; }
.aspen-stepper-step.is-done .aspen-stepper-label { color: var(--aspen-brand); }
.aspen-stepper-step.is-current .aspen-stepper-dot { background: var(--aspen-brand); border: 2px solid var(--aspen-brand); color: #fff; outline: 2px solid var(--aspen-brand); outline-offset: 2px; }
.aspen-stepper-step.is-current .aspen-stepper-label { color: var(--aspen-brand); font-weight: 700; }
.aspen-stepper-step.is-todo .aspen-stepper-dot { background: transparent; border: 2px solid var(--aspen-panel-border); color: var(--aspen-muted); }
.aspen-stepper-step.is-todo .aspen-stepper-label { color: var(--aspen-muted); }
.aspen-stepper-step.is-cancelled .aspen-stepper-dot { background: var(--aspen-danger); border: 2px solid var(--aspen-danger); color: #fff; }
.aspen-stepper-step.is-cancelled .aspen-stepper-label { color: var(--aspen-danger); font-weight: 700; }
/* terminated (cancelled/rejected): completed steps and their connectors dim to muted;
   the is-cancelled marker keeps its danger red */
.aspen-stepper.is-terminated .aspen-stepper-step.is-done .aspen-stepper-dot { background: var(--aspen-muted); border-color: var(--aspen-muted); }
.aspen-stepper.is-terminated .aspen-stepper-step.is-done .aspen-stepper-label { color: var(--aspen-muted); }
.aspen-stepper.is-terminated .aspen-stepper-line.is-done { border-top-color: var(--aspen-muted); }

/* Status-change action row under the rail: back/forward buttons lead, the destructive
   cancel hugs the right edge. Hidden-form links render an empty <form> beside (or around)
   each button; display:contents promotes the buttons themselves to flex items so the gap
   spacing stays even and margin-left:auto works on the cancel button. */
.aspen-stepper-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: 2px 0 10px 0; }
.aspen-stepper-actions > form { display: contents; }
.aspen-stepper-actions .aspen-stepper-cancel { margin-left: auto; }

/* Math-line entry columns with a trailing % toggle (cards #920/#945): the toggle sits to the
   right of the input, so a right-aligned column heading would land over the button instead of
   the field the user types into. This offsets the heading left by the button's footprint
   (btn padding + border + the 6px flex gap). Applied via header-field container-style. */
.aspen-mathline-num-header { padding-right: 42px; }

/* Row actions as ONE right-aligned trailing cell (card #1052, decided on #1022 / #941).
   Applied to the trailing action cell via the field's container-style, so it covers any grid
   using the row-actions pattern rather than one screen's markup.

   The uneven gap it fixes: the two dialog macros emit different amounts of inline content.
   dynamic-dialog defers its modal to the after-screen writer and leaves only a <button>, while
   container-dialog emits the button AND its display:none modal div inline — so the whitespace
   between two buttons differed depending on which macro produced the left one (~4px vs ~8px).
   Flex collapses inter-element whitespace entirely and skips display:none children, so the gap
   is exactly 8px whatever mix of macros a row uses. Hidden-form wrappers get display:contents
   for the same reason .aspen-stepper-actions does: so the buttons themselves are the flex items
   and the gap stays even.

   Two things measured on the live Trouble History grid and corrected here: the row's buttons carry
   an inherited margin-right: 4px, which added to the gap and rendered 12px instead of 8px, and that
   extra width pushed the trailing money action onto a second line. Margins are zeroed so gap is the
   only spacing, and the row does not wrap — a cell short on room widens its column rather than
   stacking, which is what "one right-aligned trailing cell" means. */
.aspen-row-actions { display: flex; flex-wrap: nowrap; justify-content: flex-end; align-items: center; gap: 8px; }
.aspen-row-actions > form { display: contents; }
.aspen-row-actions > .btn, .aspen-row-actions > form > .btn { margin: 0; }

/* Read-only label/value row on one line (card #1037, Run Summary "Commodity"). A form-single display
   field in a horizontal form misaligns by 7px: Bootstrap gives the control-label 7px top padding to
   sit level with a 34px input, and the framework gives static values a matching form-control-static
   treatment (webroot-layout.css) — but BasaltDefault.css forces .text-inline to display:inline
   !important, which discards that padding, so the label lands 7px below its value. Pre-existing on
   every display field app-wide; scoped here to the row the design spec draws as a single line
   (rd-row: flex, centered, label without padding) rather than re-styling every form in one card.
   Applied via the field's container-style, which lands on the form-group. */
.aspen-static-row { display: flex; align-items: center; }
.aspen-static-row > .control-label { padding-top: 0; }

/* Link-look form button (card #1037, the shared Pool link). A <link link-type="hidden-form-link"> in a
   form field renders as a submit button, and only the framework's button.button-plain turns that into
   an anchor look — but it hard-codes Bootstrap's #428bca and inherits btn-sm's 12px, so beside a real
   anchor it reads as a different, smaller blue. Composed on btn-type="link" (no fill, no hover fill)
   and corrected here to the contract's brand color and the surrounding text size, so a Pool link in a
   grid or header form is indistinguishable from the anchors around it. Existing tokens only. */
button.button-plain.btn-link { color: var(--aspen-brand); font-size: inherit; line-height: inherit; }
button.button-plain.btn-link:hover, button.button-plain.btn-link:focus { color: var(--aspen-brand-hover, var(--aspen-brand)); text-decoration: underline; }


/* Shipment Detail tag picker (Shipping > Shipment Detail > Pick Tags dialog and the per-item pick blocks).
   Moved out of the screen's injected <style> block and inline style= attributes (#1102 contract cleanup);
   colors are tokens only. The dialog is JS-built markup (the sanctioned render-mode exception), so its
   chrome lives here rather than in a container-dialog width attribute. */
.aspen-picker-dialog { width: 900px; }
.aspen-picker-subtitle { font-weight: normal; }
.aspen-picker-open-row { margin-top: 6px; }
.aspen-picker-summary { margin: 0 0 8px 0; }
.aspen-picker-summary > [class*="col-"] { padding-left: 0; padding-right: 0; }
.aspen-picker-chip { font-size: 1.05em; padding: 3px 8px; margin-left: 4px; }
.aspen-picker-chip.label-default { background-color: var(--aspen-muted); }
.aspen-picker-chip.label-primary { background-color: var(--aspen-brand); }
.aspen-picker-zone-header { margin: 14px 0 6px 0; padding-bottom: 4px; border-bottom: 1px solid var(--aspen-panel-border); color: var(--aspen-muted); }
.aspen-picker-short-note { display: block; margin: 8px 0; padding: 8px 12px; background-color: var(--aspen-bg); border: 1px solid var(--aspen-danger); border-radius: 3px; color: var(--aspen-danger); }
.aspen-picker-short-note::before { content: "\26A0  "; }
.aspen-picker-empty { padding: 6px 0; }
.aspen-picker-search-row { display: flex; align-items: flex-end; flex-wrap: wrap; margin-bottom: 10px; }
.aspen-picker-search-row label { display: block; margin-bottom: 2px; }
.aspen-picker-search-row > .form-group + .form-group, .aspen-picker-search-row > #ptm-searchBtn { margin-left: 8px; }
.aspen-picker-spacer { flex: 1; }
.aspen-picker-paging { display: flex; align-items: center; margin-top: 8px; }
.aspen-picker-paging-numbers { margin: 0 10px; }
.aspen-picker-footer { margin-top: 8px; }
.aspen-ship-item-left { line-height: 1.6; }
.aspen-ship-item-pkg-col { padding-left: 12px; border-left: 1px solid var(--aspen-panel-border); }
.aspen-ship-item-pkg-header { display: block; color: var(--aspen-muted); margin-bottom: 4px; }
.aspen-ship-item-pkg-row { padding: 2px 0; }
.aspen-ship-item-picked-header { display: block; color: var(--aspen-muted); margin: 8px 0 4px; }

/* Criteria-builder look on a stock form-list (form-list style="aspen-criteria-list"; Simple Report Filters and Filter the
   Totals, card "Redesign Builder Filters, Ask When Run, and Match Any"): not striped, no row hover, muted non-bold header
   cells, like .aspen-afs-filter-table but for a server-rendered list. Contract amendment pending (DESIGN.md Tables). */
.bg-light .table-striped.aspen-criteria-list>tbody>tr>td,
.bg-light .table-striped.aspen-criteria-list>tbody>tr:not(.first):not(.second):nth-child(odd):hover>td,
.bg-light .table-striped.aspen-criteria-list>tbody>tr:not(.first):not(.second):nth-child(even):hover>td { background-color: transparent !important; }
.aspen-criteria-list>thead>tr>th { font-weight: normal; color: var(--aspen-muted); }
/* a number value input in a form-list cell (container-style="aspen-num-input") is right-aligned, like input.number */
.aspen-num-input input.form-control { text-align: right; }
