/* field -> index mapping (shared by the one label cell and every item's
   value cell for that field, which is what guarantees alignment). This is
   a plain index, not literally "grid-row" -- the rules further down that
   consume --field-idx decide whether it drives grid-row (vertical view,
   the default) or grid-column (horizontal view, via .view-horizontal on
   #items-grid), which is what lets the same markup support both view
   modes without a second template. */
[data-row="remove"] { --field-idx: 1; }
[data-row="customer"] { --field-idx: 3; }
[data-row="item"] { --field-idx: 4; }
/* field-idx 5 is intentionally unused -- the read-only FOB recap used to
   be its own row here (data-row="sourcing_summary"), but now lives inline
   in the Item row's caption line instead (see .item-caption-row), right
   next to the product-name caption. The actual editable FOB/Ship Method/
   Incoterm/Supplier controls live further down as the first rows of
   PRODUCT COST (field-idx 15-18), right above Costs ($). Kept in sync by
   syncSourcingSummary() in app.js. */
/* PRICING sits right after the identity block, ahead of INPUTS -- the
   outcome (sell price, retail, RF NM%) reads before any input field
   instead of after them. INPUTS still appears earlier in this file/the
   template's DOM; that's fine; grid position is driven entirely by
   --field-idx (see the note above this block), not by markup order. */
[data-row="section_pricing"] { --field-idx: 6; }
/* the 6 pricing outputs used to be 6 separate rows -- they're now one
   merged "pricing_stats" row (a 2-column stat grid inside one cell, see
   templates/index.html). */
[data-row="pricing_stats"] { --field-idx: 7; }
[data-row="section_inputs"] { --field-idx: 8; }
[data-row="mode"] { --field-idx: 9; }
[data-row="sell_price"] { --field-idx: 10; }
[data-row="retail_mode"] { --field-idx: 11; }
[data-row="retail_price"] { --field-idx: 12; }
[data-row="landed_estimate"] { --field-idx: 13; }
[data-row="section_product_cost"] { --field-idx: 14; }
[data-row="fob"] { --field-idx: 15; }
[data-row="ship_method"] { --field-idx: 16; }
[data-row="incoterm"] { --field-idx: 17; }
[data-row="supplier"] { --field-idx: 18; }
[data-row="product_cost"] { --field-idx: 19; }
[data-row="adders"] { --field-idx: 20; }
[data-row="out_ocean_freight_unit"] { --field-idx: 21; }
[data-row="out_tariff_duty"] { --field-idx: 22; }
[data-row="out_allocations"] { --field-idx: 23; }
[data-row="out_sales_allowances_dollars"] { --field-idx: 24; }
[data-row="out_sga_dollars"] { --field-idx: 25; }
[data-row="out_overhead_dollars"] { --field-idx: 26; }
[data-row="out_total_cost"] { --field-idx: 27; }
[data-row="section_net_margin"] { --field-idx: 28; }
[data-row="out_nm_dollars"] { --field-idx: 29; }
[data-row="out_nm_pct"] { --field-idx: 30; }
[data-row="section_additional_info"] { --field-idx: 31; }
[data-row="total_ocean_freight"] { --field-idx: 32; }
[data-row="container_qty"] { --field-idx: 33; }
[data-row="tariff_pct"] { --field-idx: 34; }
[data-row="duty_pct"] { --field-idx: 35; }
[data-row="sales_allowances_pct"] { --field-idx: 36; }
[data-row="tooling_pct"] { --field-idx: 37; }
[data-row="sga_pct"] { --field-idx: 38; }
[data-row="interest_pct"] { --field-idx: 39; }
[data-row="cc_fees_pct"] { --field-idx: 40; }
[data-row="us_operations_pct"] { --field-idx: 41; }
[data-row="commission_pct"] { --field-idx: 42; }
[data-row="royalty_pct"] { --field-idx: 43; }
[data-row="out_total_allowances_overhead"] { --field-idx: 44; }
[data-row="cfee_pct"] { --field-idx: 45; }
[data-row="battery_defective_pct"] { --field-idx: 46; }
[data-row="out_total_cost_allocations"] { --field-idx: 47; }
[data-row="notes"] { --field-idx: 48; }

body {
    font-family: Calibri, sans-serif;
    color: #2C2C2A;
    margin: 2rem;
}

h1, h2 {
    font-family: Cambria, serif;
}

.app-logo-header {
    /* full-bleed banner: pull out of body's 2rem inset on all sides it
       touches (top + both sides) so the red runs edge-to-edge like the
       radioflyer.com header, while the rest of the page keeps its margin */
    margin: -2rem -2rem 1.5rem -2rem;
    padding: 0.85rem 2rem;
    background: #CD0016;
    text-align: center;
}

.app-logo-header img {
    /* radio-flyer-logo-header.svg is cropped tight to the wordmark (no
       padding baked in, unlike the old rf-logo.jpg), so it needs a shorter
       height than that image did to read as the same size banner */
    height: 36px;
    display: inline-block;
}

h1 {
    color: #A6192E;
}

/* Just the "Logged in as" line -- the nav links that used to share this
   row moved up onto the .toolbar row itself (see .top-nav-links below),
   right-aligned to sit directly under them. Small margins on both this and
   .toolbar below: they used to be sized for a title-and-nav row followed
   directly by the filter bar, and stacked into a much bigger gap than
   intended once this line landed in between. */
.current-user-line {
    text-align: right;
    font-size: 0.85rem;
    color: #666;
    margin: 0 0 0.5rem;
}

.top-nav-links {
    display: flex;
    align-items: baseline;
    gap: 0.9rem;
    font-size: 0.9rem;
    /* pushes the nav (and only the nav) to the far right of the toolbar
       row, same plane as +Add another item/View on the left, regardless
       of how much space the action buttons before it take up */
    margin-left: auto;
}

.top-nav-links a {
    color: #A6192E;
    font-weight: bold;
    text-decoration: none;
}

.top-nav-links a:hover {
    text-decoration: underline;
}

.top-nav-sep {
    color: #ccc;
}

.error {
    color: #CE1126;
    font-weight: bold;
}

.import-warnings {
    background: #FFF8E1;
    border: 1px solid #E0C060;
    border-radius: 4px;
    padding: 0.6rem 1rem;
    margin-bottom: 1rem;
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
}

.import-warnings ul {
    margin: 0.3rem 0 0 0;
    padding-left: 1.2rem;
}

.import-form {
    margin-top: 1rem;
}

.toolbar {
    margin-bottom: 0.25rem;
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.4rem;
}

.toolbar-spacer {
    width: 1.5rem;
}

/* Save panel: quote name/visibility/folder are set once in a while, not
   every save, so they hide behind one "Save" toggle instead of sitting in
   the toolbar permanently -- same open/close-on-outside-click pattern as
   .actions-menu (per-item Actions dropdown) and .checkbox-filter (Item/FOB
   filters), just reused here for a toolbar-level control. */
.save-panel-widget {
    position: relative;
}

.input-form .save-panel-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
}

.save-panel-caret {
    font-size: 0.65rem;
}

.save-panel-widget.open .save-panel-toggle {
    box-shadow: 0 0 0 2px rgba(44, 44, 42, 0.4) inset;
}

.save-panel {
    position: absolute;
    top: calc(100% + 0.3rem);
    left: 0;
    z-index: 20;
    width: 260px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
    padding: 0.75rem;
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.save-panel[hidden] {
    display: none;
}

.save-panel-field {
    display: flex;
    flex-direction: column;
    gap: 0.25rem;
}

.save-panel-label {
    font-family: Calibri, sans-serif;
    font-size: 0.75rem;
    color: #666;
}

.save-panel .quote-name-input {
    width: 100%;
}

.input-form .save-panel-submit {
    margin-right: 0;
}

.quote-name-input {
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
    padding: 0.5rem;
    box-sizing: border-box;
    width: 200px;
}

.save-message {
    color: #1B8A3D;
    font-weight: bold;
    margin: -0.5rem 0 1rem 0;
}

.input-form button {
    background: #CE1126;
    color: white;
    border: none;
    border-radius: 5px;
    padding: 0.6rem 1.2rem;
    font-family: Calibri, sans-serif;
    font-size: 1rem;
    cursor: pointer;
    margin-right: 0.5rem;
}

.input-form button[type="submit"]:disabled {
    background: #999;
    cursor: not-allowed;
}

#add-item-btn {
    background: transparent;
    color: #A6192E;
    border: 1px dashed #A6192E;
}

/* quiet utility buttons (Clear Filters, Collapse All) -- these get used far
   less than Calculate, so they shouldn't compete with it for attention.
   Needs ".input-form .btn-quiet" (2 classes) to out-specificity the
   generic ".input-form button" rule, same reason as .actions-menu-toggle. */
.input-form .btn-quiet {
    background: none;
    border: 1px solid #2C2C2A;
    color: #2C2C2A;
}

/* view mode dropdown -- Vertical/Horizontal hide behind one "View" toggle,
   same open/close-on-outside-click pattern as .save-panel-widget, just
   reused here for a second toolbar-level control. */
.view-panel-widget {
    position: relative;
}

.input-form .view-panel-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    margin-right: 0.5rem;
}

.view-panel-caret {
    font-size: 0.65rem;
}

.view-panel-widget.open .view-panel-toggle {
    box-shadow: 0 0 0 2px rgba(44, 44, 42, 0.4) inset;
}

.view-panel {
    position: absolute;
    top: calc(100% + 0.3rem);
    left: 0;
    z-index: 20;
    display: flex;
    flex-direction: column;
    background: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
    overflow: hidden;
}

.view-panel[hidden] {
    display: none;
}

/* shows the CURRENT state (whichever option is "active") rather than a
   single button whose label describes the state you'd switch TO, which
   read backwards at a glance. */
.view-panel .view-mode-option {
    background: none;
    border: none;
    color: #2C2C2A;
    padding: 0.6rem 1rem;
    margin: 0;
    font-family: Calibri, sans-serif;
    font-size: 0.95rem;
    cursor: pointer;
    text-align: left;
    width: 180px;
}

.view-panel .view-mode-option + .view-mode-option {
    border-top: 1px solid #eee;
}

.view-panel .view-mode-option.active {
    background: #CE1126;
    color: white;
    cursor: default;
}

/* Needs Attention + its detail panel -- a native <details>/<summary> rather
   than a custom hidden-div dropdown (see the HTML comment above this
   widget): the browser owns showing/hiding .attention-panel, so there's no
   JS open/close state that can drift from what's actually on screen. The
   pill (<summary>) is the ONLY click target for opening it now -- filtering
   the grid to just the flagged items moved to an explicit checkbox inside
   the panel (.attention-filter-row below) instead of overloading the
   pill's own click. */
.attention-widget {
    position: relative;
    display: inline-block;
}

/* <summary> isn't a <button>, so none of .input-form button's base styling
   (padding/font/border-radius) applies to it automatically the way it does
   for #collapse-expand-all-btn (.btn-quiet) -- spelled out here to match
   that button (and .bulk-status-toggle/"Freeze / Submit" right next to it)
   exactly, caret span included, rather than reading as a different kind of
   control. Ships its own disclosure triangle marker too; suppressed below
   in favor of the same plain trailing caret the other toolbar dropdowns use. */
.attention-widget summary {
    list-style: none;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    background: none;
    border: 1px solid #2C2C2A;
    color: #2C2C2A;
    padding: 0.6rem 1.2rem;
    font-family: Calibri, sans-serif;
    font-size: 1rem;
    border-radius: 5px;
}

.attention-widget summary::-webkit-details-marker {
    display: none;
}

/* a pseudo-element rather than a real <span> in the HTML -- updateAttentionBadgeNow
   sets the summary's textContent directly (to show/hide the "(N)" count),
   which would silently delete a real child element every time it runs. Same
   static (non-flipping) down-caret .bulk-status-caret uses next to it. */
.attention-widget summary::after {
    content: "\25BE";
    font-size: 0.65rem;
    margin-left: 0.3rem;
}

/* same "open" treatment .bulk-status-widget.open .bulk-status-toggle uses --
   an inset ring instead of a color change, since has-attention/active below
   already own this control's color states. */
.attention-widget[open] summary {
    box-shadow: 0 0 0 2px rgba(44, 44, 42, 0.4) inset;
}

/* same neutral tan/charcoal already used for .field-stale/.status-frozen,
   rather than introducing a new color for "something needs attention"
   (amber stays reserved for the NM% traffic light) when at least one item
   is flagged; inverted (dark fill) while the filter checkbox below is
   actively narrowing the grid down to just those items. */
.input-form .needs-attention-toggle.has-attention {
    background: #F1EFE8;
    color: #2C2C2A;
}

.input-form .needs-attention-toggle.active {
    background: #2C2C2A;
    color: #fff;
}

.attention-panel {
    position: absolute;
    top: calc(100% + 0.3rem);
    left: 0;
    z-index: 20;
    width: 300px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
    padding: 0.6rem;
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
}

/* The browser's own UA stylesheet already hides everything but <summary>
   while a <details> is closed (`details:not([open]) > *:not(summary)`) --
   but an author stylesheet rule like .attention-panel's own `display: flex`
   above always wins over a same-specificity UA rule regardless of selector
   weight (author origin beats user-agent origin in the cascade), so without
   an explicit author-level rule of our own, the panel would stay visible
   even while [open] is absent. */
.attention-widget:not([open]) .attention-panel {
    display: none;
}

.attention-filter-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-size: 0.85rem;
    padding-bottom: 0.4rem;
    border-bottom: 1px solid #eee;
}

.attention-list {
    display: flex;
    flex-direction: column;
    max-height: 280px;
    overflow-y: auto;
}

/* Checkbox (only present when this item is stale -- the only reason Sync
   can act on) + label + reason tags, plus a separate small jump-to-item
   button -- deliberately two controls, not one clickable row, so looking
   at/jumping to an item is never the same action as excluding it from the
   bulk sync run. */
.attention-row {
    display: flex;
    align-items: flex-start;
    gap: 0.4rem;
    border-bottom: 1px solid #eee;
    padding: 0.4rem 0.2rem;
}

/* fixed-width slot even when empty (a non-stale row has no checkbox) --
   keeps every row's label starting at the same left edge instead of
   stale/non-stale rows drifting out of alignment with each other. */
.attention-row-leading {
    flex: none;
    width: 1rem;
    padding-top: 0.15rem;
}

.attention-row-info {
    flex: 1;
    min-width: 0;
}

.attention-row-label {
    display: block;
    color: #2C2C2A;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
}

.attention-reason-tags {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
    margin-top: 0.3rem;
}

.attention-reason-tag {
    background: #F1EFE8;
    color: #5F5E5A;
    font-family: Calibri, sans-serif;
    font-size: 0.68rem;
    padding: 0.1rem 0.4rem;
    border-radius: 3px;
    white-space: nowrap;
}

/* Same small red-outline look as .copy-column-btn/.remove-item-btn --
   matches the rest of the app's buttons instead of a bare icon. */
.attention-jump-btn {
    background: none;
    border: 1px solid #CE1126;
    color: #CE1126;
    padding: 0.2rem 0.6rem;
    cursor: pointer;
    font-size: 0.7rem;
    white-space: nowrap;
    flex: none;
}

.attention-jump-btn:hover {
    background: #FAECE7;
}

.attention-empty {
    padding: 0.3rem 0.2rem;
}

.attention-actions {
    display: flex;
    justify-content: flex-end;
}

.input-form .attention-sync-btn {
    margin-right: 0;
    font-size: 0.85rem;
    padding: 0.4rem 0.8rem;
}

.input-form .attention-sync-btn:disabled {
    background: #999;
    cursor: not-allowed;
}

.lookup-busy-indicator {
    font-size: 0.85rem;
    font-style: italic;
    color: #A6192E;
}

.lookup-busy-indicator[hidden] {
    display: none;
}

.filter-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 1rem;
    margin-bottom: 1rem;
    padding: 0.5rem 0.75rem;
    background: #F1EFE8;
}

.checkbox-filter {
    position: relative;
    font-family: Calibri, sans-serif;
}

.checkbox-filter-toggle {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    background: #CE1126;
    color: white;
    border: none;
    padding: 0.6rem 1.2rem;
    font-family: Calibri, sans-serif;
    font-size: 1rem;
    cursor: pointer;
    margin-right: 0.5rem;
}

.checkbox-filter-name {
    font-weight: bold;
}

.checkbox-filter-caret {
    font-size: 0.7rem;
}

.checkbox-filter.open .checkbox-filter-toggle {
    box-shadow: 0 0 0 2px rgba(44, 44, 42, 0.4) inset;
}

.checkbox-filter-panel {
    position: absolute;
    top: calc(100% + 0.3rem);
    left: 0;
    z-index: 20;
    width: 240px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
    padding: 0.5rem;
}

.checkbox-filter-search {
    width: 100%;
    box-sizing: border-box;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    padding: 0.35rem 0.5rem;
    margin-bottom: 0.4rem;
    border: 1px solid #ccc;
    border-radius: 3px;
}

.checkbox-filter-list {
    max-height: 220px;
    overflow-y: auto;
}

.checkbox-filter-all-row {
    border-bottom: 1px solid #eee;
    margin-bottom: 0.3rem;
    padding-bottom: 0.3rem;
    font-weight: bold;
}

.checkbox-filter-row {
    display: flex;
    flex-direction: row;
    align-items: center;
    gap: 0.5rem;
    font-size: 0.85rem;
    padding: 0.25rem 0.15rem;
    cursor: pointer;
}

.checkbox-filter-row:hover {
    background: #F1EFE8;
}

.checkbox-filter-row input {
    cursor: pointer;
}

.checkbox-filter-actions {
    margin-top: 0.4rem;
    padding-top: 0.4rem;
    border-top: 1px solid #eee;
    text-align: right;
}

.checkbox-filter-apply {
    background: #CE1126;
    color: white;
    border: none;
    border-radius: 3px;
    padding: 0.4rem 0.8rem;
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    cursor: pointer;
}

.filter-hint {
    font-family: Calibri, sans-serif;
    font-size: 0.75rem;
    font-style: italic;
    color: #666;
    flex: 1 1 200px;
}

/* Freeze/Submit -- one dropdown toggle (matching .view-panel-widget's
   open/close-on-outside-click pattern) instead of two separate always-
   visible controls, but the panel holds two independent checkboxes
   (Frozen, Quoted) rather than three exclusive options -- each status can
   still be flipped on its own, and checked/indeterminate/unchecked
   reflects whatever's actually true for the currently-visible items
   (updateBulkStatusCheckboxes), refreshed every time the panel opens. */
.bulk-status-widget {
    position: relative;
}

.input-form .bulk-status-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.3rem;
    background: none;
    border: 1px solid #2C2C2A;
    color: #2C2C2A;
}

.bulk-status-widget.open .bulk-status-toggle {
    box-shadow: 0 0 0 2px rgba(44, 44, 42, 0.4) inset;
}

.bulk-status-caret {
    font-size: 0.65rem;
}

.bulk-status-panel {
    position: absolute;
    top: calc(100% + 0.3rem);
    left: 0;
    z-index: 20;
    display: flex;
    flex-direction: column;
    background: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
    padding: 0.5rem;
    gap: 0.3rem;
}

.bulk-status-panel[hidden] {
    display: none;
}

.bulk-status-checkbox-row {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
    padding: 0.3rem 0.4rem;
    white-space: nowrap;
    cursor: pointer;
}

.bulk-status-checkbox-row:hover {
    background: #F1EFE8;
}

/* ================= grid layout =================
   One shared CSS Grid: column 1 is the (single, shared) label column;
   each item gets its own column via the --col custom property. Every
   field is pinned to a fixed position via --field-idx (see the mapping
   block above), so item N's "PRICING" cell always lines up exactly with
   item 1's, regardless of how tall any individual field's content is --
   alignment is a property of the grid, not something each item has to
   independently match.

   View mode: by default (vertical) --field-idx drives grid-row and --col
   drives grid-column -- items are columns, fields are rows. Adding
   .view-horizontal to #items-grid swaps that: --field-idx drives
   grid-column and --col drives grid-row instead -- items become rows,
   fields become columns. Same DOM, same JS, just a different axis
   mapping, so every existing behavior (lookups, overrides, filters,
   collapsing, tab order) keeps working unmodified in both modes. */
/* List view's rail sits as a fixed-width sibling beside .items-grid-main,
   not inside .items-grid-scroll -- it needs its own independent scroll (a
   long item list vs. a wide field grid), and nesting it inside the grid's
   own horizontal-scroll wrapper would drag it along with that scroll
   instead of staying put. Plain flex/no-overflow here so it doesn't
   introduce a new scrolling containing block ahead of .items-grid-sticky-id
   /.items-grid-scroll-top's position:sticky (see their own comments on why
   that matters). */
.items-grid-layout {
    display: flex;
    align-items: flex-start;
}

.items-grid-main {
    flex: 1;
    min-width: 0;
}

/* Only ever shown in List view, and List view always hides
   .items-grid-sticky-id (see applyViewMode) -- so the only sticky bar
   actually above this one at that point is .items-grid-scroll-top-wrap
   (20px), not the combined 68px the HEIGHT_NOTE below documents for
   vertical view. */
.items-list-rail {
    flex: 0 0 380px;
    width: 380px;
    border-right: 1px solid #D8D5C9;
    display: flex;
    flex-direction: column;
    max-height: calc(100vh - 20px);
    position: sticky;
    top: 20px;
    align-self: flex-start;
}

/* the plain .items-list-rail rule's own display:flex (needed once shown)
   otherwise outranks the browser's default [hidden] UA rule -- same fix
   .items-grid-sticky-id.view-mode-hidden already needed for the same
   reason, just via the [hidden] attribute instead of a class here */
.items-list-rail[hidden] {
    display: none;
}

.items-list-rail-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 1rem 1.1rem 0.6rem;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    font-weight: bold;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: #5F5E5A;
}

/* Collapse toggle -- pilot feedback: the rail is a fixed 380px, and a
   reviewer scrolled deep into a wide quote wants that screen space back
   without losing their item list entirely. Collapsing narrows the rail to
   a thin strip (below) rather than re-hiding it via view mode, so one
   click gets it back exactly where it was (sort/select-all/scroll
   position all preserved underneath, just not painted). */
/* .btn-quiet (added in the HTML too) gets this out from under the generic
   red .input-form button styling for color/border -- but that rule keeps
   Calculate-sized padding/font-size, which reads oversized for a small
   corner icon toggle, so the ID selector below (out-specificities
   ".input-form button" on its own, same idea as #add-item-btn) trims it
   down to a compact square. */
#items-list-rail-collapse-btn {
    flex: 0 0 auto;
    width: 1.5rem;
    height: 1.5rem;
    padding: 0;
    margin-right: 0;
    font-size: 0.75rem;
    line-height: 1;
}

.items-list-rail.rail-collapsed {
    flex: 0 0 2.25rem;
    width: 2.25rem;
}

.items-list-rail.rail-collapsed .items-list-rail-header {
    flex-direction: column;
    padding: 0.75rem 0.4rem;
}

.items-list-rail.rail-collapsed .items-list-rail-header-text,
.items-list-rail.rail-collapsed .items-list-sort-label,
.items-list-rail.rail-collapsed .items-list-select-all-row,
.items-list-rail.rail-collapsed .items-list-rows {
    display: none;
}

.items-list-rail.rail-collapsed .items-list-rail-collapse-btn {
    transform: rotate(180deg);
}

.items-list-sort-label {
    display: flex;
    align-items: center;
    gap: 0.5rem;
    padding: 0 1.1rem 0.8rem;
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    color: #5F5E5A;
}

.items-list-sort {
    flex: 1;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    padding: 0.35rem 0.4rem;
    border: 1px solid #D8D5C9;
}

.items-list-select-all-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.5rem 1.1rem;
    font-family: Calibri, sans-serif;
    font-size: 0.75rem;
    font-weight: bold;
    color: #5F5E5A;
    border-top: 1px solid #D8D5C9;
    cursor: pointer;
}

.items-list-empty {
    padding: 0.8rem 1.1rem;
    font-size: 0.85rem;
}

/* The rail's one always-visible, always-scrollable list -- no picker
   dropdown to open, everything currently in play (per the real Item(s)/
   FOB filters) shows here directly. */
.items-list-rows {
    overflow-y: auto;
    border-top: 1px solid #E4E2D8;
    flex: 1;
}

/* the model number a reviewer is actually scanning the rail to find --
   bumped up from a muted section-label size/color to a real heading, since
   it's the primary way anyone identifies which item a group of rows
   belongs to */
.items-list-group-header {
    padding: 0.7rem 1.1rem 0.3rem;
    font-family: Cambria, serif;
    font-size: 1.1rem;
    font-weight: bold;
    color: #2C2C2A;
    border-top: 1px solid #EDEBE2;
}

.items-list-group-header:first-child {
    border-top: none;
}

.items-list-row {
    display: flex;
    align-items: flex-start;
    gap: 0.6rem;
    border-left: 4px solid transparent;
    padding: 0.7rem 1.1rem 0.75rem 0;
    cursor: pointer;
    font-family: Calibri, sans-serif;
}

/* the row's old left padding lives here instead, so the dead zone around
   the small checkbox is part of its click target (see buildListRailRow) */
.items-list-row-checkbox {
    padding: 0.15rem 0.2rem 0.6rem 1.4rem;
    cursor: pointer;
}

.items-list-row-checkbox input {
    width: 16px;
    height: 16px;
    cursor: pointer;
}

.items-list-row-content {
    flex: 1;
    min-width: 0;
}

/* Up/Down keyboard nav (see wireItemListRail) moves focus here row to row
   -- needs a visible ring since that's the only feedback a keyboard user
   gets that the selection actually moved. */
.items-list-row-content:focus-visible {
    outline: 2px solid #CE1126;
    outline-offset: 2px;
}

.items-list-row:hover {
    background: #F1EFE8;
}

.items-list-row-selected,
.items-list-row-selected:hover {
    border-left-color: #CE1126;
    background: #FFFFFF;
}

.items-list-row-top {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 0.5rem;
}

.items-list-row-label {
    font-size: 1rem;
    font-weight: bold;
}

.items-list-row-selected .items-list-row-label {
    font-weight: bold;
}

/* still shrunk a bit from the shared .nm-chip default (that size is tuned
   for Summary's one-per-row table, and this rail has more rows stacked at
   once) but bigger than the original compact rail so it stays readable at
   the larger row size overall */
.items-list-row .nm-chip {
    font-size: 0.85rem;
    padding: 3px 9px;
    border-radius: 5px;
}

.items-list-row-stats {
    font-size: 0.8rem;
    color: #888780;
    font-family: Consolas, monospace;
    margin-top: 4px;
}

.items-list-row-badges {
    margin-top: 5px;
    display: flex;
    gap: 4px;
}

.items-list-row .status-badge {
    font-size: 0.7rem;
    padding: 2px 7px;
    margin: 0;
}

/* horizontal scroll is confined to the grid itself, not the whole page --
   the logo banner, toolbar, and filter bar all sit outside this wrapper in
   the DOM, so they never move as you scroll right through item columns;
   only the label column + item columns underneath do. */
.items-grid-scroll {
    overflow-x: auto;
    max-width: 100%;
}

/* a tall grid (vertical view, many field rows) pushes its own horizontal
   scrollbar down to the very bottom of the page -- out of reach without
   scrolling all the way down first. This mirrors it up here instead, kept
   in view via position:sticky, and synced to the real scrollbar's
   scrollLeft by wireGridTopScrollbar() in app.js. The wrap (not the plain
   scrollbar div itself) owns position:sticky now, so the flanking arrow
   buttons stay pinned right alongside it -- 20px tall (was 14px, this bar's
   height with no buttons), see the HEIGHT_NOTE below .items-grid-sticky-id
   for what else that height feeds into. */
.items-grid-scroll-top-wrap {
    display: flex;
    align-items: center;
    gap: 4px;
    height: 20px;
    position: sticky;
    top: 0;
    z-index: 15;
    background: white;
}

/* height matches the ::-webkit-scrollbar height below exactly (was 14px
   against an 8px scrollbar) -- on non-overlay scrollbars (Windows Chrome/
   Edge default) the browser reserves the box's own height minus the
   scrollbar's thickness as ordinary, non-interactive content area above
   the track, with the 1px spacer sitting invisibly in it. That left a ~6px
   dead strip along the top of this bar that looked like part of the
   scrollbar but did nothing on click -- only the actual 8px track/thumb
   band at the bottom responded, so people found clicking "the bar" flaky
   and only trusted dragging the visible red thumb, which always landed in
   that live band. Sizing the box to the scrollbar's own thickness leaves
   no dead area: every pixel of this strip is the real, clickable track. */
.items-grid-scroll-top {
    overflow-x: auto;
    overflow-y: hidden;
    max-width: 100%;
    flex: 1;
    min-width: 0;
    height: 8px;
}

/* pilot feedback: the plain scrollbar above had no visual cue it was
   draggable, so people never found it mid-scroll -- webkit's scrollbar
   pseudo-elements let it read as an actual colored control instead of a
   stray gray line. Non-webkit browsers (Firefox) just keep their own
   default scrollbar here; the arrow buttons below work everywhere either
   way, so this is a nice-to-have, not the actual fix. */
.items-grid-scroll-top::-webkit-scrollbar {
    height: 8px;
}

.items-grid-scroll-top::-webkit-scrollbar-track {
    background: #EDEAE0;
    border-radius: 4px;
}

.items-grid-scroll-top::-webkit-scrollbar-thumb {
    background: #CE1126;
    border-radius: 4px;
}

#items-grid-scroll-top-spacer {
    height: 1px;
}

/* .btn-quiet (added in the HTML too) gets this out from under the generic
   red .input-form button styling for color/border, same as the List view
   rail's own collapse button -- but that still leaves Calculate-sized
   padding/font-size, which two classes together (".items-grid-scroll-top-
   wrap .items-grid-scroll-nav-btn", out-specificity-ing ".input-form
   button" on class count alone) trims down to a small square icon button. */
.items-grid-scroll-top-wrap .items-grid-scroll-nav-btn {
    flex: 0 0 auto;
    width: 20px;
    height: 20px;
    padding: 0;
    margin: 0;
    font-size: 0.65rem;
    line-height: 1;
    display: flex;
    align-items: center;
    justify-content: center;
}

/* Customer/Item can't be made sticky directly -- they're descendants of
   .item-column's display:contents (see the note below on
   #items-grid.view-horizontal [data-row="item"] for why that breaks
   position:sticky even one level deep). This is a separate, always-visible
   strip instead, living as a sibling BEFORE .items-grid-scroll (same
   placement as .items-grid-scroll-top, and for the same reason -- see that
   rule's comment: .items-grid-scroll's overflow-x:auto forces its
   overflow-y to auto too, per the CSS overflow spec, which makes IT the
   nearest scrolling ancestor for any sticky descendant instead of the
   viewport. A sticky bar placed *inside* .items-grid-scroll never actually
   pins (confirmed directly: it just scrolls away with everything else),
   so this one has to sit outside it, same as the scrollbar mirror already
   does. That means it can't ride .items-grid-scroll's horizontal scroll
   "for free" the way something inside it would -- overflow-x:hidden here
   plus wireGridTopScrollbar() in app.js setting its scrollLeft keeps it
   lined up with the real grid horizontally, the same sync already wired
   between the scrollbar mirror and the real grid.
   Content is rebuilt from the live DOM by refreshStickyIdBar() in app.js
   (customer-select + item-model values), not server-rendered, so it can't
   drift out of sync with edits, add/remove/copy, or filtering.
   HEIGHT_NOTE: this bar's height (48px) plus the scrollbar mirror wrap
   above it (20px, since the scroll-arrows addition) = the
   html { scroll-padding-top: 68px } below -- keep those in sync if either
   changes, or rows will snap in partially hidden under these bars. (Grown
   from 34px/48px to fit the third FOB/Variant line -- see .sid-meta
   below.) */
.items-grid-sticky-id {
    display: grid;
    grid-template-columns: 230px;
    grid-auto-columns: 210px;
    position: sticky;
    top: 20px;
    z-index: 14;
    height: 48px;
    overflow-x: hidden;
    background: #2C2C2A;
    box-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
}

.items-grid-sticky-id.view-mode-hidden {
    display: none;
}

.sticky-id-label {
    grid-column: 1;
    grid-row: 1;
    display: flex;
    align-items: center;
    padding: 0 0.5rem;
    color: #fff;
    font-family: Calibri, sans-serif;
    font-weight: bold;
    font-size: 0.75rem;
    border-right: 1px solid rgba(255, 255, 255, 0.25);
}

.sticky-id-cell {
    grid-row: 1;
    grid-column: var(--col);
    position: relative;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 0.15rem 0.5rem;
    padding-right: 1.6rem;
    border-right: 1px solid rgba(255, 255, 255, 0.15);
    overflow: hidden;
}

.sticky-id-cell .sid-customer,
.sticky-id-cell .sid-item {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sticky-id-cell .sid-customer {
    color: #fff;
    font-weight: bold;
    font-size: 0.75rem;
}

.sticky-id-cell .sid-item {
    /* bigger/bolder than a plain secondary line, and closer to Customer's
       own size -- pilot feedback: accounts already know which customer
       they're on, the model # is what they actually need to read at a
       glance while scrolled down the sheet. */
    color: #fff;
    font-size: 0.85rem;
    font-weight: 600;
}

/* third line: FOB always shown, Variant/Option appended after it when the
   item has one -- read directly off the live fields (see getItemIdentity
   in app.js) regardless of whether the Variant row itself is currently
   toggled open, since this is often the only place it's still visible
   once scrolled past the real row. */
.sticky-id-cell .sid-meta {
    display: flex;
    gap: 0.35rem;
    white-space: nowrap;
    overflow: hidden;
}

.sticky-id-cell .sid-fob {
    color: #999;
    font-size: 0.65rem;
    text-overflow: ellipsis;
    overflow: hidden;
}

.sticky-id-cell .sid-variant {
    color: #E8B34D;
    font-weight: bold;
    font-size: 0.65rem;
    text-overflow: ellipsis;
    overflow: hidden;
}

/* Drag handle floats in the corner rather than joining the
   customer/item/meta stack (buildIdentityCell in app.js appends it last;
   this cell's own padding-right above already leaves room for it so it
   never overlaps the ellipsis-truncated text). ".input-form .sticky-id-cell
   .item-drag-handle" (3 classes) outranks ".input-form .item-drag-handle"
   (2 classes) regardless of source order, so this dark-bar palette always
   wins over the light default meant for the white .sticky-h-id-cell/
   plain-page context. */
.input-form .sticky-id-cell .item-drag-handle {
    position: absolute;
    top: 3px;
    right: 3px;
    border-color: rgba(255, 255, 255, 0.45);
    color: rgba(255, 255, 255, 0.85);
    background: rgba(255, 255, 255, 0.08);
    padding: 0 0.3rem;
    font-size: 0.7rem;
    line-height: 1.5;
}

.input-form .sticky-id-cell .item-drag-handle:hover {
    background: rgba(255, 255, 255, 0.2);
    border-color: rgba(255, 255, 255, 0.7);
}

/* Scroll-snap makes the page settle with a field row's top edge lined up
   exactly at the bottom of the sticky bars above -- without this, releasing
   a scroll mid-row leaves that row visually sliced in half by the sticky
   bar, which is what made freezing feel unstable/"clunky" before.
   `proximity` (not `mandatory`) only pulls the scroll to a nearby line when
   it would land close to one anyway, so ordinary free scrolling elsewhere
   on the page isn't fought. Snap targets are scoped to vertical view only
   (horizontal view scrolls between item rows, not field rows, and has no
   equivalent sticky bar to align against). */
html {
    scroll-snap-type: y proximity;
    scroll-padding-top: 68px;
}

/* scroll-snap-align: start none -- explicitly block-axis (vertical) only.
   A bare "start" sets snapping on BOTH axes at once, and .grid-label is
   also sitting inside .items-grid-scroll, which separately carries its own
   scroll-snap-type: x proximity (below) for the horizontal grid scroll.
   .grid-label is "a single non-repeating column" (this rule's own note
   below), i.e. the only inline-axis snap target that exists in this view
   -- so with a bare "start" it became the ONE snap point on the x axis
   too, always sitting at scrollLeft 0. `proximity` then yanked any small
   rightward scroll from the left edge (a track click, an arrow-button
   nudge) straight back to 0, since that's "close enough" to the only snap
   point going -- only a drag far enough past the proximity threshold
   escaped it. List view never hit this because it hides .grid-label
   entirely (display:none can't be a snap target) -- confirmed as the
   actual difference between "broken in vertical view, fine in list view".
   `none` for the inline value opts this element out of x-axis snapping
   altogether while keeping the intended y-axis (page-scroll) snap. */
#items-grid:not(.view-horizontal) .grid-label {
    scroll-snap-align: start none;
}

#items-grid {
    display: grid;
    grid-template-columns: 230px;
    grid-auto-columns: 210px;
    border-top: 1px solid #2C2C2A;
    border-left: 1px solid #2C2C2A;
    margin-bottom: 1rem;
}

/* List view's detail pane gets an actual bordered card (same visual
   language as Summary's panels and the rail: thin border, rounded corners,
   white surface) instead of vertical mode's top+left-only border above --
   that border was only ever "good enough" because it filled the rest of
   the page and the right/bottom edges scrolled out of view; with just 1-2
   items selected next to the rail, the missing edges and the dead space
   past the last column both become obvious. width:fit-content makes the
   card hug just the visible column(s) instead of stretching to fill
   .items-grid-main's full flex width -- if enough items are selected to
   exceed the available space, .items-grid-scroll's own overflow-x:auto
   still kicks in normally, since fit-content only affects this element's
   own sizing, not whether its parent scrolls it. */
/* nowrap, not wrap: at 480px/card, 3+ selected already don't fit most
   screens side by side -- wrapping would stack the overflow BELOW instead,
   trading a horizontal scroll (which the grid already has via
   .items-grid-scroll, same as Grid/Horizontal view's own overflow) for a
   much longer page to scroll down through. */
#items-grid.view-list {
    display: flex;
    flex-wrap: nowrap;
    align-items: flex-start;
    gap: 16px;
    width: auto;
    /* the base #items-grid rule's top+left border frames vertical/
       horizontal view's shared grid outline -- each card draws its own
       full border now, so that outer top/left-only frame is just a stray
       line across the top of the cards here. */
    border: none;
}

/* List view's detail pane used to reuse vertical mode's shared label
   column + one-field-per-row grid -- exactly the "vertical grid" this
   replaces. Each selected item is now a self-contained card: the shared
   .grid-label column (single, row-aligned across whatever's selected) no
   longer applies once each card lays its fields out independently, so it's
   hidden here; .cell-label/.card-section-title below (silent everywhere
   else -- see their bare display:none) carry the field/section names
   instead, right inside each card. */
#items-grid.view-list .grid-label {
    display: none;
}

.cell-label,
.card-section-title,
.card-section-toggle {
    display: none;
}

/* flex-shrink:0 is the actual fix here -- a plain width:480px alone still
   shrinks below that (flex items shrink by default regardless of their
   own width) once enough cards are selected at once to outgrow the
   available space. That's what let every card in a large multi-select
   silently squeeze itself down to near-illegible tile widths instead of
   just scrolling past the same fixed 480px each. */
#items-grid.view-list .item-column:not(.list-item-hidden) {
    display: grid;
    grid-template-columns: repeat(12, minmax(0, 1fr));
    gap: 8px;
    align-content: start;
    width: 480px;
    flex-shrink: 0;
    background: #FFFFFF;
    border: 1px solid #D8D5C9;
    border-radius: 12px;
    padding: 14px 16px 16px;
}

/* the Actions/Copy/Remove header and the Pricing recap are the only two
   fields with a solid fill of their own -- left at the sharp corners they
   inherit from vertical view, a filled rectangle that close to the card's
   rounded edge reads as a mismatched hard corner sitting inside a soft
   one. Rounding them too makes the nesting read as deliberate. */
#items-grid.view-list .item-column:not(.list-item-hidden) > .grid-cell[data-row="remove"],
#items-grid.view-list .item-column:not(.list-item-hidden) > .grid-cell[data-row="pricing_stats"] {
    border-radius: 8px;
}

/* default: every field spans the full card width -- the groups below carve
   out the fields that instead wrap several-per-row as compact tiles.
   order: var(--field-idx) reuses the exact same field sequence already
   established for vertical/horizontal view (see the --field-idx map at the
   top of this file) -- e.g. Pricing's recap already sits ahead of Inputs
   there, so the card falls in line for free instead of maintaining a
   second, competing order of its own. */
#items-grid.view-list .item-column:not(.list-item-hidden) > * {
    grid-column: span 12;
    grid-row: auto;
    order: var(--field-idx);
}

#items-grid.view-list .item-column:not(.list-item-hidden) .card-section-title {
    display: block;
}

#items-grid.view-list .item-column:not(.list-item-hidden) .card-section-toggle {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 1px solid #B4B2A9;
    color: #5F5E5A;
    font-family: Calibri, sans-serif;
    font-size: 0.75rem;
    line-height: 1;
    width: 18px;
    height: 18px;
    padding: 0;
    cursor: pointer;
}

#items-grid.view-list .item-column:not(.list-item-hidden) > .grid-cell[data-row^="section_"] {
    background: none;
    color: #5F5E5A;
    border: none;
    border-top: 1px solid #E4E2D8;
    justify-content: space-between;
    padding: 8px 0 0;
    margin-top: 4px;
}

#items-grid.view-list .item-column:not(.list-item-hidden) > .grid-cell[data-row="section_pricing"] {
    border-top: none;
    margin-top: 0;
    padding-top: 0;
}

/* Pricing recap, card mode: grouped by column instead of the vertical
   grid's plain row-by-row fill (Sell price/Retail price, then Landed
   cost/Retailer IMU, then RF NM %/RF NM $) -- left column is the sell
   side and its bottom-line result (Sell price, RF NM $, RF NM %), right
   column is everything about the retail side (Retail price, Landed cost,
   Retailer IMU). Explicit placement per data-stat, not auto-flow: the
   desired grouping doesn't match source order (RF NM $/% are the last two
   stat-blocks in the markup but belong in column 1), so reordering via
   `order` alone can't produce it -- each stat needs its own row/column. */
#items-grid.view-list .item-column:not(.list-item-hidden) .pricing-stats-cell {
    grid-template-rows: repeat(3, auto);
}

#items-grid.view-list .item-column:not(.list-item-hidden) .pricing-stats-cell [data-stat="sell_price"] {
    grid-column: 1;
    grid-row: 1;
}

#items-grid.view-list .item-column:not(.list-item-hidden) .pricing-stats-cell [data-stat="nm_dollars"] {
    grid-column: 1;
    grid-row: 2;
}

#items-grid.view-list .item-column:not(.list-item-hidden) .pricing-stats-cell [data-stat="nm_pct"] {
    grid-column: 1;
    grid-row: 3;
}

#items-grid.view-list .item-column:not(.list-item-hidden) .pricing-stats-cell [data-stat="retail_price"] {
    grid-column: 2;
    grid-row: 1;
}

#items-grid.view-list .item-column:not(.list-item-hidden) .pricing-stats-cell [data-stat="landed_cost"] {
    grid-column: 2;
    grid-row: 2;
}

#items-grid.view-list .item-column:not(.list-item-hidden) .pricing-stats-cell [data-stat="retailer_imu_pct"] {
    grid-column: 2;
    grid-row: 3;
}

/* Mode/Sell/Retail/Landed: 3 across */
#items-grid.view-list .item-column:not(.list-item-hidden) :is(
    [data-row="mode"], [data-row="sell_price"], [data-row="retail_mode"],
    [data-row="retail_price"], [data-row="landed_estimate"]
) {
    grid-column: span 4;
}

/* every plain numeric/select/output tile: 4 across */
#items-grid.view-list .item-column:not(.list-item-hidden) :is(
    [data-row="fob"], [data-row="ship_method"],
    [data-row="product_cost"], [data-row="adders"],
    [data-row="out_ocean_freight_unit"], [data-row="out_tariff_duty"], [data-row="out_allocations"],
    [data-row="out_sales_allowances_dollars"], [data-row="out_sga_dollars"], [data-row="out_overhead_dollars"],
    [data-row="total_ocean_freight"], [data-row="container_qty"],
    [data-row="tariff_pct"], [data-row="duty_pct"], [data-row="sales_allowances_pct"], [data-row="tooling_pct"],
    [data-row="sga_pct"], [data-row="interest_pct"], [data-row="cc_fees_pct"], [data-row="us_operations_pct"],
    [data-row="commission_pct"], [data-row="royalty_pct"], [data-row="cfee_pct"], [data-row="battery_defective_pct"],
    [data-row="out_total_allowances_overhead"], [data-row="out_total_cost_allocations"]
) {
    grid-column: span 3;
}

/* Net margin $ / %, and Incoterm/Supplier: 2 across instead of 4 -- their
   text (route names, "vendor name ($cost)") is consistently wider than the
   other Product cost fields' short values (Import/FCL/a number), so 4-up
   left them cramped/wrapping. Total cost joins this group too, but for a
   different reason -- it's the one field left over after SG&A/Overhead
   fill 2 of the 4 slots in their row, so spanning 2 slots' worth is what
   makes it exactly fill the row instead of leaving a dangling empty tile
   next to it. */
#items-grid.view-list .item-column:not(.list-item-hidden) :is(
    [data-row="out_nm_dollars"], [data-row="out_nm_pct"],
    [data-row="incoterm"], [data-row="supplier"],
    [data-row="out_total_cost"]
) {
    grid-column: span 6;
}

/* the tile treatment itself -- label on top, value/input below, shared by
   every field grouped above (identity/section/notes/pricing_stats keep
   their own bespoke layouts and are deliberately left out of this list) */
#items-grid.view-list .item-column:not(.list-item-hidden) > .grid-cell:is(
    [data-row="mode"], [data-row="sell_price"], [data-row="retail_mode"], [data-row="retail_price"], [data-row="landed_estimate"],
    [data-row="fob"], [data-row="ship_method"], [data-row="incoterm"], [data-row="supplier"],
    [data-row="product_cost"], [data-row="adders"],
    [data-row="out_ocean_freight_unit"], [data-row="out_tariff_duty"], [data-row="out_allocations"],
    [data-row="out_sales_allowances_dollars"], [data-row="out_sga_dollars"], [data-row="out_overhead_dollars"],
    [data-row="out_total_cost"],
    [data-row="total_ocean_freight"], [data-row="container_qty"],
    [data-row="tariff_pct"], [data-row="duty_pct"], [data-row="sales_allowances_pct"], [data-row="tooling_pct"],
    [data-row="sga_pct"], [data-row="interest_pct"], [data-row="cc_fees_pct"], [data-row="us_operations_pct"],
    [data-row="commission_pct"], [data-row="royalty_pct"], [data-row="cfee_pct"], [data-row="battery_defective_pct"],
    [data-row="out_total_allowances_overhead"], [data-row="out_total_cost_allocations"],
    [data-row="out_nm_dollars"], [data-row="out_nm_pct"]
) {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    justify-content: center;
    gap: 2px;
    background: #FFFFFF;
    border: 1px solid #D8D5C9;
    border-radius: 6px;
    padding: 6px 8px;
}

/* the tile chrome above sets its own display:flex at high enough
   specificity to otherwise beat the `hidden` attribute outright -- which
   silently broke two existing things: Sell Price/Target Margin (and
   Retail Price/Target IMU) showing BOTH variants at once instead of
   whichever the mode dropdown picked, and Additional Information's own
   "Collapse All" toggle (see wireCollapsibleSection in app.js, which
   toggles `hidden` per-row rather than a class like Inputs/Product Cost
   below). Tied specificity with the rule above, so this only wins because
   it's declared after it -- re-verify that ordering if this block ever
   moves. */
#items-grid.view-list .item-column:not(.list-item-hidden) > .grid-cell[hidden] {
    display: none;
}

#items-grid.view-list .item-column:not(.list-item-hidden) > .grid-cell:is([data-row="out_nm_dollars"], [data-row="out_nm_pct"]) {
    background: #EAF3DE;
}

/* Inputs/Product Cost collapse via a class on #items-grid (see the
   .inputs-collapsed/.product-cost-collapsed rules elsewhere in this file),
   which the tile chrome above would otherwise also silently override --
   these win unconditionally since the extra collapsed class makes them
   more specific than the plain tile rule, regardless of file order. */
#items-grid.view-list.inputs-collapsed .item-column:not(.list-item-hidden) > .grid-cell:is(
    [data-row="mode"], [data-row="sell_price"], [data-row="retail_mode"], [data-row="retail_price"], [data-row="landed_estimate"]
) {
    display: none;
}

#items-grid.view-list.product-cost-collapsed .item-column:not(.list-item-hidden) > .grid-cell:is(
    [data-row="fob"], [data-row="ship_method"], [data-row="incoterm"], [data-row="supplier"],
    [data-row="product_cost"], [data-row="adders"],
    [data-row="out_ocean_freight_unit"], [data-row="out_tariff_duty"], [data-row="out_allocations"],
    [data-row="out_sales_allowances_dollars"], [data-row="out_sga_dollars"], [data-row="out_overhead_dollars"],
    [data-row="out_total_cost"]
) {
    display: none;
}

/* plain computed outputs (not editable, no input/select inside) -- a
   quiet gray fill so they read as "calculated" at a glance next to the
   white editable tiles around them. Same tint the vertical grid already
   uses for its own subtotal rows (out_total_allowances_overhead/
   out_total_cost_allocations' unconditional background further down this
   file), so this isn't a new color convention, just carried into the card.
   Net margin's two tiles are deliberately left out -- they're also
   read-only, but already get their own green emphasis above. */
#items-grid.view-list .item-column:not(.list-item-hidden) > .grid-cell:is(
    [data-row="out_ocean_freight_unit"], [data-row="out_tariff_duty"], [data-row="out_allocations"],
    [data-row="out_sales_allowances_dollars"], [data-row="out_sga_dollars"], [data-row="out_overhead_dollars"],
    [data-row="out_total_allowances_overhead"], [data-row="out_total_cost_allocations"], [data-row="out_total_cost"]
) {
    background: #F1EFE8;
}

/* field-override/field-stale (see their own rules further down) mark a
   field with a colored left border + tint -- reasserted here at higher
   specificity than the plain tile chrome above, which would otherwise
   silently swallow the indicator inside a card. */
#items-grid.view-list .item-column:not(.list-item-hidden) > .grid-cell.field-override {
    border-left: 3px dashed #2C2C2A;
    background: #F1EFE8;
}

#items-grid.view-list .item-column:not(.list-item-hidden) > .grid-cell.field-stale {
    border-left: 3px solid #2C2C2A;
    background: #F1EFE8;
}

#items-grid.view-list .item-column:not(.list-item-hidden) .cell-label {
    display: block;
    font-family: Calibri, sans-serif;
    font-size: 0.68rem;
    font-weight: normal;
    color: #5F5E5A;
}

#items-grid.view-list .item-column:not(.list-item-hidden) :is(
    [data-row="mode"], [data-row="sell_price"], [data-row="retail_mode"], [data-row="retail_price"], [data-row="landed_estimate"],
    [data-row="fob"], [data-row="ship_method"], [data-row="incoterm"], [data-row="supplier"],
    [data-row="product_cost"], [data-row="adders"],
    [data-row="total_ocean_freight"], [data-row="container_qty"],
    [data-row="tariff_pct"], [data-row="duty_pct"], [data-row="sales_allowances_pct"], [data-row="tooling_pct"],
    [data-row="sga_pct"], [data-row="interest_pct"], [data-row="cc_fees_pct"], [data-row="us_operations_pct"],
    [data-row="commission_pct"], [data-row="royalty_pct"], [data-row="cfee_pct"], [data-row="battery_defective_pct"]
) :is(input, select) {
    border: none;
    background: transparent;
    padding: 0;
}

/* Identity block, card mode only: the model number reads as the card's
   real headline (bigger/bold/serif, matching the section titles' branding)
   and the product-name description below it reads as an actual sentence
   instead of a truncated aside -- both were sized/dimmed for vertical
   view's cramped single-line row, which no longer applies once the card
   has room to spare. The extra .grid-cell[data-row="item"] qualifier below
   isn't needed to select the right element (.item-model-input is already
   unique) -- it's there purely to outrank the vertical-view sizing rules
   further down this file on specificity, since view-list/item-column/
   :not(.list-item-hidden) alone tie with those rules' own selector weight. */
#items-grid.view-list .item-column:not(.list-item-hidden) .grid-cell[data-row="item"] .item-model-input {
    font-family: Cambria, serif;
    font-size: 1.3rem;
    font-weight: bold;
}

#items-grid.view-list .item-column:not(.list-item-hidden) .grid-cell[data-row="item"] .item-lookup-status {
    font-size: 0.9rem;
    font-style: normal;
    opacity: 1;
    white-space: normal;
    overflow: visible;
    text-overflow: clip;
}

/* column 1 is the frozen Customer/Item mirror (.sticky-h-id-cell, below) --
   a real dedicated column, not shared with any field. Column 2 is Actions
   (needs its own width for 3 side-by-side buttons); column 3 is the
   unused gap. Every --field-idx-driven placement in horizontal mode
   (.item-column > *, .grid-label, and the hardcoded group-header/collapse
   column numbers further down) adds +1 to field-idx to make room for the
   leading mirror column -- field-idx itself stays untouched since vertical
   mode also reads it (as grid-row there, unaffected by any of this).

   Every ordinary field column (Customer and Item included -- they used to
   get their own narrower widths, 130px/160px, before this) is now a flat
   190px via grid-auto-columns, rather than sized to each field's own
   max-content: the point is a uniform rhythm scrolling across columns,
   matching vertical view's uniform per-item column width, even though
   that means a short field like Mode sits in more room than its content
   needs, and a long one (Incoterm/Route, Notes) wraps instead of widening
   its column. 190px clears the widest ordinary field observed (Incoterm/
   Supplier at 186px) with a little headroom. PRICING (field-idx 7, column
   8) is the one deliberate exception, explicitly sized instead -- it
   carries 6 values via a 3-column stat card (see .pricing-stats-cell),
   not one value, and forcing it down to 190px would mean either wrapping
   to 3+ lines (inflating the row height fix from earlier back out) or
   collapsing to a 2-column stack. 340px comfortably fits its natural
   ~324px.
   Column 28 (NET MARGIN's hidden per-item divider, same idea as PRICING's
   own hidden divider at column 7) is zeroed out explicitly too, same as
   columns 3/6/7 -- otherwise the uniform 190px would apply there as well,
   leaving a blank 190px gap between PRODUCT COST's last value and NET
   MARGIN's first.

   INPUTS/PRODUCT COST/ADDITIONAL INFO's own field columns (10-14, 16-27,
   32-46) are 190px via a CSS custom property instead of a plain 190px
   literal, specifically so collapsing a section can zero it back out.
   Before column widths were uniform, collapsing worked "for free": a
   collapsed section's fields went display:none, and since columns were
   max-content (content-driven), a column with no visible content just
   naturally sized to ~0. A flat, uniform pixel width doesn't respond to
   its content's visibility at all -- the track still reserves its full
   190px regardless of what's (or isn't) rendered inside it, which is
   exactly why collapsing left a huge blank gap instead of closing it
   (confirmed directly). Each section's --w-* variable defaults to 190px
   and only the matching .*-collapsed class below overrides it to 0 --
   since each is independent, this composes correctly for any combination
   of collapsed sections (e.g. "Collapse All") without needing a separate
   hardcoded column list per combination. */
#items-grid.view-horizontal {
    --w-inputs: 190px;
    --w-product-cost: 190px;
    --w-additional-info: 190px;
    grid-template-columns:
        200px 220px 0 190px 190px 0 0 340px
        190px repeat(5, var(--w-inputs))
        190px repeat(12, var(--w-product-cost))
        0 repeat(3, 190px)
        repeat(15, var(--w-additional-info))
        190px;
    grid-auto-columns: 190px;
    grid-template-rows: none;
    grid-auto-rows: minmax(2.2rem, auto);
}

.item-column {
    display: contents;
}

.item-column.filtered-out,
.item-column.list-item-hidden {
    display: none;
}

.item-column > * {
    grid-column: var(--col);
    grid-row: var(--field-idx);
}

/* row 1 is added below for the group header (INPUTS/PRICING/etc, ungrouped
   fields spanning over it), row 2 for the regular field-name header --
   item rows need to start one row later than --col to make room, hence
   +1 */
#items-grid.view-horizontal .item-column > * {
    grid-column: calc(var(--field-idx) + 1);
    grid-row: calc(var(--col) + 1);
}

.grid-label {
    grid-column: 1;
    grid-row: var(--field-idx);
}

#items-grid.view-horizontal .grid-label {
    grid-column: calc(var(--field-idx) + 1);
    grid-row: 2;
}

/* fields with no section group above them (Actions/Customer/Item/the
   Sourcing Summary recap/Notes) span both header rows, matching how a
   grouped-header table gives ungrouped columns a full-height header while
   grouped columns get a group super-header + a field sub-header. FOB/Ship
   Method/Incoterm/Supplier are grouped under INPUTS now (see the field-idx
   map above), so they get the normal grouped sub-header treatment below
   instead of appearing here. */
#items-grid.view-horizontal .grid-label[data-row="remove"],
#items-grid.view-horizontal .grid-label[data-row="customer"],
#items-grid.view-horizontal .grid-label[data-row="item"],
#items-grid.view-horizontal .grid-label[data-row="notes"] {
    grid-row: 1 / 3;
}

/* section group headers (INPUTS/PRICING/PRODUCT COST/NET MARGIN/
   ADDITIONAL INFORMATION): span row 1 across the field-idx range of the
   columns they introduce, instead of being squeezed into their own
   divider column's width like every other label. The per-item marker
   cells for these data-row values (the colored divider strip itself)
   keep the generic single-column placement above -- only the label
   (header) text moves. */
#items-grid.view-horizontal .grid-label[data-row="section_pricing"] {
    grid-row: 1;
    grid-column: 8 / 9;
}

#items-grid.view-horizontal .grid-label[data-row="section_inputs"] {
    grid-row: 1;
    grid-column: 10 / 15;
}

#items-grid.view-horizontal .grid-label[data-row="section_product_cost"] {
    grid-row: 1;
    grid-column: 16 / 28;
}

#items-grid.view-horizontal .grid-label[data-row="section_net_margin"] {
    grid-row: 1;
    grid-column: 29 / 31;
}

#items-grid.view-horizontal .grid-label[data-row="section_additional_info"] {
    grid-row: 1;
    grid-column: 32 / 47;
}

/* horizontal mode only: Item gets a visual separator from the scrolling
   fields to its right, marking the end of the leading identity block
   (frozen mirror + Actions + Customer + Item) before the scrollable
   fields begin. */
#items-grid.view-horizontal [data-row="item"] {
    border-right: 2px solid #2C2C2A;
}

/* Frozen Customer/Item mirror for horizontal view: column 1, ahead of
   Actions -- same idea as vertical view's .items-grid-sticky-id bar
   (a persistent, always-visible identity strip), adapted to horizontal's
   one-row-per-item layout (one real cell per row here, instead of one
   shared bar). It's a genuine sibling of .item-column, not one of its
   display:contents descendants, which is what lets position:sticky take
   effect at all (confirmed directly: even an isolated sticky div with a
   display:contents parent fails to stick in this browser).

   This used to sit AFTER Item, at a leftover unused field-idx slot, held
   in place with position:sticky -- which ran into two separate problems:
   1) a sticky element's containing block, for a grid item, is its OWN
      grid area. Anchored mid-row, its box was only ~200px wide, so it
      could only stay stuck for ~200px of scrolling before that box was
      exhausted and it released -- which is exactly why it visibly
      un-froze partway through INPUTS, well before PRODUCT COST. Widening
      that box to span the rest of the grid (so it'd have more room to
      stay stuck) starved the actual PRICING column of its own width
      instead, since nothing else was anchoring those columns -- causing
      the mirror to visually overlap and cover PRICING.
   Moving it to be the FIRST column (this) should have sidestepped that
   entirely -- natural position and stuck position are the same place, so
   there's no travel needed -- but position:sticky here STILL silently let
   go after roughly (viewport width - column width) of scrolling, in a
   real multi-row quote, for reasons that didn't cleanly match any
   grid/sticky spec rule we could pin down (confirmed directly: reproduced
   consistently across repeated tests, not a one-off timing artifact).
   Given position:sticky has now failed for two structurally different
   reasons in this same grid, it's positioned with a plain JS-computed
   transform instead (see syncStickyMirrorX in app.js) -- not subject to
   containing-block computation at all, since transform is pure paint,
   decoupled from layout/grid-area semantics entirely. */
#items-grid.view-horizontal .sticky-h-id-cell {
    grid-column: 1;
    grid-row: calc(var(--col) + 1);
    position: relative;
    z-index: 5;
    display: flex;
    flex-direction: column;
    justify-content: center;
    padding: 0.3rem 0.5rem;
    padding-right: 1.6rem;
    background: #fff;
    border-right: 1px solid #ddd;
    border-bottom: 1px solid #ddd;
    box-shadow: 2px 0 4px rgba(0, 0, 0, 0.08);
    overflow: hidden;
}

#items-grid:not(.view-horizontal) .sticky-h-id-cell {
    display: none;
}

.sticky-h-id-cell .sid-customer,
.sticky-h-id-cell .sid-item {
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
}

.sticky-h-id-cell .sid-customer {
    font-weight: bold;
    font-size: 0.8rem;
    color: #2C2C2A;
}

.sticky-h-id-cell .sid-item {
    /* see .sticky-id-cell .sid-item above -- same "make the model # bigger"
       feedback, horizontal view's own frozen bar */
    font-size: 0.85rem;
    font-weight: 600;
    color: #2C2C2A;
}

/* same third line as the vertical bar's .sid-meta -- this cell's height
   isn't fixed (it tracks the real row's natural height), so no HEIGHT_NOTE
   adjustment needed here. */
.sticky-h-id-cell .sid-meta {
    display: flex;
    gap: 0.35rem;
    white-space: nowrap;
    overflow: hidden;
}

.sticky-h-id-cell .sid-fob {
    color: #999;
    font-size: 0.7rem;
    text-overflow: ellipsis;
    overflow: hidden;
}

.sticky-h-id-cell .sid-variant {
    color: #8A5A00;
    font-weight: bold;
    font-size: 0.7rem;
    text-overflow: ellipsis;
    overflow: hidden;
}

/* same floating drag handle as the vertical bar's .sticky-id-cell above,
   just without that one's dark-bar color override -- the default
   .input-form .item-drag-handle grey already reads fine on this cell's
   white background. */
.input-form .sticky-h-id-cell .item-drag-handle {
    position: absolute;
    top: 3px;
    right: 3px;
    padding: 0 0.3rem;
    font-size: 0.7rem;
    line-height: 1.5;
}

/* Horizontal view's own scroll-snap, mirroring the vertical one above but
   on the x axis -- settles a field column's left edge right at the mirror
   above instead of leaving it sliced in half mid-scroll. Applied
   unconditionally on .items-grid-scroll (not scoped to view-horizontal):
   in vertical/list view .grid-label defines no *inline*-axis snap-align
   (see its own rule above, which is now explicitly block-axis-only for
   exactly this reason), so this stays a no-op there -- it used NOT to be
   scoped that way, and .grid-label being "a single non-repeating column"
   sitting at the very start of this scroll container made it the only x
   snap point around, permanently anchoring scrollLeft at 0 in vertical
   view (confirmed as the cause of the "right arrow does nothing from the
   left edge" bug -- see that rule's comment for the full story). */
.items-grid-scroll {
    scroll-snap-type: x proximity;
    /* only the mirror column (200px) is actually frozen now -- Actions/
       Customer/Item scroll away like any other field, so snapping only
       needs to clear the one thing that stays put. */
    scroll-padding-left: 200px;
}

/* none start -- inline-axis (x) only, same reasoning as the vertical-view
   rule above but mirrored: a bare "start" would also register .grid-label
   as a snap target on html's own (unconditional) y-proximity snapping,
   which isn't wanted here -- horizontal view "scrolls between item rows,
   not field rows, and has no equivalent sticky bar to align against" (see
   the html rule's own comment). Not yet observed to misbehave in practice
   here, but it's the identical cross-axis leak the vertical-view rule
   just got fixed for, so closing it here too rather than leaving a
   matching latent bug. */
#items-grid.view-horizontal .grid-label {
    scroll-snap-align: none start;
}

/* Floating column headers for horizontal view: as you scroll DOWN through
   many item rows, the field names (Overhead, Ocean Freight, etc.) stay
   pinned at the top of the viewport instead of scrolling away with row 1.
   position:sticky does NOT work for this, confirmed directly: these
   headers are direct children of #items-grid (not display:contents
   descendants, unlike the Customer/Item mirror's original problem), but
   they're still grid items, and a grid item's sticky containing block is
   its OWN grid area -- one row, ~35px tall. Reaching "stuck" from a
   natural position hundreds of pixels down the page (below the logo/
   toolbar/filter bar) would need far more travel room than a 35px-tall
   area can ever provide, so it never engages at all (confirmed: it just
   scrolls in perfect lockstep with the page, zero offset, at every scroll
   position tested). Positioned via JS transform instead -- see
   syncStickyHeadersY() in app.js -- same reasoning as the mirror's
   translateX fix: a transform has no containing-block size limit to run
   into, since it's pure paint, not layout. */
#items-grid.view-horizontal .grid-label {
    position: relative;
    z-index: 6;
}

.grid-label,
.grid-cell {
    padding: 0.3rem 0.5rem;
    border-right: 1px solid #ddd;
    border-bottom: 1px solid #ddd;
    display: flex;
    align-items: center;
    min-width: 0;
}

/* horizontal mode only: top-align every cell instead of centering. Rows
   auto-size to their tallest cell (e.g. Item's model number + product
   name caption, or Actions/Copy/Remove stacked in the "remove" cell) --
   centering a short single-line control (a Customer/FOB select) against
   that full row height sits it well below where a *centered* multi-line
   cell's own first line lands, so the two don't read as level with each
   other. Top-aligning both means every cell's primary control starts at
   the same line regardless of how much extra content follows beneath it. */
#items-grid.view-horizontal .grid-label,
#items-grid.view-horizontal .grid-cell {
    align-items: flex-start;
}

.grid-cell[hidden] {
    display: none;
}

.grid-label {
    font-family: Calibri, sans-serif;
    font-weight: bold;
    font-size: 0.85rem;
    background: #fafafa;
}

.grid-cell input[type="number"],
.grid-cell input[type="text"],
.grid-cell select {
    font-family: Calibri, sans-serif;
    width: 100%;
    box-sizing: border-box;
    font-size: 0.85rem;
}

.grid-cell textarea {
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    width: 100%;
    min-height: 4em;
    box-sizing: border-box;
    resize: vertical;
}

/* the [data-row="item"] attribute (not just .grid-cell) is what pushes this
   past .grid-cell input[type="text"]'s font-size:0.85rem (further up this
   file) in specificity -- without it, the 0.85rem generic rule silently
   wins and this font-size never actually takes effect. */
.grid-cell[data-row="item"] .item-lookup-status {
    font-size: 0.7rem;
    font-style: italic;
    opacity: 0.75;
    margin: 0.15rem 0 0;
    font-family: Calibri, sans-serif;
    border: none;
    background: transparent;
    padding: 0;
}

/* looks like plain caption text until hovered/focused -- editable, but
   shouldn't visually compete with the real input fields above it */
.grid-cell[data-row="item"] .item-lookup-status:hover,
.grid-cell[data-row="item"] .item-lookup-status:focus {
    opacity: 1;
    outline: none;
    border-bottom: 1px dashed #999;
}

/* Customer + Item + the Sourcing Summary recap read as one compact title
   block -- still 3 separate grid rows underneath (every selector/JS hook
   these rely on -- .customer-select, .item-model-input -- is untouched),
   just tightened spacing/typography and no border between them so they
   read as one block. FOB/Ship Method/Incoterm/Supplier themselves live
   further down as ordinary INPUTS rows now (unstyled, same look as every
   other input field) -- only their read-only recap sits up here.
   Vertical view only: in horizontal view these are already side-by-side
   columns, where this "stacked caption" treatment doesn't apply. */
#items-grid:not(.view-horizontal) .grid-label[data-row="customer"],
#items-grid:not(.view-horizontal) .grid-cell[data-row="customer"] {
    padding: 0.4rem 0.5rem 0;
    border-bottom: none;
}

#items-grid:not(.view-horizontal) .grid-label[data-row="customer"] {
    font-size: 0.62rem;
    font-weight: normal;
    letter-spacing: 0.03em;
    color: #999;
    background: none;
}

/* Customer is the headline of the block -- Item (below) is the product
   detail underneath it, not the other way around */
#items-grid:not(.view-horizontal) .grid-cell[data-row="customer"] select {
    border: none;
    background: transparent;
    color: #2C2C2A;
    font-family: Cambria, serif;
    font-weight: bold;
    font-size: 1.05rem;
    padding: 0;
    cursor: pointer;
}

#items-grid:not(.view-horizontal) .grid-label[data-row="item"],
#items-grid:not(.view-horizontal) .grid-cell[data-row="item"] {
    padding: 0.1rem 0.5rem 0.4rem;
    border-bottom: none;
}

/* horizontal mode only: without this, the Item cell falls back to the
   generic .grid-cell rule (a single flex ROW, not column) -- .item-headline-row
   and .item-caption-row (each width:100%) then compete side by side for
   the same line instead of stacking, squeezing the model-number input
   down to a handful of visible characters. Vertical mode doesn't need this
   rule (it has its own dedicated column layout above); this is horizontal's
   equivalent, minus that block's borderless/serif headline styling, which
   was designed around vertical's card-like look and would look out of
   place next to horizontal's ordinary bordered fields. */
#items-grid.view-horizontal .grid-cell[data-row="item"] {
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
}

#items-grid:not(.view-horizontal) .grid-cell[data-row="item"] {
    flex-direction: column;
    align-items: stretch;
    gap: 0.15rem;
}

#items-grid:not(.view-horizontal) .grid-cell[data-row="item"] .item-model-input {
    border: none;
    border-bottom: 1px solid transparent;
    background: transparent;
    font-family: Calibri, sans-serif;
    font-weight: normal;
    font-size: 0.85rem;
    color: #2C2C2A;
    padding: 0.1rem 0;
    width: 100%;
}

#items-grid:not(.view-horizontal) .grid-cell[data-row="item"] .item-model-input:hover {
    border-bottom-color: #ccc;
}

#items-grid:not(.view-horizontal) .grid-cell[data-row="item"] .item-model-input:focus {
    border-bottom-color: #CE1126;
    outline: none;
}

/* Item + RF NM% together form the second line of the "headline" a rep
   scans across many items -- Customer (own row above) is the first line,
   this pairs the item with the one pricing number that matters most at a
   glance, so the header alone reads e.g. "Walmart US" / "411 · 14.2%". */
.item-headline-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.4rem;
    width: 100%;
}

.item-headline-main {
    min-width: 0;
    flex: 1;
}

.item-headline-nm {
    font-family: Consolas, monospace;
    font-weight: bold;
    font-size: 1.05rem;
    white-space: nowrap;
    flex-shrink: 0;
}

.item-headline-nm.nm-good { color: #1B8A3D; }
.item-headline-nm.nm-warn { color: #B8860B; }
.item-headline-nm.nm-bad { color: #CE1126; }
.item-headline-nm.pending-note { color: #999; font-weight: normal; font-size: 0.85rem; }

/* the Option/Variant line sits between the model-#/NM% headline and the
   product-name caption -- styled as a pill so a variant (e.g. "Closed
   Color Box", "2-Pack Tray") pops out at a glance among several rows that
   otherwise share the same model #. Empty ones stay near-invisible
   (dashed, muted) so items that don't need a variant label don't add
   visual noise. */
.item-variant-row {
    width: 100%;
}

/* scoped through [data-row="item"] for the same specificity reason as
   .item-lookup-status above -- otherwise .grid-cell input[type="text"]'s
   font-size:0.85rem wins and this pill renders oversized. */
.grid-cell[data-row="item"] .item-variant-input {
    display: block;
    width: 100%;
    box-sizing: border-box;
    font-family: Calibri, sans-serif;
    font-size: 0.72rem;
    font-weight: bold;
    color: #8A5A00;
    background: #FCF3DC;
    border: 1px dashed transparent;
    border-radius: 3px;
    padding: 0.1rem 0.4rem;
    margin: 0.1rem 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.item-variant-input:placeholder-shown {
    font-weight: normal;
    color: #999;
    background: transparent;
    border-color: #ccc;
}

.item-variant-input:hover {
    border-color: #999;
}

.item-variant-input:focus {
    outline: none;
    border-color: #CE1126;
}

/* third line of the headline block: the product-name caption on the left,
   the FOB recap on the right -- directly under the variant pill above it,
   so the two right-aligned values (NM%, FOB) stack, and the two
   left-aligned ones (model #, product name) stack. */
.item-caption-row {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 0.4rem;
    width: 100%;
}

.item-caption-row .item-lookup-status {
    min-width: 0;
    flex: 1;
    margin: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.sourcing-summary-text {
    font-family: Calibri, sans-serif;
    font-size: 0.72rem;
    font-style: italic;
    color: #666;
    white-space: nowrap;
    flex-shrink: 0;
}

/* percentage-suffix fields: keep a real <input type=number> (so validation
   and JS value-reading are unaffected) but visually show "8%" instead of
   bare "8" via an adjacent, non-editable suffix */
.pct-field {
    display: flex;
    align-items: center;
    gap: 0.2rem;
    width: 100%;
}

.pct-field input {
    min-width: 0;
    flex: 1;
}

.pct-suffix {
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    opacity: 0.8;
}

/* section header rows -- one label cell + one cell per item, all sharing
   the same background so the divider reads as a single full-width bar */
.grid-label[data-row^="section_"],
.grid-cell[data-row^="section_"] {
    font-family: Cambria, serif;
    font-weight: bold;
    font-size: 0.8rem;
    letter-spacing: 0.03em;
    background: #F1EFE8;
    color: #2C2C2A;
}

.grid-label[data-row="section_net_margin"],
.grid-cell[data-row="section_net_margin"],
.grid-label[data-row="section_additional_info"],
.grid-cell[data-row="section_additional_info"] {
    background: #A6192E;
    color: white;
}

.input-form .section-toggle {
    background: none;
    border: 1px solid white;
    color: white;
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    padding: 0 0.4rem;
    margin-left: 0.5rem;
    cursor: pointer;
}

/* INPUTS/PRODUCT COST section headers are cream, not red -- the default
   white-on-transparent toggle button would be nearly invisible there */
.section-toggle.section-toggle-dark {
    border-color: #2C2C2A;
    color: #2C2C2A;
}

/* INPUTS and PRODUCT COST collapse via a class on #items-grid rather than
   the `hidden` attribute per-row (like ADDITIONAL INFORMATION uses),
   because several of these rows (Sell Price/Target Margin, Retail
   Price/Target IMU) already use `hidden` to toggle between their two mode
   variants -- reusing the same attribute for collapse would fight with
   that. This only ever ADDS display:none on top of whatever the mode
   toggle already decided; it never clears it. */
#items-grid.inputs-collapsed [data-row="mode"],
#items-grid.inputs-collapsed [data-row="sell_price"],
#items-grid.inputs-collapsed [data-row="retail_mode"],
#items-grid.inputs-collapsed [data-row="retail_price"],
#items-grid.inputs-collapsed [data-row="landed_estimate"] {
    display: none;
}

#items-grid.product-cost-collapsed [data-row="fob"],
#items-grid.product-cost-collapsed [data-row="ship_method"],
#items-grid.product-cost-collapsed [data-row="incoterm"],
#items-grid.product-cost-collapsed [data-row="supplier"],
#items-grid.product-cost-collapsed [data-row="product_cost"],
#items-grid.product-cost-collapsed [data-row="adders"],
#items-grid.product-cost-collapsed [data-row="out_ocean_freight_unit"],
#items-grid.product-cost-collapsed [data-row="out_tariff_duty"],
#items-grid.product-cost-collapsed [data-row="out_allocations"],
#items-grid.product-cost-collapsed [data-row="out_sales_allowances_dollars"],
#items-grid.product-cost-collapsed [data-row="out_sga_dollars"],
#items-grid.product-cost-collapsed [data-row="out_overhead_dollars"],
#items-grid.product-cost-collapsed [data-row="out_total_cost"] {
    display: none;
}

/* horizontal mode only: a collapsed section's group-header label still
   spans its full field-idx column range by default, which forces those
   (now display:none) columns to stay wide enough to fit the label text --
   a big blank gap instead of a clean collapse. When collapsed, shrink the
   label back down to its own single divider column (matching the empty
   per-item marker cells already sitting there), rather than the full
   group's width, so collapsing still reads as "INPUTS, collapsed" instead
   of a bare unlabeled +.
   This alone isn't enough to actually close the gap, though -- that's
   what the --w-inputs/--w-product-cost/--w-additional-info overrides
   just below are for. This rule only stops the LABEL from forcing extra
   width; the FIELD columns underneath it are what --w-* zeroes out. */
#items-grid.view-horizontal.inputs-collapsed .grid-label[data-row="section_inputs"] {
    grid-column: 9;
}

#items-grid.view-horizontal.product-cost-collapsed .grid-label[data-row="section_product_cost"] {
    grid-column: 15;
}

#items-grid.view-horizontal.additional-info-collapsed .grid-label[data-row="section_additional_info"] {
    grid-column: 31;
}

/* Actually closes the collapse gap: overrides the section's --w-* custom
   property (declared on #items-grid.view-horizontal above, default
   190px) to 0, which is what its field columns' repeat() actually sizes
   from. Each is independent of the others, so any combination of
   collapsed sections -- including "Collapse All", all three at once --
   composes correctly without needing a separate rule per combination. */
#items-grid.view-horizontal.inputs-collapsed {
    --w-inputs: 0px;
}

#items-grid.view-horizontal.product-cost-collapsed {
    --w-product-cost: 0px;
}

#items-grid.view-horizontal.additional-info-collapsed {
    --w-additional-info: 0px;
}

/* PRICING and NET MARGIN have no collapse toggle, so their own divider
   column (a leftover from the vertical layout, where it's a colored
   section-header row) now serves no purpose in horizontal mode -- the
   group name already shows spanning above their real fields (see the
   .grid-label[data-row="section_pricing"/"section_net_margin"] rules
   above). Hiding it removes the redundant empty bar that otherwise sits
   between, e.g., the collapsed INPUTS "+" and PRICING's actual columns.
   Scoped to .grid-cell specifically -- [data-row="..."] alone would also
   match the .grid-label group-header text itself (same attribute, both
   element types), silently hiding the one thing this comment says is
   still showing. That was the actual bug behind PRICING's columns having
   no visible header at all: pricing_stats' own field-name label is
   intentionally blank (it's a merged multi-value cell), so once the group
   label above it was accidentally hidden too, nothing labeled that column
   at any level. */
#items-grid.view-horizontal .grid-cell[data-row="section_pricing"],
#items-grid.view-horizontal .grid-cell[data-row="section_net_margin"] {
    display: none;
}

.grid-label[data-row="pricing_stats"],
.grid-cell[data-row="pricing_stats"] {
    background: #FBEAEA;
}

/* PRICING's 6 values (Sell Price, Retail Price, Landed Cost, Retailer
   IMU, RF NM %, RF NM $) as a compact 2-column stat grid in one cell,
   instead of 6 stacked full-width rows -- same values, same traffic-light
   coloring on RF NM %, just laid out like a small dashboard tile. */
.pricing-stats-cell {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 0.35rem 0.6rem;
    align-items: start;
}

.stat-block {
    display: flex;
    flex-direction: column;
}

.stat-value {
    font-family: Consolas, monospace;
    font-size: 1.15rem;
    font-weight: bold;
    color: #2C2C2A;
}

.stat-label {
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    color: #666;
    margin-top: 0.1rem;
}

.stat-value.nm-good { color: #1B8A3D; }
.stat-value.nm-warn { color: #B8860B; }
.stat-value.nm-bad { color: #CE1126; }

/* horizontal mode only: the same 6 values, but as one-line "label value"
   pairs instead of stacked value-over-label tiles. 2 columns (matching
   vertical/List view's own Sell price/RF NM $/RF NM % left, Retail
   price/Retailer Landed Cost/Retailer IMU right pairing -- see the HTML
   comment above pricing_stats), not the 3-across layout this used to be:
   that packed the same 6 values into 2 rows instead of 3, which read
   shorter but broke the paired grouping every other view now uses. */
#items-grid.view-horizontal .pricing-stats-cell {
    grid-template-columns: repeat(2, auto);
    gap: 0.15rem 0.9rem;
}

#items-grid.view-horizontal .stat-block {
    flex-direction: row-reverse;
    align-items: baseline;
    justify-content: flex-end;
    gap: 0.3rem;
    white-space: nowrap;
}

#items-grid.view-horizontal .stat-value {
    font-size: 0.85rem;
}

#items-grid.view-horizontal .stat-label {
    margin-top: 0;
}

/* subtotal rows within ADDITIONAL INFORMATION -- shaded + bolded (via
   the "key" class) to stand out from the plain % input rows above them */
.grid-label[data-row="out_total_allowances_overhead"],
.grid-cell[data-row="out_total_allowances_overhead"],
.grid-label[data-row="out_total_cost_allocations"],
.grid-cell[data-row="out_total_cost_allocations"],
.grid-label[data-row="out_total_cost"],
.grid-cell[data-row="out_total_cost"] {
    background: #F1EFE8;
}

.grid-cell .output-value {
    font-family: Consolas, monospace;
    font-size: 0.9rem;
    width: 100%;
    text-align: right;
}

.grid-cell .output-value.key {
    font-weight: bold;
    color: #2C2C2A;
    font-size: 1rem;
}

/* RF NM% / Net Margin% traffic-light coloring: >=12% green, 8-12% amber,
   <8% red. Only applied to the NM% cells, not the $ amounts. */
.grid-cell .output-value.nm-good {
    color: #1B8A3D;
}

.grid-cell .output-value.nm-warn {
    color: #B8860B;
}

.grid-cell .output-value.nm-bad {
    color: #CE1126;
}

.pending-note {
    font-size: 0.75rem;
    font-style: italic;
    opacity: 0.7;
}

/* per-item "this one couldn't be calculated" note -- deliberately a
   warning color rather than plain .pending-note's muted gray, since this
   is actionable (missing/invalid input) rather than just "not filled in
   yet" -- and the top-of-page count banner that summarizes all of them. */
.item-calc-error {
    color: #B45309;
    font-style: normal;
    opacity: 1;
    font-size: 0.75rem;
    margin-top: 0.2rem;
}

/* Landed estimate's cell now holds a second line (the Walmart flag below)
   in vertical/horizontal view too -- List view's own tile layout already
   stacks every field's cells this way (see the big :is() rule above), but
   the base .grid-cell rule is a plain centered flex ROW, which would sit
   this new line beside the input instead of under it. */
.grid-cell[data-row="landed_estimate"] {
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
}

/* Adders' cell now holds a comment textarea under the $ input -- same
   column-instead-of-row override as Landed estimate's cell above, for the
   same reason (the base .grid-cell rule is a centered flex ROW). */
.grid-cell[data-row="adders"] {
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
}

textarea.adders-comment {
    font-family: Calibri, sans-serif;
    font-size: 0.75rem;
    width: 100%;
    min-height: 2.4em;
    box-sizing: border-box;
    resize: vertical;
}

/* Walmart landed-cost flag -- same warning color as .item-calc-error
   (actionable, not just "not filled in yet"), but its own class since this
   isn't a calc error: the item still prices out fine at Landed estimate=0,
   Sales just hasn't confirmed the real number for a Walmart quote yet. */
.landed-estimate-warning {
    display: block;
    color: #B45309;
    font-size: 0.7rem;
    margin-top: 0.25rem;
    cursor: help;
}

/* this rule's own display:block otherwise outranks the browser's default
   [hidden] UA rule at equal specificity (author styles always win ties
   against the UA sheet) -- same fix .items-list-rail[hidden] above needed
   for the same reason. Without this, the flag showed for EVERY customer
   (e.g. Academy Sports), not just Walmart, regardless of what JS set
   .hidden to. */
.landed-estimate-warning[hidden] {
    display: none;
}

.landed-estimate-main-row {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 0.3rem;
}

.landed-estimate-main-row input {
    flex: 1 1 6rem;
    min-width: 0;
}

/* mirrors .section-toggle's dark (cream-background) variant -- this cell
   sits in the cream INPUTS section, not one of the red section headers.
   ".input-form .landed-math-toggle" (2 classes), not just
   ".landed-math-toggle" (1 class) -- same reason .actions-menu-toggle
   below needs the same prefix: otherwise ".input-form button" (1 class +
   1 type, still more specific than a single bare class) wins and this
   rendered as a full-size red pill button instead of a small toggle,
   badly enough to get clipped by the cell's own width. */
/* a plain +/- square, same expanded/collapsed convention as the INPUTS/
   PRODUCT COST section toggles (.section-toggle-dark) rather than a
   labeled pill -- the action is still named, just in aria-label/title
   now instead of the button's own visible text (see setExpanded in
   wireLandedMathPanel and updateLandedMathToggleAllLabel, app.js).
   .landed-math-toggle-all-btn (the "Show all math" master toggle, up in
   the shared row-label cell) shares this exact look on purpose -- same
   symbol, same size, same meaning, just acting on every item at once
   instead of one. It's a separate class rather than reusing
   .landed-math-toggle itself so querySelectorAll(".landed-math-toggle")
   elsewhere keeps finding only the real per-item toggles, never the
   master button standing in front of them. */
.input-form .landed-math-toggle,
.input-form .landed-math-toggle-all-btn {
    flex: 0 0 auto;
    align-self: flex-start;
    width: 1.1rem;
    height: 1.1rem;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    background: none;
    border: 1px solid #2C2C2A;
    color: #2C2C2A;
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    line-height: 1;
    padding: 0;
    margin-right: 0;
    cursor: pointer;
}

.input-form .landed-math-toggle:hover,
.input-form .landed-math-toggle-all-btn:hover {
    background: #2C2C2A;
    color: #fff;
}

/* same [hidden]-vs-author-styles fix as .landed-estimate-warning above --
   the panel's own display:block (set once expanded via JS removing the
   [hidden] attribute) would otherwise tie with the UA [hidden] rule. */
.landed-math-panel[hidden] {
    display: none;
}

/* Label sits on its OWN full-width line above its input now, rather than
   sharing a row with it -- an item column is only ~190-210px wide, and a
   3-across Label|Input|Per-unit layout (the previous approach, a grid
   shared across the whole panel) squeezed Label down to almost nothing,
   badly enough to wrap words mid-letter ("Outbound ocean freig-ht"). No
   shared grid needed for alignment either: every .landed-math-input is a
   fixed width and always the FIRST thing in its own input-line, so they
   line up across rows for free (including the 1-input "Additional costs"
   row) without coordinating column tracks across siblings at all. */
.landed-math-panel {
    display: flex;
    flex-direction: column;
    gap: 0.35rem;
    margin-top: 0.4rem;
    padding: 0.5rem 0.55rem;
    background: #FBFAF6;
    border: 1px solid #E6E1D0;
    border-radius: 6px;
}

.landed-math-row {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
}

.landed-math-label {
    font-size: 0.72rem;
    color: #444;
}

.landed-math-input-line {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.landed-math-input {
    flex: 0 0 5.2rem;
    width: 5.2rem;
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    box-sizing: border-box;
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 0.2rem 0.35rem;
    text-align: right;
}

.landed-math-per-unit {
    flex: 1 1 auto;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
    font-family: Consolas, monospace;
    font-size: 0.68rem;
    color: #6b6b6b;
    text-align: right;
    white-space: nowrap;
}

.landed-math-note {
    font-size: 0.68rem;
    font-style: italic;
    color: #8a8a8a;
    margin: -0.15rem 0 0;
}

.landed-math-note strong {
    font-style: normal;
    color: #555;
}

.landed-math-formula {
    font-family: Consolas, monospace;
    font-size: 0.7rem;
    color: #5a5a52;
    text-align: right;
    background: #F1EFE8;
    border-radius: 4px;
    padding: 0.3rem 0.5rem;
    margin: 0;
}

/* --- Landed Estimate's row-LABEL cell (the shared 230px column, one per
   quote, not per item): "Show all math" + a bulk-fill mini form for
   Outbound Freight/Tariff & Duty. Column is only 230px wide (#items-grid's
   grid-template-columns), so everything here stacks instead of the
   per-item panel's 3-column grid. */
.grid-label.landed-estimate-label {
    flex-direction: column;
    align-items: stretch;
    gap: 0.4rem;
    padding: 0.45rem 0.5rem;
}

.landed-label-top-row {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 0.3rem;
}

/* same [hidden]-vs-author-styles fix this file already needs for
   .landed-estimate-warning/.landed-math-panel -- collapsed by default
   (pilot feedback: it was permanently inflating this shared label cell's
   row height for every item, not just when someone actually wanted it). */
.landed-bulk-fill[hidden] {
    display: none;
}

.landed-bulk-fill {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    margin-top: 0.35rem;
    padding: 0.4rem 0.45rem;
    background: #fff;
    border: 1px solid #ddd;
    border-radius: 5px;
}

.landed-bulk-fill-title {
    margin: 0;
    font-size: 0.62rem;
    font-weight: normal;
    color: #8a8a8a;
    letter-spacing: 0.02em;
}

.landed-bulk-field {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    font-size: 0.68rem;
    font-weight: normal;
    color: #555;
}

.landed-bulk-field input {
    font-family: Calibri, sans-serif;
    font-size: 0.78rem;
    font-weight: normal;
    width: 100%;
    box-sizing: border-box;
    border: 1px solid #ccc;
    border-radius: 4px;
    padding: 0.2rem 0.35rem;
}

/* same specificity note as .landed-toggle-all-btn above -- this one's a
   real "commit" action (fills every item), so it gets a small solid
   button instead of that one's outline, without going anywhere near
   ".input-form button"'s full toolbar size. */
.input-form .landed-bulk-apply-btn {
    background: #2C2C2A;
    color: #fff;
    border: none;
    border-radius: 4px;
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    font-weight: normal;
    padding: 0.3rem 0;
    margin-right: 0;
    cursor: pointer;
}

.input-form .landed-bulk-apply-btn:hover {
    background: #4a4a46;
}

.item-errors-banner {
    background: #FFF8E1;
    border: 1px solid #E0C060;
    border-radius: 4px;
    padding: 0.5rem 1rem;
    margin-bottom: 1rem;
    font-style: normal;
    opacity: 1;
}

/* field override marker: a persistent per-field indicator (not a one-time
   event banner) showing that THIS field's current value differs from what
   the reference data would generate right now for the active selections.
   Computed live off the database rather than a memory of past events, so
   it stays quiet immediately after a normal auto-fill (the field then
   matches what was just fetched) and stays correct across Save/Load.
   Neutral (dashed charcoal border + the same tan already used for section
   dividers), not amber -- amber is reserved for the RF NM%/Net Margin%
   traffic light, and reusing it here made an override marker readable as
   a margin warning at a glance. */
.grid-cell.field-override {
    border-left: 3px dashed #2C2C2A;
    background: #F1EFE8;
}

/* Stale: this field was auto-filled, never hand-typed, and the database's
   value has since moved on without anyone knowing -- distinct from a
   deliberate .field-override above (which should persist forever). Same
   neutral family (not amber, for the reasons noted above) but a solid
   border rather than dashed, to read as "needs a look" rather than
   "intentionally set this way". Use Sync to pull the new value in. */
.grid-cell.field-stale {
    border-left: 3px solid #2C2C2A;
    background: #F1EFE8;
}

/* Transient highlight for the "jump to this item" link from Summary (see
   applyPendingFocusFromSummary in app.js). .item-column itself is
   display:contents (see the .item-column rule above) and never renders a
   box of its own, so the highlight has to land on each real cell box
   underneath it instead -- applied to every .grid-cell in the column at
   once reads as the whole column being outlined. A plain toggle rather
   than an animated fade: JS removes the class outright after ~2s. */
.item-column.jump-highlight > .grid-cell {
    outline: 2px solid #CE1126;
    outline-offset: -2px;
}

.input-form .remove-item-btn,
.input-form .copy-column-btn {
    background: none;
    border: 1px solid #CE1126;
    color: #CE1126;
    padding: 0.2rem 0.6rem;
    cursor: pointer;
    font-size: 0.7rem;
    white-space: nowrap;
}

.grid-cell.item-header-cell {
    flex-direction: column;
    align-items: stretch;
    gap: 0.5rem;
    padding: 0.5rem 0.6rem;
    background: #F1EFE8;
}

/* horizontal mode only: Actions ▾ + Copy + Remove side by side instead of
   stacked -- stacked, this was the tallest content in an item's row (taller
   even than the compacted PRICING card above), which set the row height
   every plain single-line field then had to sit in. One row here is what
   gets a row down close to a single control's natural height. */
#items-grid.view-horizontal .grid-cell.item-header-cell {
    flex-direction: row;
    align-items: center;
    gap: 0.4rem;
    padding: 0.3rem 0.5rem;
}

/* Actions dropdown -- consolidates the Submitted/New Product/Exclude from
   Summary/Freeze checkboxes into one compact control (they used to be a
   flex-wrapped row that ate noticeable space, worse once columns got
   narrower). Same open/close-on-outside-click pattern as the Item/FOB
   checkbox filters (createCheckboxFilterController in app.js), just
   simpler -- no search box, no draft/commit/Apply step, since each
   checkbox already takes effect immediately. */
.actions-menu {
    position: relative;
}

/* ".input-form .actions-menu-toggle" (2 classes), not just
   ".actions-menu-toggle" (1 class) -- needs to out-specificity the
   generic ".input-form button" rule (1 class + 1 type = still more
   specific than a single bare class), which otherwise wins and leaves
   this looking like a full-size red toolbar button instead of a small
   per-item control. display:inline-flex (not flex) keeps it shrink-to-fit
   instead of stretching to the header cell's full width. */
.input-form .actions-menu-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.25rem;
    background: none;
    border: 1px solid #2C2C2A;
    color: #2C2C2A;
    padding: 0.15rem 0.4rem;
    margin-right: 0;
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    cursor: pointer;
}

.actions-menu-caret {
    font-size: 0.65rem;
}

.actions-menu.open .actions-menu-toggle {
    box-shadow: 0 0 0 2px rgba(44, 44, 42, 0.4) inset;
}

.actions-menu-panel {
    position: absolute;
    top: calc(100% + 0.3rem);
    left: 0;
    z-index: 20;
    width: 180px;
    background: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
    padding: 0.5rem;
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
}

.actions-menu-panel[hidden] {
    display: none;
}

.submitted-checkbox-label,
.new-product-checkbox-label,
.exclude-summary-checkbox-label,
.frozen-checkbox-label,
.variant-toggle-checkbox-label {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    white-space: nowrap;
    cursor: pointer;
}

/* Sits in the Actions dropdown instead of the item-actions-row (Copy/
   Remove) -- at typical column widths a third button there ran past the
   column's edge. A top divider sets it apart from the checkboxes above it,
   since it's a one-off action, not a persistent toggle. */
.actions-menu-panel .sync-column-btn {
    background: none;
    border: none;
    border-top: 1px solid #eee;
    color: #2C2C2A;
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    text-align: left;
    padding: 0.4rem 0 0;
    margin-top: 0.1rem;
    cursor: pointer;
}

.actions-menu-panel .sync-column-btn:hover {
    color: #A6192E;
}

.item-actions-row {
    display: flex;
    gap: 0.4rem;
}

/* Grip handle for drag-to-reorder (wireColumnDragAndDrop in app.js) --
   deliberately neutral grey rather than Copy/Remove's red outline, since
   it isn't a destructive or one-off action. cursor:grab (not the button
   default pointer) is the one hint that it's draggable at all. Lives in
   the frozen Customer/Item mirror bar now (.sticky-id-cell/.sticky-h-id-cell
   below), not down in the real Customer cell -- pilot feedback wanted it
   reachable without scrolling back up to the top of a tall column. */
.input-form .item-drag-handle {
    background: none;
    border: 1px solid #999;
    color: #666;
    padding: 0.2rem 0.5rem;
    cursor: grab;
    font-size: 0.75rem;
    line-height: 1;
}

.input-form .item-drag-handle:active {
    cursor: grabbing;
}

/* Live feedback while a column is being dragged -- .item-column itself is
   display:contents (never a rendered box, see that rule above), so the
   class has to land on this class name via the item-column and be applied
   to every cell underneath it at once, same technique .jump-highlight
   above already uses. */
.item-column.dragging-column > * {
    opacity: 0.4;
}

/* frozen item: fields are locked (readonly/pointer-events blocked, but
   still submit their current value) and visually greyed so it's obvious
   at a glance which items are "finished" vs still editable */
.item-frozen .item-header-cell {
    background: #E3E6EA;
}

.locked-control {
    pointer-events: none;
}

input.locked-control,
textarea.locked-control {
    background: #eee;
    color: #666;
}

select.locked-control {
    opacity: 0.6;
}

/* collapsible ADDITIONAL INFORMATION detail rows */
.additional-info-row[hidden] {
    display: none;
}

.results {
    margin-top: 2rem;
}

.submitted-filter {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 0.75rem;
    margin-bottom: 1rem;
}

.submitted-filter select {
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
    padding: 0.4rem;
}

.submitted-filter .export-btn {
    display: inline-block;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    padding: 0.4rem 0.8rem;
    border-radius: 4px;
    border: 1px solid #CE1126;
    background: #CE1126;
    color: white;
    text-decoration: none;
}

.submitted-filter .export-btn:hover {
    background: #A6192E;
}

.table-scroll {
    overflow-x: auto;
    max-width: 100%;
}

.submitted-table {
    border-collapse: collapse;
    font-family: Consolas, monospace;
    font-size: 0.85rem;
}

.submitted-table th,
.submitted-table td {
    padding: 0.4rem 0.8rem;
    border-bottom: 1px solid #ddd;
    text-align: right;
    white-space: nowrap;
}

/* alignment is driven by each column's actual data kind (text vs
   currency/pct/numeric), not position -- safe regardless of column order
   or count, which differs between the full detail table and Summary's
   reduced column set */
.submitted-table .col-left {
    text-align: left;
    font-family: Calibri, sans-serif;
}

.submitted-table thead th {
    font-family: Cambria, serif;
    background: #2C2C2A;
    color: white;
    border-bottom: none;
    position: sticky;
    top: 0;
}

/* Summary page: header reads red/white, same combo as the toolbar's
   Calculate/Export/Save buttons, instead of the full-detail table's
   charcoal header */
.summary-table.submitted-table thead th {
    background: #CE1126;
    color: white;
}

/* Submitted Quotes: one card per logged submission -- a compact always-
   visible "glance" header (identity + top-line pricing + the Accepted/
   Effective Date controls) with the full SUBMITTED_COLUMNS detail collapsed
   underneath, rather than the old full-width table that put ~40 columns in
   front of someone regardless of whether they needed them. Reuses the
   Summary detail-panel's tan/border/tile vocabulary (.detail-rate-table
   etc.) so the expanded view still reads as the same visual language. */
.quote-card-list {
    display: flex;
    flex-direction: column;
    gap: 0.6rem;
}

.quote-card {
    border: 1px solid #E4E2D8;
    border-radius: 6px;
    background: #FFFFFF;
    border-left: 4px solid transparent;
}

/* Left border turns green the moment a card is checked Accepted -- a quick
   scan down the collapsed list should be enough to see what's closed,
   without opening every card or reading the (small) checkbox itself. */
.quote-card.is-accepted {
    border-left-color: #1B8A3D;
}

/* Amber left border for a still-open Finance flag -- same "scan the list
   without opening every card" reasoning as .is-accepted's green above.
   Accepted (customer said yes) and needs-finance-review (Finance hasn't
   signed off) are independent, so a card can show both borders' worth of
   state at once -- this rule just wins on the left edge itself when open,
   since an unresolved flag is the more urgent thing to notice. */
.quote-card.needs-finance-review {
    border-left-color: #B08600;
}

/* Submitted Quotes: the All Submissions / Needs Finance Review tabs --
   plain links (a real page navigation, not a client-side re-render, since
   the review filter is a query param handled server-side) styled as tabs. */
.review-tabs {
    display: flex;
    gap: 1.2rem;
    border-bottom: 1px solid #ccc;
    margin-bottom: 1rem;
}

.review-tab {
    padding: 0.5rem 0.2rem;
    font-family: Calibri, sans-serif;
    font-size: 0.95rem;
    color: #5F5E5A;
    text-decoration: none;
    border-bottom: 3px solid transparent;
}

.review-tab-active {
    color: #A6192E;
    font-weight: bold;
    border-bottom-color: #CD0016;
}

/* The Finance strip -- sits between a card's glance header and its
   (still-collapsed) full detail panel, always visible (not gated behind
   the expand toggle) since a flag and its resolution are exactly the thing
   this page exists to surface. */
.quote-card-finance {
    padding: 0.5rem 0.9rem 0.7rem;
    border-top: 1px solid #E4E2D8;
    background: #FFFDF6;
    display: flex;
    flex-direction: column;
    gap: 0.45rem;
}

.finance-flag-badges {
    display: flex;
    flex-wrap: wrap;
    gap: 0.3rem;
}

.status-flagged {
    background: #FAEEDA;
    color: #854F0B;
}

.status-flag-reason {
    background: #F1EFE8;
    color: #5F5E5A;
}

.status-cleared {
    background: #EAF3DE;
    color: #27500A;
}

/* Deliberately a different color from .status-cleared (Finance Approved) --
   this one means "nothing tripped the checklist, no human looked at it,"
   which shouldn't read as visually identical to an actual Finance sign-off. */
.status-system-approved {
    background: #E7F0FA;
    color: #1B4D89;
}

/* The finance-status column sitting directly on the glance line (next to
   NM%) -- badge on top, and (once reviewed) who/when + the note stacked
   right underneath it, all in one column instead of a separate strip
   further down the card. .quote-card-glance's default flex
   align-items:stretch would otherwise pull this to the row's full height;
   center it instead so it sits like the other glance fields regardless of
   how tall the row gets. */
/* A FIXED width (not auto, not max-width) is what actually matters here --
   every other glance field uses flex-grow with a 0 basis specifically so
   its proportional share of the row is the same on every card (see the
   comment on .quote-card-glance's fields above), which only lines up
   column-to-column because every card sends the same set of flex-grow
   ratios through the same total. An auto-width item breaks that: this
   column's content varies a lot card to card (a bare badge vs. badge +
   reviewer/date/note), so letting it size to content shifted every field
   after it (Accepted, Effective Date, Submitted At/By) sideways by a
   different amount on every card. Fixed width keeps its footprint (and so
   everything after it) identical regardless of how much text is inside --
   a long reviewer note just wraps within it instead of widening the column.
   The badge itself stays nowrap so "Needs Finance Review" never breaks
   mid-word even though the column is narrower than that button's own
   comfortable width. */
.finance-glance {
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    gap: 0.2rem;
    flex: 0 0 11rem;
    width: 11rem;
}

.finance-glance-badge {
    white-space: nowrap;
}

.finance-glance-resolved {
    display: flex;
    flex-direction: column;
    gap: 0.1rem;
    font-size: 0.7rem;
    color: #5F5E5A;
}

.finance-glance-resolved-note {
    font-style: italic;
}

.status-escalated {
    background: #FCEBEB;
    color: #791F1F;
}

.finance-review-action {
    display: flex;
    flex-direction: column;
    gap: 0.4rem;
    max-width: 34rem;
}

.finance-note-input {
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    padding: 0.4rem 0.5rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    resize: vertical;
}

.finance-review-buttons {
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

.finance-clear-btn,
.finance-escalate-btn {
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    font-weight: bold;
    border-radius: 4px;
    padding: 0.35rem 0.9rem;
    cursor: pointer;
}

.finance-clear-btn {
    background: #1B8A3D;
    color: #fff;
    border: none;
}

.finance-escalate-btn {
    background: none;
    color: #A6192E;
    border: 1px solid #A6192E;
}

.finance-review-resolved {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    font-size: 0.8rem;
    color: #2C2C2A;
}

.finance-resolved-by {
    font-weight: bold;
}

.finance-resolved-note {
    color: #5F5E5A;
}

.quote-card-header {
    display: flex;
    align-items: center;
    gap: 0.75rem;
    padding: 0.6rem 0.9rem;
    font-family: Calibri, sans-serif;
}

.card-toggle {
    background: none;
    border: none;
    color: #2C2C2A;
    font-size: 0.9rem;
    line-height: 1;
    cursor: pointer;
    padding: 0.2rem 0.3rem;
    flex: 0 0 auto;
}

/* Key info (Customer/Item/pricing) first, the Accepted/Effective Date
   controls inline right after (see .quote-card-inline-status below --
   it's slotted into this same row, right after the RF NM% field, not off
   in a separate right-aligned block), then Submitted At/By and Quote Name
   trailing at the end -- see SUBMITTED_CARD_HEADER_ORDER.

   flex-grow with a 0 basis (not fixed widths, not CSS grid fr columns) is
   what a field's proportion below actually means: every card's row shares
   whatever space IS available in that same proportion, so fields still
   line up from one card to the next without ever risking an overflow --
   flex-shrink lets them give way instead. flex-wrap (rather than
   overflow-x scroll, which an earlier version of this layout used) is the
   fallback once there's truly not enough room, e.g. a narrow window: a
   field drops to its own line instead of forcing a per-card scrollbar. */
.quote-card-glance {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    row-gap: 0.3rem;
    column-gap: 0.6rem;
    flex: 1 1 auto;
    min-width: 0;
}

.glance-field {
    display: flex;
    flex-direction: column;
    flex: 1 1 0;
    min-width: 3rem;
}

.glance-field[data-key="customer"] { flex-grow: 1.3; }
.glance-field[data-key="item_model"] { flex-grow: 0.9; }
.glance-field[data-key="sell_price"] { flex-grow: 0.7; }
.glance-field[data-key="retail_price"] { flex-grow: 0.7; }
.glance-field[data-key="nm_pct"] { flex-grow: 0.6; }
.glance-field[data-key="submitted_at"] { flex-grow: 0.85; }
.glance-field[data-key="submitted_by"] { flex-grow: 0.95; }
.glance-field[data-key="quote_name"] { flex-grow: 1.3; }

.glance-label {
    font-size: 0.65rem;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: #888780;
    white-space: nowrap;
}

/* Same font/size for every field in the header -- one uniform look for
   the whole row instead of switching typefaces per field. Weight is the
   only differentiator Customer/Item (the new lead-in fields) get, to
   read as this card's headline without the row looking patched together. */
.glance-value {
    font-size: 0.85rem;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.glance-field[data-key="customer"] .glance-value,
.glance-field[data-key="item_model"] .glance-value {
    font-weight: bold;
}

/* The Accepted checkbox + Effective Date field, slotted directly into the
   glance row's natural flow (right after RF NM%) rather than pushed to a
   separate right-aligned block -- doesn't grow/shrink like the data
   fields around it (its content, a checkbox and a native date input,
   isn't meaningfully truncatable the way text is). */
.quote-card-inline-status {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex: 0 0 auto;
    font-size: 0.8rem;
}

.accepted-toggle,
.effective-date-field {
    display: flex;
    align-items: center;
    gap: 0.3rem;
    cursor: pointer;
    white-space: nowrap;
}

.effective-date-field input {
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    padding: 0.15rem 0.3rem;
    width: 8.5rem;
}

.save-status {
    font-size: 0.7rem;
    color: #5F5E5A;
    min-width: 3.5rem;
    transition: opacity 0.6s ease;
}

.save-status-fade {
    opacity: 0;
}

.save-status-error {
    color: #CE1126;
    font-weight: bold;
}

.quote-card-detail {
    border-top: 1px solid #E4E2D8;
    background: #F7F7F5;
    padding: 0.6rem 0.9rem;
}

.quote-card-detail .detail-rate-table {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    border: 1px solid #E4E2D8;
    border-radius: 4px;
    overflow: hidden;
    background: #FFFFFF;
}

.quote-card-detail .detail-rate-row {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.3rem 0.6rem;
    border-bottom: 1px solid #EDEBE2;
    border-right: 1px solid #EDEBE2;
    font-size: 0.8rem;
}

.quote-card-detail .detail-rate-row:nth-child(2n) {
    border-right: none;
}

.quote-card-detail .detail-rate-label {
    color: #5F5E5A;
}

/* One font throughout the panel (matching the header) rather than
   switching to Consolas for values -- the combined "$X (Y%)" rows below
   read as one sentence-like value now, not a column of numbers that needs
   monospace to line up. */
.quote-card-detail .detail-rate-sub {
    color: #5F5E5A;
}

/* Notes is free text, not a short label:value pair -- span both grid
   columns and stack label above value instead of squeezing a paragraph
   into half a row next to its label. */
.quote-card-detail .detail-rate-row-wide {
    grid-column: 1 / -1;
    flex-direction: column;
    align-items: flex-start;
    border-right: none;
}

@media (max-width: 900px) {
    .quote-card-detail .detail-rate-table {
        grid-template-columns: 1fr;
    }

    .quote-card-detail .detail-rate-row {
        border-right: none;
    }
}

.summary-toolbar {
    display: flex;
    align-items: center;
    gap: 1rem;
    margin-bottom: 1rem;
}

.nm-filter-label {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
    white-space: nowrap;
    cursor: pointer;
}

/* Summary's Item filter -- same open/close-panel shape as the main grid's
   .checkbox-filter widgets (toggle button + absolute panel, box-shadow on
   open), but single-select rows instead of checkboxes+Apply, and each row
   carries the same badges/chip the table itself uses (variant/FOB/vendor,
   NM% chip) instead of flattening to plain option text -- a native <select>
   can't render that formatting inside its options. */
.item-filter {
    position: relative;
    display: inline-flex;
    align-items: stretch;
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
}

.item-filter-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    background: none;
    border: 1px solid #2C2C2A;
    color: #2C2C2A;
    padding: 0.4rem 0.8rem;
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
    cursor: pointer;
    max-width: 22rem;
}

/* Lets an active item filter be cleared without reopening the panel --
   butted up against the toggle (shared border, no gap) so the two read as
   one control rather than two separate buttons. */
.item-filter-clear {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 1.6rem;
    background: #F1EFE8;
    border: 1px solid #2C2C2A;
    border-left: none;
    color: #2C2C2A;
    font-size: 1rem;
    line-height: 1;
    cursor: pointer;
}

.item-filter-clear:hover {
    background: #e2ded2;
}

/* the rule above's own display:inline-flex (needed once shown) otherwise
   outranks the browser's default [hidden] UA rule -- same fix
   .items-list-rail[hidden] elsewhere in this file needed for the same
   reason */
.item-filter-clear[hidden] {
    display: none;
}

.item-filter-toggle-label {
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}

.item-filter-caret {
    font-size: 0.7rem;
    flex-shrink: 0;
    color: #888780;
}

.item-filter.open .item-filter-toggle {
    box-shadow: 0 0 0 2px rgba(44, 44, 42, 0.4) inset;
}

.item-filter-panel {
    position: absolute;
    top: calc(100% + 0.3rem);
    left: 0;
    z-index: 20;
    width: 22rem;
    background: white;
    border: 1px solid #ccc;
    border-radius: 4px;
    box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
    padding: 0.35rem;
}

.item-filter-search {
    width: 100%;
    box-sizing: border-box;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    padding: 0.35rem 0.5rem;
    margin-bottom: 0.4rem;
    border: 1px solid #ccc;
    border-radius: 3px;
}

.item-filter-list {
    max-height: 320px;
    overflow-y: auto;
}

.item-filter-empty {
    padding: 0.6rem;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    color: #888780;
    text-align: center;
}

.item-filter-row {
    padding: 0.4rem 0.6rem;
    border-radius: 3px;
    cursor: pointer;
}

.item-filter-row:hover {
    background: #F1EFE8;
}

.item-filter-row.is-selected {
    background: #F1EFE8;
}

.item-filter-row-all {
    border-bottom: 1px solid #eee;
    margin-bottom: 0.3rem;
    padding-bottom: 0.55rem;
    font-weight: bold;
}

.item-filter-row-item {
    font-weight: bold;
    font-size: 0.9rem;
    color: #2C2C2A;
}

.item-filter-row-source {
    margin-top: 0.3rem;
    display: flex;
    align-items: center;
    gap: 0.35rem;
    flex-wrap: wrap;
}

/* Same look as .summary-table .variant-lbl/.ship-badge/.vendor-lbl, just not
   scoped to the table -- this panel sits in the toolbar above it. */
.item-filter-variant {
    display: inline-block;
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    font-weight: bold;
    color: #8A5A00;
    background: #FCF3DC;
    padding: 2px 6px;
    border-radius: 3px;
}

.item-filter-fob {
    display: inline-block;
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    color: #2C2C2A;
    background: #EFEFEF;
    padding: 2px 8px;
    border-radius: 3px;
}

.item-filter-vendor {
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    color: #888780;
}

.item-filter-row-metrics {
    margin-top: 0.35rem;
    display: flex;
    align-items: center;
    gap: 0.5rem;
}

/* shrunk from the shared .nm-chip default (tuned for Summary's one-per-row
   table) to fit this panel's more compact rows -- same idea as
   .items-list-row .nm-chip's override */
.item-filter-row-metrics .nm-chip {
    font-size: 0.8rem;
    padding: 2px 8px;
    border-radius: 5px;
}

.item-filter-imu {
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    color: #555;
}

/* Sort by field (Item #/RF NM%/Sell Price/Retail Price/Retailer IMU%) +
   direction toggle -- two controls butted together (shared border, no gap)
   so they read as one "Sort by" unit, same visual idea as the Item filter's
   toggle+clear pairing above. */
.sort-control {
    display: inline-flex;
    align-items: stretch;
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
}

#sort-key-select {
    background: none;
    border: 1px solid #2C2C2A;
    border-right: none;
    color: #2C2C2A;
    padding: 0.4rem 0.6rem;
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
    cursor: pointer;
}

#sort-direction-btn {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 2rem;
    background: none;
    border: 1px solid #2C2C2A;
    color: #2C2C2A;
    font-size: 0.75rem;
    cursor: pointer;
}

#sort-direction-btn:hover {
    background: #F1EFE8;
}

#expand-all-btn {
    background: none;
    border: 1px solid #2C2C2A;
    color: #2C2C2A;
    padding: 0.4rem 0.8rem;
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
    cursor: pointer;
}

#expand-all-btn:disabled {
    opacity: 0.5;
    cursor: default;
}

/* RF NM% traffic-light coloring in the Summary/Submitted Quotes tables --
   same thresholds/colors as the main grid's .output-value.nm-* classes
   (see app.py's _nm_class), just re-scoped to a plain <td> instead of the
   grid's .output-value span */
.submitted-table td.nm-good {
    color: #1B8A3D;
    font-weight: bold;
}

.submitted-table td.nm-warn {
    color: #B8860B;
    font-weight: bold;
}

.submitted-table td.nm-bad {
    color: #CE1126;
    font-weight: bold;
}

/* Summary page: the Item filter and/or NM checkbox can legitimately leave
   zero rows visible (as opposed to baseGroups itself being empty, which
   Jinja handles separately with its own "All items are excluded" note) --
   without this the table would just look broken, a header over nothing. */
.summary-table .no-results-cell {
    text-align: center;
    padding: 1.5rem 1rem;
    font-size: 0.85rem;
    font-style: italic;
    opacity: 0.7;
}

.link-btn {
    background: none;
    border: none;
    padding: 0;
    font-family: inherit;
    font-size: inherit;
    font-style: inherit;
    color: #CE1126;
    text-decoration: underline;
    cursor: pointer;
}

/* Summary page: Item cell rowspans over its variant rows (same item at
   different ship points/vendors) -- vertical-align keeps the label from
   sticking to the top of a multi-row span */
.summary-table td[rowspan] {
    vertical-align: middle;
}

/* Item cell links out to that item's submitted-quote history -- kept in
   the same monospace/near-black as the rest of the table (not the app's
   red link color, which reads as an alert/error in a data table) with an
   underline as the only "this is clickable" signal */
.summary-table td[rowspan] a {
    color: inherit;
    text-decoration: underline;
}

.summary-table td[rowspan] a:hover {
    color: #A6192E;
}

/* the variant pill is the first thing in the Source cell (before the FOB
   badge) since it's what actually distinguishes rows that share the same
   Item # in the group above -- same amber pill look as the grid's
   .item-variant-input so a reviewer recognizes it as "the same kind of
   label" across both screens. */
.summary-table .variant-lbl {
    display: block;
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    font-weight: bold;
    color: #8A5A00;
    background: #FCF3DC;
    padding: 2px 6px;
    border-radius: 3px;
    margin-bottom: 3px;
    white-space: normal;
}

.summary-table .ship-badge {
    display: inline-block;
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    color: #2C2C2A;
    background: #EFEFEF;
    padding: 2px 8px;
    border-radius: 3px;
    white-space: nowrap;
}

.summary-table .vendor-lbl {
    display: block;
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    color: #888780;
    margin-top: 3px;
    white-space: normal;
}

/* Per-item cost breakdown: a chevron in its own narrow column expands a
   detail row under that item (see the .detail-row group below) -- kept as
   a real <button> not just a symbol so it's keyboard/screen-reader
   operable, but sized/reset to disappear into the cell visually */
.summary-table .detail-toggle-cell {
    width: 1.5rem;
    padding-right: 0;
}

.summary-table .detail-toggle {
    background: none;
    border: none;
    color: #2C2C2A;
    font-size: 0.8rem;
    line-height: 1;
    cursor: pointer;
    padding: 0.2rem 0.3rem;
}

.summary-table .detail-row td {
    background: #F7F7F5;
    white-space: normal;
}

.summary-table .detail-panel {
    display: flex;
    flex-direction: column;
    gap: 0.5rem;
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    padding: 0.25rem 0;
}

.summary-table .detail-section-label {
    font-size: 0.65rem;
    font-weight: bold;
    letter-spacing: 0.03em;
    text-transform: uppercase;
    color: #5F5E5A;
}

.summary-table .detail-identity-line {
    display: flex;
    align-items: baseline;
    gap: 0.6rem;
    font-size: 0.85rem;
}

.summary-table .detail-identity-name {
    font-weight: bold;
    color: #2C2C2A;
}

.summary-table .detail-identity-sub {
    font-size: 0.7rem;
    color: #888780;
}

.summary-table .detail-badge {
    font-size: 0.7rem;
    background: #F1EFE8;
    padding: 1px 6px;
    border-radius: 3px;
}

/* Core Inputs: a responsive tile grid rather than a fixed column count --
   the field count here varies (e.g. Battery Defective only matters for
   e-bike/battery-op items), so a fixed 3- or 4-column grid would either
   leave an awkward gap or force wrapping mid-row */
.summary-table .core-input-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: 0.6rem;
}

.summary-table .core-input-tile {
    border-left: 3px solid transparent;
    padding: 0.1rem 0.4rem;
}

.summary-table .core-input-value {
    font-family: Consolas, monospace;
    font-size: 0.85rem;
}

.summary-table .core-input-label {
    font-size: 0.7rem;
    color: #888780;
}

/* Shared by Core Inputs' rate table (Tariff/CFee/Battery Defective) and
   Retailer Info's allowance table -- label:value rows in a two-column table
   rather than tiles, since both are compact rate stacks (3 and 8 fields
   respectively) that would sprawl further as tiles than they do as a table. */
.summary-table .detail-rate-table {
    display: grid;
    grid-template-columns: repeat(2, 1fr);
    border: 1px solid #E4E2D8;
    border-radius: 4px;
    overflow: hidden;
}

.summary-table .detail-rate-row {
    display: flex;
    justify-content: space-between;
    gap: 0.5rem;
    padding: 0.3rem 0.6rem;
    border-bottom: 1px solid #EDEBE2;
    border-right: 1px solid #EDEBE2;
    border-left: 3px solid transparent;
}

.summary-table .detail-rate-row:nth-child(2n) {
    border-right: none;
}

.summary-table .detail-rate-label {
    color: #5F5E5A;
}

.summary-table .detail-rate-value {
    font-family: Consolas, monospace;
}

/* Override marker: same dashed-border/tan-background convention as the main
   grid's .field-override (see the field-override rule under the grid-cell
   styles above) -- re-scoped to these Summary tile/row shapes instead of a
   grid-cell, but meant to read as "the same kind of signal" at a glance */
.summary-table .core-input-tile.field-override,
.summary-table .detail-rate-row.field-override {
    border-left-color: #2C2C2A;
    border-left-style: dashed;
    background: #F1EFE8;
}

.summary-table .total-cost-line {
    display: flex;
    justify-content: space-between;
    align-items: center;
    background: #F1EFE8;
    border-radius: 4px;
    padding: 0.4rem 0.6rem;
    font-size: 0.8rem;
}

.summary-table .total-cost-value {
    font-family: Consolas, monospace;
    font-weight: bold;
    font-size: 0.9rem;
}

/* White rather than the panel's off-white/tan -- free-text notes read as a
   card sitting on top of the panel, not another rate stack blending into
   the same tan background as everything else. */
.summary-table .detail-notes-text {
    background: #FFFFFF;
    border: 1px solid #E4E2D8;
    border-radius: 4px;
    padding: 0.4rem 0.6rem;
    font-size: 0.8rem;
    white-space: pre-wrap;
}

.summary-table .detail-recap-divider {
    border-top: 1px solid #D8D5C9;
}

.summary-table .recap-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(110px, 1fr));
    gap: 0.6rem;
}

.summary-table .recap-tile {
    background: #F1EFE8;
    border-radius: 4px;
    padding: 0.4rem 0.6rem;
}

.summary-table .recap-label {
    font-size: 0.7rem;
    color: #5F5E5A;
}

.summary-table .recap-value {
    font-family: Consolas, monospace;
    font-size: 0.9rem;
}

/* Retail Price/Retailer IMU% are what the retailer sees, not RF's own
   margin -- a distinct blue keeps them from reading as part of the
   green/amber/red NM traffic light next to them. */
.summary-table .recap-tile.recap-tile-retailer {
    background: #E3EDF7;
}

.summary-table .recap-tile.recap-tile-retailer .recap-label,
.summary-table .recap-tile.recap-tile-retailer .recap-value {
    color: #1F4E79;
}

/* recap's RF NM%/NM$ tiles reuse the same traffic-light thresholds as the
   rest of the app (see app.py's _nm_class) -- tinted the same as the
   .nm-chip below so the two headline numbers read as one signal even
   though they're in different parts of the expanded row */
.summary-table .recap-tile.nm-good {
    background: #EAF3DE;
}

.summary-table .recap-tile.nm-good .recap-label,
.summary-table .recap-tile.nm-good .recap-value {
    color: #27500A;
}

.summary-table .recap-tile.nm-warn {
    background: #FAEEDA;
}

.summary-table .recap-tile.nm-warn .recap-label,
.summary-table .recap-tile.nm-warn .recap-value {
    color: #854F0B;
}

.summary-table .recap-tile.nm-bad {
    background: #FCEBEB;
}

.summary-table .recap-tile.nm-bad .recap-label,
.summary-table .recap-tile.nm-bad .recap-value {
    color: #791F1F;
}

/* Bigger, filled chip for a headline NM% figure -- used on Summary's
   always-visible row (it's the one number in that table a reviewer
   scanning the whole quote is most likely to be looking for) and on List
   view's item rail rows. Not scoped under .summary-table -- both callers
   share this exact look. */
.nm-chip {
    display: inline-block;
    font-family: Consolas, monospace;
    font-weight: bold;
    font-size: 1rem;
    padding: 4px 10px;
    border-radius: 6px;
}

.nm-chip.nm-good {
    background: #EAF3DE;
    color: #27500A;
}

.nm-chip.nm-warn {
    background: #FAEEDA;
    color: #854F0B;
}

.nm-chip.nm-bad {
    background: #FCEBEB;
    color: #791F1F;
}

/* Same reasoning as .nm-chip above -- shared between Summary's rows and
   List view's item rail rows. */
.status-badge {
    display: inline-block;
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    padding: 1px 6px;
    border-radius: 3px;
    margin-top: 3px;
    margin-right: 3px;
}

.status-submitted {
    background: #EAF3DE;
    color: #27500A;
}

.status-frozen {
    background: #F1EFE8;
    color: #444441;
}

/* Submitted Quotes card header -- same green as .status-submitted (this
   app's one established "positive" status color). display:none by default,
   shown only inside an .is-accepted card (see .quote-card.is-accepted
   below) -- NOT via this span's own `hidden` attribute: the base
   .status-badge rule above sets `display: inline-block` on every
   .status-badge regardless of source order/specificity, since author CSS
   always outranks the browser's own `[hidden] { display: none }` default,
   so relying on `hidden` here would show the badge on every card
   unconditionally (the bug this replaced). */
.status-accepted-badge {
    display: none;
    background: #EAF3DE;
    color: #27500A;
}

.quote-card.is-accepted .status-accepted-badge {
    display: inline-block;
}

/* Sits right next to the expand chevron in the same narrow toggle cell --
   plain text arrow (not an icon font glyph) since this app has no icon
   library loaded, matching the existing "&larr; Back to Price Calc" link's
   own use of a text arrow entity. */
.summary-table .jump-to-calc-btn {
    background: none;
    border: none;
    color: #888780;
    font-size: 0.8rem;
    line-height: 1;
    cursor: pointer;
    padding: 0.2rem 0.3rem;
}

.summary-table .jump-to-calc-btn:hover {
    color: #2C2C2A;
}

/* Frozen header: .table-scroll's overflow-x:auto forces its overflow-y to
   auto too (the CSS overflow spec ties the two axes together), which
   already makes it the nearest scrolling ancestor for the sticky thead
   th's below -- but with no height cap it's only ever as tall as its
   content, so it never actually scrolls internally and the header has
   nothing to stick against as the page itself scrolls. Capping the height
   here is what makes it the container that scrolls, which is what
   thead th's existing position:sticky needs to have any effect. */
.summary-page .table-scroll {
    max-height: 75vh;
}

.print-btn {
    background: #CE1126;
    color: white;
    border: none;
    padding: 0.5rem 1.2rem;
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
    cursor: pointer;
}

/* printing to PDF is just the browser's native print dialog with
   destination "Save as PDF" -- hide the on-screen chrome (nav links, the
   print button itself) and force the table header's background to survive
   print, since browsers strip backgrounds by default */
@page {
    size: landscape;
}

@media print {
    .print-hide {
        display: none !important;
    }

    body {
        margin: 0.4in;
    }

    .submitted-table thead th {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    .submitted-table td.nm-good,
    .submitted-table td.nm-warn,
    .submitted-table td.nm-bad {
        -webkit-print-color-adjust: exact;
        print-color-adjust: exact;
    }

    /* Summary printout: the on-screen table scrolls horizontally and uses
       nowrap/monospace sizing meant for a monitor, which pushes 13 columns
       past one landscape page's width. For print specifically, let the
       scroll container size to content instead of clipping it, and shrink
       the table down to the actual printable width so every column lands
       on a single page rather than being cut off or wrapping to a second
       page. */
    .summary-table.submitted-table {
        table-layout: fixed;
    }

    .table-scroll {
        overflow: visible;
        width: 100%;
    }

    .summary-table.submitted-table th,
    .summary-table.submitted-table td {
        white-space: normal;
        word-break: break-word;
        font-size: 0.65rem;
        padding: 0.15rem 0.3rem;
    }
}

/* admin page */
.admin-login-form {
    display: flex;
    flex-direction: column;
    gap: 0.75rem;
    max-width: 260px;
}

.admin-login-form label {
    display: flex;
    flex-direction: column;
    gap: 0.3rem;
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
}

.admin-login-form input {
    padding: 0.5rem;
    font-family: Calibri, sans-serif;
}

.admin-table {
    border-collapse: collapse;
    font-family: Consolas, monospace;
    font-size: 0.85rem;
    margin-bottom: 1rem;
}

.admin-table th,
.admin-table td {
    padding: 0.4rem 0.6rem;
    border-bottom: 1px solid #ddd;
    text-align: right;
    white-space: nowrap;
}

.admin-table .col-left {
    text-align: left;
    font-family: Calibri, sans-serif;
}

.admin-table thead th {
    font-family: Cambria, serif;
    background: #2C2C2A;
    color: white;
    border-bottom: none;
}

.admin-table input[type="number"],
.admin-table input[type="text"] {
    width: 80px;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
}

.admin-delete-btn {
    background: none;
    border: 1px solid #CE1126;
    color: #CE1126;
    padding: 0.2rem 0.5rem;
    cursor: pointer;
    font-size: 0.7rem;
}

.admin-add-allowance {
    margin-bottom: 1.5rem;
    background: #F1EFE8;
    padding: 0.75rem 1rem;
}

.admin-add-allowance summary {
    cursor: pointer;
    font-family: Calibri, sans-serif;
    font-weight: bold;
}

.admin-allowance-form {
    display: flex;
    flex-wrap: wrap;
    gap: 0.75rem;
    align-items: flex-end;
    margin-top: 0.75rem;
}

.admin-allowance-form label {
    display: flex;
    flex-direction: column;
    gap: 0.2rem;
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
}

.admin-allowance-form input,
.admin-allowance-form select {
    padding: 0.3rem;
    font-family: Calibri, sans-serif;
}

/* ================= saved quotes page ================= */
.saved-quotes-layout {
    display: flex;
    gap: 1.25rem;
    align-items: flex-start;
}

.saved-quotes-sidebar {
    flex: 0 0 240px;
    background: #F1EFE8;
    border: 1px solid #ddd;
    border-radius: 6px;
    padding: 0.6rem;
    max-height: 640px;
    overflow-y: auto;
}

.saved-quotes-main {
    flex: 1;
    min-width: 0;
}

.folder-heading {
    font-family: Calibri, sans-serif;
    font-size: 0.7rem;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    opacity: 0.55;
    font-weight: bold;
    padding: 0.6rem 0.5rem 0.35rem;
}

.sidebar-divider {
    border: none;
    border-top: 1px solid #ddd;
    margin: 0.5rem 0.2rem;
}

.folder-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.4rem 0.5rem;
    border-radius: 5px;
    cursor: pointer;
    font-family: Calibri, sans-serif;
    font-size: 0.87rem;
    white-space: nowrap;
}

.folder-row:hover {
    background: #E4E1D8;
}

.folder-row.active {
    background: #CE1126;
    color: white;
}

.folder-row.archive-row.active {
    background: #2C2C2A;
    color: white;
}

.folder-row .caret {
    width: 0.9em;
    font-size: 0.65em;
    opacity: 0.6;
    display: inline-block;
    transition: transform 0.12s ease;
    flex: 0 0 auto;
}

.folder-row.expanded .caret {
    transform: rotate(90deg);
}

.folder-row .icon {
    flex: 0 0 auto;
    font-size: 0.95em;
    opacity: 0.75;
}

.folder-row .fname {
    flex: 1;
    overflow: hidden;
    text-overflow: ellipsis;
}

.folder-row .fcount {
    font-size: 0.78em;
    opacity: 0.65;
}

.folder-row.active .fcount {
    opacity: 0.9;
}

.year-list {
    display: none;
    padding-left: 1.1rem;
}

.year-list.open {
    display: block;
}

.year-row {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    padding: 0.32rem 0.5rem;
    border-radius: 5px;
    cursor: pointer;
    font-family: Calibri, sans-serif;
    font-size: 0.82rem;
}

.year-row:hover {
    background: #E4E1D8;
}

.year-row.active {
    background: #A6192E;
    color: white;
}

.year-row .fcount {
    margin-left: auto;
}

.saved-quotes-breadcrumb {
    display: flex;
    align-items: center;
    gap: 0.4rem;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    margin-bottom: 0.8rem;
    min-height: 1.6rem;
}

.saved-quotes-breadcrumb .crumb {
    background: #F1EFE8;
    border-radius: 999px;
    padding: 0.25rem 0.7rem;
    display: inline-flex;
    align-items: center;
    gap: 0.4rem;
}

.saved-quotes-breadcrumb .crumb.view-archived {
    background: #2C2C2A;
    color: white;
}

.saved-quotes-breadcrumb .crumb button {
    border: none;
    background: none;
    cursor: pointer;
    color: inherit;
    opacity: 0.6;
    font-size: 0.9em;
    padding: 0;
    line-height: 1;
}

.saved-quotes-breadcrumb .crumb button:hover {
    opacity: 1;
}

.saved-quotes-breadcrumb .sep {
    opacity: 0.4;
}

.saved-quotes-controls {
    display: flex;
    align-items: center;
    gap: 0.6rem;
    flex-wrap: wrap;
    margin-bottom: 0.8rem;
}

.saved-quotes-search {
    font-family: Calibri, sans-serif;
    font-size: 0.9rem;
    padding: 0.5rem 0.6rem;
    border: 1px solid #ccc;
    border-radius: 5px;
    flex: 1 1 260px;
    max-width: 340px;
}

.saved-quotes-chip-group {
    display: inline-flex;
    border: 1px solid #2C2C2A;
    border-radius: 5px;
    overflow: hidden;
}

.saved-quotes-chip {
    background: none;
    border: none;
    color: #2C2C2A;
    padding: 0.5rem 0.85rem;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    cursor: pointer;
}

.saved-quotes-chip + .saved-quotes-chip {
    border-left: 1px solid #2C2C2A;
}

.saved-quotes-chip.active {
    background: #CE1126;
    color: white;
}

.saved-quotes-owner-toggle {
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    display: flex;
    align-items: center;
    gap: 0.35rem;
    white-space: nowrap;
}

.saved-quotes-count {
    font-family: Calibri, sans-serif;
    font-size: 0.82rem;
    opacity: 0.65;
    white-space: nowrap;
}

.suggest-banner {
    display: none;
    align-items: center;
    gap: 0.7rem;
    background: #FCF3D9;
    color: #8A5A00;
    border-radius: 6px;
    padding: 0.6rem 0.9rem;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
    margin-bottom: 0.9rem;
}

.suggest-banner.show {
    display: flex;
}

.suggest-banner button.archive-all-btn {
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    font-weight: bold;
    padding: 0.32rem 0.7rem;
    border-radius: 4px;
    border: 1px solid #8A5A00;
    background: transparent;
    color: #8A5A00;
    cursor: pointer;
}

.suggest-banner button.archive-all-btn:hover {
    background: #8A5A00;
    color: white;
}

.suggest-banner .dismiss-btn {
    border: none;
    background: none;
    margin-left: auto;
    opacity: 0.7;
    cursor: pointer;
    color: inherit;
    font-size: 1rem;
}

.saved-quotes-table {
    border-collapse: collapse;
    width: 100%;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
}

.saved-quotes-table thead th {
    font-family: Cambria, serif;
    background: #2C2C2A;
    color: white;
    text-align: left;
    padding: 0.55rem 0.9rem;
    position: sticky;
    top: 0;
    cursor: pointer;
    user-select: none;
    white-space: nowrap;
}

.saved-quotes-table thead th .arrow {
    opacity: 0.5;
    font-size: 0.75em;
    margin-left: 0.25rem;
}

.saved-quotes-table thead th.sorted .arrow {
    opacity: 1;
}

.saved-quotes-table tbody td {
    padding: 0.55rem 0.9rem;
    border-bottom: 1px solid #ddd;
    white-space: nowrap;
}

.saved-quotes-table tbody tr:hover {
    background: #F1EFE8;
}

.saved-quotes-table tbody tr:last-child td {
    border-bottom: none;
}

.saved-quotes-table tbody tr.is-stale td:first-child {
    box-shadow: inset 3px 0 0 #B8860B;
}

.saved-quotes-table .name-cell {
    font-weight: bold;
    max-width: 260px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
    padding-left: 0.7rem;
}

.stale-badge {
    display: inline-block;
    font-family: Calibri, sans-serif;
    font-size: 0.68rem;
    font-weight: bold;
    background: #FCF3D9;
    color: #B8860B;
    border-radius: 999px;
    padding: 0.08rem 0.5rem;
    margin-left: 0.4rem;
    white-space: nowrap;
}

.saved-quotes-table .pill {
    display: inline-block;
    font-size: 0.72rem;
    font-weight: bold;
    padding: 0.15rem 0.55rem;
    border-radius: 999px;
}

.saved-quotes-table .pill.private {
    background: #EFE3C8;
    color: #8A5A00;
}

.saved-quotes-table .pill.shared {
    background: #D9EEDD;
    color: #1B8A3D;
}

.folder-move-select {
    font-family: Calibri, sans-serif;
    font-size: 0.8rem;
    padding: 0.25rem 0.4rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    background: white;
    color: #2C2C2A;
    max-width: 160px;
}

.saved-quotes-row-actions {
    display: flex;
    gap: 0.4rem;
}

.saved-quotes-row-actions button {
    font-family: Calibri, sans-serif;
    font-size: 0.78rem;
    padding: 0.28rem 0.6rem;
    border-radius: 4px;
    border: 1px solid #ccc;
    background: white;
    color: #2C2C2A;
    cursor: pointer;
}

.saved-quotes-row-actions a.load-btn {
    font-family: Calibri, sans-serif;
    font-size: 0.78rem;
    padding: 0.28rem 0.6rem;
    border-radius: 4px;
    border: 1px solid #CE1126;
    background: #CE1126;
    color: white;
    cursor: pointer;
    text-decoration: none;
    display: inline-block;
}

.saved-quotes-row-actions button.danger:hover {
    background: #FBE4E4;
    border-color: #CE1126;
    color: #A6192E;
}

.saved-quotes-pager {
    display: flex;
    align-items: center;
    justify-content: flex-end;
    gap: 0.5rem;
    margin-top: 0.9rem;
    font-family: Calibri, sans-serif;
    font-size: 0.85rem;
}

.saved-quotes-pager button {
    font-family: Calibri, sans-serif;
    padding: 0.35rem 0.7rem;
    border: 1px solid #ccc;
    border-radius: 4px;
    background: white;
    color: #2C2C2A;
    cursor: pointer;
}

.saved-quotes-pager button:disabled {
    opacity: 0.4;
    cursor: default;
}

.saved-quotes-empty {
    padding: 2.5rem;
    text-align: center;
    opacity: 0.6;
    font-style: italic;
    font-family: Calibri, sans-serif;
    display: none;
}

@media (max-width: 760px) {
    .saved-quotes-layout {
        flex-direction: column;
    }
    .saved-quotes-sidebar {
        flex: 1 1 auto;
        width: 100%;
        max-height: 220px;
    }
}

/* Log Out is a POST form (CSRF-protected) styled to look like the nav links */
.logout-form {
    display: inline;
    margin: 0;
}
.link-button {
    background: none;
    border: 0;
    padding: 0;
    font: inherit;
    color: #A6192E;
    font-weight: bold;
    cursor: pointer;
}
.link-button:hover {
    text-decoration: underline;
}
.sign-in-button {
    display: inline-block;
    padding: 0.6rem 1.1rem;
    background: #A6192E;
    color: #fff;
    border-radius: 4px;
    text-decoration: none;
    font-weight: bold;
    font-family: Calibri, sans-serif;
}

.view-only-note {
    margin-left: 12px;
    padding: 2px 8px;
    border-radius: 4px;
    background: #fff4d6;
    color: #7a5b00;
    font-size: 0.85em;
}

.last-saved-note {
    margin-right: 10px;
    font-size: 0.85em;
    color: #666;
    white-space: nowrap;
}

/* names like "Stephanie Decker" were cut off by the 80px default above */
.admin-table input.user-name-input {
    width: 190px;
}
