/* EntryOps — Muted Cyan palette.
 *
 * Two variants under one palette name, picked by the user's system
 * preference (prefers-color-scheme). Default = light, matching the
 * desktop TariffMill light-mode screenshots; dark = the muted-cyan
 * dark variant the desktop ships alongside.
 *
 * Add a manual override later by toggling [data-theme="dark"] on <html>;
 * the structure below is forward-compatible with that. */

:root {
  /* ---- Muted Cyan — LIGHT (default) ----
   * Neutrals are interpolated cool→warm by a single --warmth percentage
   * (Heath 2026-06-26: "it's very bright" → add a warmth slider). Each
   * token color-mixes from the original cool-cyan anchor toward a warm
   * paper anchor; the slider in the top-bar Appearance menu sets --warmth
   * on <html> (persisted to localStorage 'eo-warmth', applied pre-paint
   * by the head script in base.html). 0% = original cyan, 100% = full
   * warm paper. The teal accent is NOT mixed so the brand reads the same.
   * NOTE: scoped to the light palette — the dark block below keeps its
   * own fixed set, so the slider is a no-op in dark mode. */
  /* Tell the UA which palette to paint NATIVE controls from. Without this
     the browser styles inputs/selects/scrollbars from the SYSTEM preference
     while the page renders light, which is why the /trade sheet filter came
     out a near-black slab on a light card (Heath 2026-08-06). Same family as
     the checkbox fix below — a native control the page never colored. */
  color-scheme: light;
  --warmth:       60%;
  --bg:            color-mix(in oklab, #eaf3f5, #e6dccd var(--warmth));
  --panel:         color-mix(in oklab, #ffffff, #f6f0e6 var(--warmth));
  --panel-2:       color-mix(in oklab, #f1f7f9, #eee5d8 var(--warmth));
  --text:          color-mix(in oklab, #1f3a44, #322c24 var(--warmth));
  --muted:         color-mix(in oklab, #5d7c86, #776d62 var(--warmth));
  --border:        color-mix(in oklab, #c5d6dc, #d8ccb8 var(--warmth));
  --border-strong: color-mix(in oklab, #9fb9c2, #b8aa95 var(--warmth));
  --accent:       #4a8fa3;
  --accent-hover: #3a7a8d;
  --accent-soft:  #cce3eb;
  --error:        #c95757;
  --warn:         #d59644;
  --ok:           #4f9c6d;
  --shadow:       0 1px 3px rgba(31, 58, 68, 0.06), 0 1px 2px rgba(31, 58, 68, 0.04);
  /* Status TINT families (v0.4.1313). Defined for every palette so a
     status pill / stale badge / service-line row can never fall back to a
     light literal on a dark panel — that fallback was the "splotchy dark
     mode" (Heath 2026-08-08). Tint = surface, ink = text, edge = border. */
  --ok-bg:     #e3f3ea;  --ok-ink:     #1f6b43;  --ok-edge:    #9ed3b6;
  --warn-bg:   #fbf0d8;  --warn-ink:   #8a6500;  --warn-edge:  #d9ab48;
  --neutral-bg:#eef1f3;  --neutral-ink:#5a6671;  --neutral-edge:#c8d2d8;
  --info-bg:   #eef5f9;  --info-ink:   #1f2d36;  --info-edge:  #bcd6e4;
  --violet-bg: #f6f3fb;  --violet-ink: #7a6aa0;
}

/* ---- Muted Cyan — DARK ----
 * Applied two ways: automatically when the OS prefers dark (unless the user
 * explicitly chose Light), and explicitly via [data-theme="dark"] (the
 * Appearance-menu toggle, which wins over the OS). The two selectors share the
 * same values — keep them in sync. The pre-paint head script in base.html
 * stamps data-theme from localStorage('eo-theme') so there's no flash. */
@media (prefers-color-scheme: dark) {
  /* scoped exactly like the palette below: an explicit Light
     choice must keep LIGHT native controls on a dark OS */
  :root:not([data-theme="light"]) { color-scheme: dark; }
  :root:not([data-theme="light"]) {
    --bg:           #0f1f25;
    --panel:        #16292f;
    --panel-2:      #1e353c;
    --text:         #c9d8dd;
    --muted:        #8fa3aa;
    --border:       #2e4750;
    --border-strong:#3f6068;
    --accent:       #5fb3cc;
    --accent-hover: #74c4dc;
    --accent-soft:  rgba(95, 179, 204, 0.16);
    --error:        #d97070;
    --warn:         #e0a85a;
    --ok:           #6fc090;
    --shadow:       0 1px 3px rgba(0, 0, 0, 0.4);
  /* Dark tints: same families, low-alpha washes + brightened ink so
     every status surface sits ON the dark panel instead of glowing. */
  --ok-bg:     rgba(111, 192, 144, 0.14);  --ok-ink:     #6fc090;  --ok-edge:    rgba(111, 192, 144, 0.45);
  --warn-bg:   rgba(217, 171, 72, 0.14);   --warn-ink:   #e0be6e;  --warn-edge:  rgba(217, 171, 72, 0.45);
  --neutral-bg:rgba(122, 143, 150, 0.16);  --neutral-ink:#9fb2b9;  --neutral-edge:rgba(122, 143, 150, 0.42);
  --info-bg:   rgba(95, 179, 204, 0.14);   --info-ink:   #bcd8e2;  --info-edge:  rgba(95, 179, 204, 0.42);
  --violet-bg: rgba(122, 106, 160, 0.16);  --violet-ink: #a99cc9;
    /* Dark tints: same families, low-alpha washes + brightened ink so
       every status surface sits ON the dark panel instead of glowing. */
    --ok-bg:     rgba(111, 192, 144, 0.14);  --ok-ink:     #6fc090;  --ok-edge:    rgba(111, 192, 144, 0.45);
    --warn-bg:   rgba(217, 171, 72, 0.14);   --warn-ink:   #e0be6e;  --warn-edge:  rgba(217, 171, 72, 0.45);
    --neutral-bg:rgba(122, 143, 150, 0.16);  --neutral-ink:#9fb2b9;  --neutral-edge:rgba(122, 143, 150, 0.42);
    --info-bg:   rgba(95, 179, 204, 0.14);   --info-ink:   #bcd8e2;  --info-edge:  rgba(95, 179, 204, 0.42);
    --violet-bg: rgba(122, 106, 160, 0.16);  --violet-ink: #a99cc9;
  }
}
:root[data-theme="dark"] {
  color-scheme: dark;
  --bg:           #0f1f25;
  --panel:        #16292f;
  --panel-2:      #1e353c;
  --text:         #c9d8dd;
  --muted:        #8fa3aa;
  --border:       #2e4750;
  --border-strong:#3f6068;
  --accent:       #5fb3cc;
  --accent-hover: #74c4dc;
  --accent-soft:  rgba(95, 179, 204, 0.16);
  --error:        #d97070;
  --warn:         #e0a85a;
  --ok:           #6fc090;
  --shadow:       0 1px 3px rgba(0, 0, 0, 0.4);
}

/* ---- "Haze" theme (internal key: cozy — renamed 2026-07-18) — a COMPLETE palette (cool river / evergreen):
 *   #FBFBFB / #D3D5CA off-whites → surfaces (panel / panel-2)
 *   #1D2827 evergreen-black      → text
 *   #4A525B slate               → muted text
 *   #8A98AC blue-grey           → strong borders
 *   #A1B4DA periwinkle          → accent family (soft tint) + accent (darkened)
 * A FULL palette override (not just the accent), so selecting it wins over the
 * light/dark mode. Toggled via [data-palette="cozy"] from the Appearance menu
 * (localStorage 'eo-palette'). Placed after the mode blocks so it beats them by
 * source order at equal specificity. The accent is the periwinkle darkened to
 * #5468a0 so white button/tab text stays legible (the raw #A1B4DA is too light
 * for white text); the raw periwinkle is kept for accent-soft. */
:root[data-palette="cozy"] {
  /* Surfaces are warmth-responsive (Cooler↔Warmer slider); default 60% stays
     cool (this is a cool palette), warmer only nudges toward a soft off-white. */
  --bg:            color-mix(in oklab, #e7ebf0, #eef0ea var(--warmth));
  --panel:         color-mix(in oklab, #f7f9fb, #fbfbf6 var(--warmth));
  --panel-2:       color-mix(in oklab, #d4d9e0, #dee0d5 var(--warmth));
  --text:          #1D2827;
  --muted:         #4A525B;
  --border:        #d3d7d6;
  --border-strong: #8A98AC;
  --accent:        #5468a0;
  --accent-hover:  #43547f;
  --accent-soft:   #dfe4f2;
  --error:         #b0503c;
  --warn:          #b8873a;
  --ok:            #5a9e86;
  --shadow:        0 1px 3px rgba(29, 40, 39, 0.10), 0 1px 2px rgba(29, 40, 39, 0.06);
}

/* ---- "Mocha" theme — a COMPLETE palette (warm brass / wood / watch-movement):
 *   #CDD5DC cool grey-blue → cool surface anchor
 *   #C4AA64 brass          → warm surface anchor + amber warn
 *   #A2A39E mid grey       → strong borders
 *   #74685A warm taupe     → muted text
 *   #804B21 copper         → accent (buttons, active tabs, links, sliders)
 *   #180D0A near-black     → text
 * Full palette override, same mechanism as Cozy ([data-palette="mocha"],
 * localStorage 'eo-palette'). The accent is the copper #804B21 (dark enough for
 * legible white button text); brass #C4AA64 is too light for white text so it
 * drives the warm surface tint + the "warn" amber instead. */
:root[data-palette="mocha"] {
  /* Warmth-responsive surfaces; default 60% leans to the warm brass off-whites,
     cooler pulls toward the grey-blue (#CDD5DC family). */
  --bg:            color-mix(in oklab, #e6ebef, #f2ecdf var(--warmth));
  --panel:         color-mix(in oklab, #f4f7f9, #fbf6ec var(--warmth));
  --panel-2:       color-mix(in oklab, #d6dce1, #e6ddcc var(--warmth));
  --text:          #180D0A;
  --muted:         #74685A;
  --border:        #dbd5cb;
  --border-strong: #A2A39E;
  --accent:        #804B21;
  --accent-hover:  #6a3d1a;
  --accent-soft:   #efe3d0;
  --error:         #b0503c;
  --warn:          #b98a35;
  --ok:            #5a9e86;
  --shadow:        0 1px 3px rgba(24, 13, 10, 0.10), 0 1px 2px rgba(24, 13, 10, 0.06);
}

/* ---- "Silt" theme — a COMPLETE palette (muted heritage: sage / khaki / copper):
 *   #F8D5A9 warm sand   → warm surface anchor
 *   #C8CDC3 sage grey   → cool surface anchor
 *   #B4AB91 khaki       → strong borders
 *   #65807E slate-green → accent family (darkened for white-text legibility)
 *   #A0653F copper      → warn amber
 *   #212C30 charcoal    → text
 * Full palette override, same mechanism as Cozy ([data-palette="silt"],
 * localStorage 'eo-palette'). A LIGHT, warmth-responsive palette. The accent is
 * the slate-green darkened to #4f6a67 (the raw #65807E is too light for white
 * button text); copper #A0653F drives the warn amber. */
:root[data-palette="silt"] {
  /* Warmth-responsive surfaces; default 60% leans to the warm sand off-whites,
     cooler pulls toward the sage grey (#C8CDC3 family). */
  --bg:            color-mix(in oklab, #e9ece6, #f3ecdf var(--warmth));
  --panel:         color-mix(in oklab, #f5f7f3, #fbf6ec var(--warmth));
  --panel-2:       color-mix(in oklab, #dee2da, #ece1cd var(--warmth));
  --text:          #212C30;
  --muted:         #545f5c;
  --border:        #d8dad2;
  --border-strong: #B4AB91;
  --accent:        #4f6a67;
  --accent-hover:  #3f5654;
  --accent-soft:   #dde3df;
  --error:         #b0503c;
  --warn:          #b07a3e;
  --ok:            #5a9e86;
  --shadow:        0 1px 3px rgba(33, 44, 48, 0.10), 0 1px 2px rgba(33, 44, 48, 0.06);
}

/* ---- "Blush" theme — a COMPLETE palette (dusty rose / plum / quartz):
 *   #F0E3E7 rose quartz  → cool surface anchor
 *   #F6E7DE blush sand   → warm surface anchor
 *   #C9A2AE dusty rose   → strong borders
 *   #755A64 mauve        → muted text
 *   #9E3D63 deep rose    → accent (buttons, active tabs, links)
 *   #2B1B22 plum-black   → text
 * Full palette override, same mechanism as the others ([data-palette="blush"],
 * cookie 'eo-palette'). A LIGHT, warmth-responsive palette. The accent is the
 * deep rose #9E3D63 — 6.4:1 against white, so white button text stays legible;
 * the light quartz tones are surfaces only and never carry text. Every pair
 * below was measured, not eyeballed: worst case is muted-on-panel-2 at 4.52:1
 * and ok-on-panel at 4.99:1, both over the 4.5:1 floor. */
:root[data-palette="blush"] {
  /* Warmth-responsive surfaces; default 60% leans to the warm blush sand,
     cooler pulls toward the rose quartz (#F0E3E7 family). */
  --bg:            color-mix(in oklab, #f0e3e7, #f6e7de var(--warmth));
  --panel:         color-mix(in oklab, #fdf7f8, #fdf6ef var(--warmth));
  --panel-2:       color-mix(in oklab, #e6d5db, #eddccf var(--warmth));
  --text:          #2B1B22;
  --muted:         #755A64;
  --border:        #e7d8dd;
  --border-strong: #C9A2AE;
  --accent:        #9E3D63;
  --accent-hover:  #84314f;
  --accent-soft:   #f6dfe8;
  --error:         #b0503c;
  --warn:          #b0743e;
  --ok:            #3a7660;
  --shadow:        0 1px 3px rgba(43, 27, 34, 0.10), 0 1px 2px rgba(43, 27, 34, 0.06);
}

/* ---- Every custom palette is LIGHT — say so, and re-assert the light tints.
 *
 * The blocks above override the SURFACE tokens, but two things were still
 * being decided by the dark @media block on a machine whose OS prefers dark,
 * because a palette that doesn't redefine a token inherits whatever the mode
 * block set (verified in a browser 2026-08-12, all four palettes):
 *
 *   1. `color-scheme` stayed `dark`, so the browser painted NATIVE controls —
 *      selects, checkboxes, scrollbars, date pickers — from the dark palette
 *      on top of a light card. Exactly the near-black-slab bug the /trade
 *      sheet filter hit (Heath 2026-08-06); the base light palette already
 *      carries `color-scheme: light` for this reason.
 *   2. The status TINT families stayed on their dark values, so a status pill
 *      rendered `--ok-ink: #6fc090` (a light green meant for a #16292f panel)
 *      on a near-white surface — about 2:1, illegible.
 *
 * Grouped rather than repeated per palette: all four are light, none of them
 * wants a different answer, and one list is one place to change. Specificity
 * ties the mode block and wins on source order. */
:root[data-palette="cozy"],
:root[data-palette="mocha"],
:root[data-palette="silt"],
:root[data-palette="blush"] {
  color-scheme: light;
  --ok-bg:     #e3f3ea;  --ok-ink:     #1f6b43;  --ok-edge:    #9ed3b6;
  --warn-bg:   #fbf0d8;  --warn-ink:   #8a6500;  --warn-edge:  #d9ab48;
  --neutral-bg:#eef1f3;  --neutral-ink:#5a6671;  --neutral-edge:#c8d2d8;
  --info-bg:   #eef5f9;  --info-ink:   #1f2d36;  --info-edge:  #bcd6e4;
  --violet-bg: #f6f3fb;  --violet-ink: #7a6aa0;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14px/1.5 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  min-height: 100vh;
}

a { color: var(--accent); text-decoration: none; }
a:hover { color: var(--accent-hover); text-decoration: underline; }

/* Force transparent rendering for the SVG logo in all placements. */
img[src*="logo.svg"] {
  background: transparent;
}

/* ----------------------------------------------------------------- top bar */

.topbar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 24px;
  background: var(--panel);
  border-bottom: 1px solid var(--border);
  box-shadow: var(--shadow);
  /* Pin the top nav while the page scrolls (Heath 2026-06-12). z-index
     sits above sticky table headers (z-index: 1) used by the CAPE
     pivot widgets + data tables. */
  position: sticky;
  top: 0;
  z-index: 50;
}
.brand {
  font-weight: 600;
  font-size: 16px;
  color: var(--accent);
  letter-spacing: 0.3px;
}
.brand:hover { text-decoration: none; color: var(--accent-hover); }
/* Logo hover menu — cross-surface shortcuts (EntryOps / CAPE / Metrics). */
.brand-menu { position: relative; display: inline-flex; align-items: center; }
.brand-caret { font-size: 10px; color: var(--muted); margin-left: 4px; vertical-align: middle; }
.brand-dropdown {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 6px;
  min-width: 210px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow);
  padding: 6px;
  z-index: 60;
}
/* Keep the menu open while the cursor crosses the gap to it. */
.brand-dropdown::before {
  content: ""; position: absolute; top: -8px; left: 0; right: 0; height: 8px;
}
.brand-menu:hover .brand-dropdown,
.brand-menu:focus-within .brand-dropdown,
.brand-menu.open .brand-dropdown { display: block; }
.brand-dropdown a {
  display: block;
  padding: 8px 10px;
  border-radius: 6px;
  color: var(--text);
  text-decoration: none;
}
.brand-dropdown a:hover { background: var(--accent); color: #fff; }
.brand-dropdown a strong { display: block; font-size: 13px; font-weight: 600; }
.brand-dropdown a small { display: block; font-size: 11px; color: var(--muted); }
.brand-dropdown a:hover small { color: rgba(255, 255, 255, 0.85); }
.user-chip {
  color: var(--muted);
  margin-right: 12px;
  font-size: 13px;
}

.tabnav {
  display: flex;
  gap: 4px;
  flex: 1;
  justify-content: center;
}
.tab {
  padding: 8px 18px;
  border-radius: 6px;
  font-size: 13px;
  color: var(--muted);
  border: 1px solid transparent;
  transition: background 0.15s, color 0.15s, border-color 0.15s;
}
.tab:hover {
  background: var(--accent-soft);
  color: var(--accent);
  text-decoration: none;
}
.tab.active {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
}
/* Help menu — a dropdown that lives IN the tab row. Same behaviour as the
 * brand menu (hover or keyboard focus, with a bridge over the gap so the
 * cursor can travel to it), styled to sit flush with .tab rather than
 * introducing a second look for the same interaction. */
.tab-menu { position: relative; display: inline-flex; align-items: center; }
.tab-dropdown {
  display: none;
  position: absolute;
  top: 100%;
  left: 0;
  margin-top: 6px;
  min-width: 220px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow);
  padding: 6px;
  z-index: 60;
}
.tab-dropdown::before {
  content: ""; position: absolute; top: -8px; left: 0; right: 0; height: 8px;
}
.tab-menu:hover .tab-dropdown,
.tab-menu:focus-within .tab-dropdown,
.tab-menu.open .tab-dropdown { display: block; }

/* Touch: no hover exists, so the first tap on the trigger OPENS the menu
   (script adds .open) instead of navigating; a second tap follows the link.
   Widen the cursor-bridge into a real tap target while we're here — an 8px
   strip is fine for a mouse crossing a gap and useless for a fingertip. */
@media (hover: none) {
  .brand-dropdown::before,
  .tab-dropdown::before { top: -12px; height: 12px; }
  .brand-dropdown a,
  .tab-dropdown a { padding: 11px 12px; }
}
.tab-dropdown a {
  display: block;
  padding: 8px 10px;
  border-radius: 6px;
  color: var(--text);
  text-decoration: none;
}
.tab-dropdown a:hover { background: var(--accent); color: #fff; }
.tab-dropdown a strong { display: block; font-size: 13px; font-weight: 600; }
.tab-dropdown a small { display: block; font-size: 11px; color: var(--muted); }
.tab-dropdown a:hover small { color: rgba(255, 255, 255, 0.85); }

.userarea { display: flex; align-items: center; gap: 10px; }

/* ---- Appearance menu (warmth slider) ----
 * Personal display pref, client-side only. The chosen warmth persists to
 * localStorage('eo-warmth') and is applied to <html>'s --warmth before
 * paint (head script in base.html); the light neutrals then color-mix to
 * follow it (see :root above). */
.appearance-menu { position: relative; display: inline-flex; }
.appearance-btn { font-size: 14px; line-height: 1; padding: 5px 10px; }
.appearance-popover {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  /* Wide enough for the 5-button theme bar (Default·Dark·Cozy·Mocha·Silt). */
  width: 264px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  box-shadow: var(--shadow);
  padding: 14px;
  z-index: 60;
}
.appearance-popover[hidden] { display: none; }
.appearance-popover h4 {
  margin: 0 0 10px;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.appearance-popover input[type="range"] {
  width: 100%;
  accent-color: var(--accent);
  padding: 0;
  margin: 0;
  cursor: pointer;
}
.appearance-hint {
  display: flex;
  justify-content: space-between;
  font-size: 11px;
  color: var(--muted);
  margin: 6px 0 12px;
}
.appearance-popover .link { font-size: 12px; }
.appearance-popover h4:not(:first-child) { margin-top: 14px; }
.appearance-seg {
  display: flex; width: 100%;
  border: 1px solid var(--border); border-radius: 7px; overflow: hidden;
  margin-bottom: 4px;
}
.appearance-opt {
  flex: 1; padding: 5px 4px; font: inherit; font-size: 12px; font-weight: 600;
  background: var(--panel-2); color: var(--muted);
  border: none; border-right: 1px solid var(--border); cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.appearance-opt:last-child { border-right: none; }
.appearance-opt:hover { color: var(--text); }
.appearance-opt.active { background: var(--accent); color: #fff; }
.userarea-link {
  padding: 5px 12px;
  border-radius: 5px;
  font-size: 12px;
  font-weight: 600;
  color: var(--muted);
  border: 1px solid var(--border);
  background: var(--panel-2);
}
.userarea-link:hover {
  background: var(--accent-soft);
  color: var(--accent);
  border-color: var(--accent);
  text-decoration: none;
}
.userarea-link.active { background: var(--accent); color: white; border-color: var(--accent); }
.inline { display: inline; }
.link {
  background: none;
  border: none;
  color: var(--accent);
  cursor: pointer;
  font: inherit;
  padding: 0;
}
.link:hover { color: var(--accent-hover); text-decoration: underline; }

/* ------------------------------------------------------------------- page */

/* Full-width content with side padding — left-aligned. Matches the
 * desktop's "content fills the window" layout rather than the cloud's
 * earlier 1200px centered shell that left dead space on wide monitors. */
.page {
  margin: 0;
  padding: 24px;
  width: 100%;
  box-sizing: border-box;
}

.footer {
  color: var(--muted);
  font-size: 12px;
  padding: 16px 24px;
  background: var(--panel);
  border-top: 1px solid var(--border);
}
.footer .status-bar {
  display: flex;
  /* center, was space-between: the © line is the bar's only child since
     the v0.4.1183 readout removals, and space-between pinned it left.
     That removal only touched base.html — cape_base.html still carries
     THREE spans, and centering them with no gap ran them together as
     "CAPE post-filing surface© 2026 Process Logic Labs, LLCEntryOps
     0.4.1340 · production". The gap is what separates a multi-span bar;
     on a single-child bar it does nothing, so base.html is unaffected. */
  justify-content: center;
  align-items: center;
  gap: 14px;
  flex-wrap: wrap;
}

/* ----------------------------------------------------------------- cards */

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 24px;
  margin-bottom: 20px;
  box-shadow: var(--shadow);
}
.card-narrow { max-width: 420px; margin: 60px auto; }

h1, h2 { margin-top: 0; }
h1 { font-size: 22px; font-weight: 600; }
h2 { font-size: 16px; font-weight: 600; color: var(--muted); margin-bottom: 12px; }
.muted { color: var(--muted); }

.stack { display: flex; flex-direction: column; gap: 14px; }
.row-between { display: flex; justify-content: space-between; align-items: flex-start; gap: 16px; margin-bottom: 16px; }
.actions { display: flex; gap: 8px; align-items: center; }

/* Two-column split: left sidebar of controls, right main panel of
 * results / drop zone. Matches the desktop TariffMill PDF Processing +
 * Invoice Processing layout pattern. Collapses to a single column on
 * narrow viewports so mobile still works. */
.layout-split {
  display: grid;
  /* The `minmax(0, 1fr)` on the second column is the key piece — by
   * default CSS grid treats `1fr` as `minmax(auto, 1fr)` and the
   * `auto` floor is the intrinsic min-content width. A wide preview
   * table inside the right column would force the column wider than
   * the viewport, blowing past the page edge. `minmax(0, 1fr)` lets
   * the column shrink to 0 if needed; the inner .table-scroll then
   * gets to do its overflow:auto job and the table scrolls horizontally
   * INSIDE the card instead of pushing the whole page sideways. */
  grid-template-columns: minmax(280px, 340px) minmax(0, 1fr);
  gap: 20px;
  align-items: start;
  width: 100%;
}
.layout-main { min-width: 0; }  /* same intent for any non-grid parent */
@media (max-width: 880px) {
  .layout-split { grid-template-columns: 1fr; }
}
.layout-split .panel {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 18px;
  margin-bottom: 16px;
  box-shadow: var(--shadow);
}
.layout-split .panel:last-child { margin-bottom: 0; }
.layout-split .panel h3 {
  margin: 0 0 12px 0;
  font-size: 11px;
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  letter-spacing: 0.6px;
  padding-bottom: 8px;
  border-bottom: 1px solid var(--border);
}

/* Bigger drop zone for the main panel where it's the visual centerpiece. */
.file-drop.large {
  padding: 60px 32px;
  font-size: 14px;
}
.file-drop.large span { font-size: 14px; margin-top: 12px; }

/* ----------------------------------------------------------------- forms */

label { display: block; }
label > span { display: block; margin-bottom: 4px; color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.5px; }
/* Every text-ish input type is listed here on purpose. A type left out of
   this selector gets NO background or color from us and falls through to the
   user-agent default, which under a dark OS renders a black box in the middle
   of a light form -- exactly what happened to the /trade entry-date field
   (v0.4.1326). date/number/search/tel/url/month were the ones missing. */
input[type="email"], input[type="password"], input[type="text"],
input[type="date"], input[type="number"], input[type="search"],
input[type="tel"], input[type="url"], input[type="month"],
select, textarea {
  width: 100%;
  padding: 9px 12px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
  font: inherit;
}
input[type="email"]:focus, input[type="password"]:focus,
input[type="text"]:focus, input[type="date"]:focus, input[type="number"]:focus,
input[type="search"]:focus, input[type="tel"]:focus, input[type="url"]:focus,
input[type="month"]:focus, select:focus, textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-soft);
}
select option { background: var(--panel); color: var(--text); }
/* The calendar picker glyph is drawn by the browser, not by us, so it does
   not follow --text. Invert it in dark mode or it stays near-black on a dark
   field and reads as a missing control. */
:root[data-theme="dark"] input[type="date"]::-webkit-calendar-picker-indicator {
  filter: invert(1) opacity(0.75);
}
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) input[type="date"]::-webkit-calendar-picker-indicator {
    filter: invert(1) opacity(0.75);
  }
}

.file-drop {
  display: block;
  /* Tightened in v0.4.61 — the 32px-padding zone was eating most of the
   * dashboard's vertical real estate above the Last Result Preview.
   * 14px keeps the dashed border visible + the labels readable without
   * dominating the page. drag-over keeps its outline so the operator
   * still gets the unmistakable "release here" feedback. */
  padding: 14px 20px;
  border: 2px dashed var(--border-strong);
  border-radius: 8px;
  text-align: center;
  cursor: pointer;
  background: var(--panel-2);
  transition: border-color 0.15s, background 0.15s;
}
.file-drop:hover { border-color: var(--accent); background: var(--accent-soft); }
.file-drop.drag-over {
  border-color: var(--accent);
  background: var(--accent-soft);
  /* slight scale + outline so the operator gets unmistakable feedback
   * that the drop will land here when they release the mouse */
  outline: 2px dashed var(--accent);
  outline-offset: -8px;
}
.file-drop input[type="file"] {
  width: 100%;
  margin-bottom: 8px;
  color: var(--text);
}
.file-drop span { display: block; color: var(--muted); font-size: 13px; }

button.primary, a.primary.button {
  background: var(--accent);
  color: white;
  border: none;
  padding: 9px 18px;
  border-radius: 6px;
  font: inherit;
  font-weight: 500;
  cursor: pointer;
  display: inline-block;
  transition: background 0.15s;
}
button.primary:hover, a.primary.button:hover {
  background: var(--accent-hover);
  text-decoration: none;
  color: white;
}

a.button, button.button {
  background: var(--panel-2);
  color: var(--text);
  padding: 9px 16px;
  border-radius: 6px;
  border: 1px solid var(--border);
  display: inline-block;
  font: inherit;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
/* An explicit `display` beats the UA stylesheet's `[hidden] { display: none }`,
   so every .button carrying the hidden attribute would render anyway. This has
   been fixed one element at a time four times (appearance-popover, hts-ac-
   dropdown, parts-modal, parts-selection-toolbar); cure it for buttons as a
   class instead of waiting for the fifth. */
a.button[hidden], button.button[hidden],
a.primary.button[hidden], button.primary[hidden] { display: none; }
a.button:hover, button.button:hover {
  border-color: var(--accent);
  background: var(--panel);
  text-decoration: none;
}

/* ----------------------------------------------------------------- flash */

.flash {
  padding: 12px 16px;
  border-radius: 6px;
  margin-bottom: 16px;
  border: 1px solid var(--border);
  background: var(--panel);
}
.flash.info  { background: var(--accent-soft); border-color: var(--accent); }
.flash.error { background: rgba(201, 87, 87, 0.08); border-color: var(--error); color: var(--error); }
.flash.warn  { background: rgba(213, 150, 68, 0.10); border-color: var(--warn); color: var(--warn); }
.form-error  { color: var(--error); font-size: 13px; }

/* ----------------------------------------------------------------- tables */

table.data {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
table.data th, table.data td {
  padding: 8px 12px;
  border-bottom: 1px solid var(--border);
  text-align: left;
  vertical-align: top;
}
table.data th {
  font-weight: 600;
  color: var(--muted);
  text-transform: uppercase;
  font-size: 11px;
  letter-spacing: 0.5px;
  background: var(--panel-2);
}
table.data tbody tr:hover { background: var(--accent-soft); }

/* Results tables — bounded vertical scroll inside the card with sticky
 * column headers so the operator can scan a long list without losing
 * the column labels. Fixed 500px (not vh-relative) so the search form
 * above the table stays on screen at the operator's actual resolution.
 * 70vh used to leave too much room and the *page* scrolled instead
 * of the *table*. */
.table-scroll {
  overflow-x: auto;
  overflow-y: auto;
  max-height: 500px;
  margin: 0 -24px;
  padding: 0 24px;
}
/* The Invoice Processing preview is the main work surface — give it most of
 * the viewport height (minus the top nav + the preview card's header/toolbar)
 * so it shows many more rows than the shared 500px cap, while still scrolling
 * INSIDE the card rather than pushing the page. Scoped to #preview so the
 * Parts / HTS tables keep the conservative fixed cap. */
#preview .table-scroll {
  max-height: calc(100vh - 210px);
  min-height: 320px;
}
.table-scroll > table.data > thead th {
  /* Direct-child scope ON PURPOSE: the descendant form also matched the
   * expanded Invoice-Line-Tariffs sub-table's header (a th's ancestor
   * chain passes through the OUTER table.data), so the inner header
   * stuck at top:0 and painted over the real one during scroll. z-index
   * above row content that creates its own stacking contexts. */
  position: sticky;
  top: 0;
  z-index: 3;
  background: var(--panel-2);
}

/* Compact icon button — square, icon-only action (with title/aria-label
 * tooltip) for toolbars where labelled buttons would wrap. Inline SVGs
 * inherit currentColor so they pick up the hover color. */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  padding: 0;
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--muted);
  background: var(--panel);
  cursor: pointer;
  text-decoration: none;
  flex: 0 0 auto;
}
.icon-btn:hover {
  color: var(--text);
  border-color: var(--accent);
  background: var(--accent-soft);
}
.icon-btn svg { width: 16px; height: 16px; display: block; }

/* ----------------------------------------------------------------- badges */

.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 4px;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  font-weight: 600;
}
.badge-pending          { background: var(--panel-2); color: var(--muted); border: 1px solid var(--border); }
.badge-running          { background: rgba(213, 150, 68, 0.15); color: var(--warn); }
.badge-succeeded        { background: rgba(79, 156, 109, 0.15); color: var(--ok); }
.badge-failed           { background: rgba(201, 87, 87, 0.15); color: var(--error); }
.badge-needs_net_weight { background: rgba(213, 150, 68, 0.15); color: var(--warn); }
.badge-needs_parts      { background: rgba(213, 150, 68, 0.15); color: var(--warn); }
.badge-needs_template   { background: rgba(213, 150, 68, 0.15); color: var(--warn); }
.badge-needs_mid        { background: rgba(213, 150, 68, 0.15); color: var(--warn); }

/* Make multi-chip badge links unobtrusive — keep the chip color
   doing the visual work; strip default link decoration and tighten
   spacing between adjacent chips. */
.badge-link {
  text-decoration: none;
  margin-right: 4px;
  display: inline-block;
}
.badge-link:hover .badge { filter: brightness(1.15); }

/* Composite /setup page — each needs_* gap gets its own fieldset
   block so the operator can see at a glance how many gaps remain. */
.setup-section {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 12px 16px 14px;
  margin: 0;
}
.setup-section + .setup-section { margin-top: 14px; }
.setup-section legend {
  padding: 0 6px;
  font-size: 13px;
  font-weight: 600;
  color: var(--text);
  display: inline-flex;
  align-items: center;
  gap: 8px;
}

/* Missing Parts grid — many editable columns. Per-field min-widths
   so values like long MIDs ("INSIGMAC123BOM" — 14 chars), full
   descriptions ("Quinbin - litter bin of oval base..."), and 12-char
   HTS codes ("7326.90.8688") fit without truncating. The table sits
   inside .table-scroll, so anything that doesn't fit the viewport
   becomes a horizontal scroll instead of clipping. */
.missing-parts-table input[type="text"] {
  width: 100%;
  min-width: 110px;
  padding: 4px 6px;
  font-size: 12px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 3px;
  color: var(--text);
}
.missing-parts-table input[type="text"]:focus {
  outline: none;
  border-color: var(--accent);
  background: var(--panel);
}
.missing-parts-table th,
.missing-parts-table td {
  padding: 4px 6px;
  font-size: 12px;
}
.missing-parts-table td code { font-size: 11px; }

/* Per-field widths. Inputs use min-width so they grow with viewport
   when the table has spare horizontal space; the cell follows the
   input. The Part # input keeps its dedicated .pn-input class for
   any future styling distinct from the generic field inputs. */
.missing-parts-table .pn-input,
.missing-parts-table input[data-field="part_number"]   { min-width: 140px; }
.missing-parts-table input[data-field="description"]   { min-width: 240px; }
.missing-parts-table input[data-field="hts_code"]      { min-width: 120px; }
.missing-parts-table input[data-field="qty_unit"]      { min-width:  70px; }
.missing-parts-table input[data-field="mid"]           { min-width: 170px; }
.missing-parts-table input[data-field="client_code"]   { min-width: 100px; }
.missing-parts-table input[data-field="country_origin"],
.missing-parts-table input[data-field="country_of_melt"],
.missing-parts-table input[data-field="country_of_cast"],
.missing-parts-table input[data-field="country_of_smelt"] { min-width: 90px; }
.missing-parts-table input[data-field$="_pct"]         { min-width:  75px; }

/* Inline HTS autocomplete dropdown (Missing/Incomplete Parts grid).
   Positioned absolutely against the active HTS Code input by JS.
   Sits above tables (z-index above .table-scroll's overflow stack). */
.hts-ac-dropdown {
  position: absolute;
  z-index: 1000;
  max-height: 320px;
  overflow-y: auto;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 4px;
  box-shadow: 0 4px 14px rgba(0, 0, 0, 0.18);
  font-size: 12px;
  padding: 2px 0;
}
.hts-ac-dropdown[hidden] { display: none; }
.hts-ac-row {
  display: grid;
  grid-template-columns: 110px 1fr auto;
  align-items: center;
  gap: 10px;
  padding: 5px 10px;
  cursor: pointer;
  border-bottom: 1px solid var(--border);
}
.hts-ac-row:last-child { border-bottom: none; }
.hts-ac-row:hover, .hts-ac-row.active {
  background: var(--accent-soft, #e8f1f9);
}
.hts-ac-row code {
  font-family: var(--mono);
  font-size: 12px;
  color: var(--accent);
  white-space: nowrap;
}
.hts-ac-desc {
  color: var(--text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.hts-ac-uom {
  color: var(--muted);
  font-size: 11px;
  font-style: italic;
  white-space: nowrap;
}
.hts-ac-empty {
  padding: 8px 12px;
  color: var(--muted);
  font-style: italic;
}

/* Inline edit on the run-preview table (preview.html). Editable cells
   show a subtle hover cue + ✎ marker in the header; clicking swaps
   the cell to an <input>. Saved cells flash a soft highlight. */
table.preview-editable th.editable-col { cursor: help; }
table.preview-editable th.editable-col .edit-marker {
  font-size: 10px;
  color: var(--muted);
  margin-left: 2px;
}
table.preview-editable td.editable-cell {
  cursor: text;
  position: relative;
}
table.preview-editable td.editable-cell:hover {
  background: var(--accent-soft, #eef4fa);
  outline: 1px dashed var(--accent);
  outline-offset: -1px;
}
table.preview-editable td.editable-cell.edited {
  background: rgba(127, 200, 127, 0.18);
  font-weight: 600;
}
.inline-edit-input {
  width: 100%;
  min-width: 60px;
  padding: 2px 4px;
  font-size: inherit;
  font-family: inherit;
  background: var(--panel);
  border: 1px solid var(--accent);
  border-radius: 2px;
  color: var(--text);
  box-sizing: border-box;
}
.inline-edit-input:focus { outline: none; }

.edit-status-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  margin: 10px 0;
  padding: 10px 14px;
  background: rgba(255, 200, 0, 0.08);
  border: 1px solid #b07d2b;
  border-radius: 4px;
  font-size: 13px;
}
.edit-status-bar[hidden] { display: none; }
.edit-status-bar .inline {
  display: inline;
  margin: 0;
}

/* ----------------------------------------- parts search + table-click row */

.parts-search {
  margin: 16px 0 12px 0;
  padding: 14px 16px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 8px;
}
.quick-filters {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin-bottom: 12px;
}
.qf {
  display: inline-block;
  padding: 5px 12px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  color: var(--muted);
  font-size: 12px;
  font-weight: 500;
}
.qf:hover { background: var(--accent-soft); color: var(--accent); border-color: var(--accent); text-decoration: none; }
.qf.active { background: var(--accent); color: white; border-color: var(--accent); }
.qf.active:hover { background: var(--accent-hover); color: white; }

.search-grid {
  display: grid;
  grid-template-columns: 160px 130px 1fr 160px 160px auto;
  gap: 10px;
  align-items: end;
}
.search-grid .grow { grid-column: span 1; }
.search-actions { display: flex; gap: 6px; padding-bottom: 1px; }
@media (max-width: 880px) {
  .search-grid { grid-template-columns: 1fr 1fr; }
  .search-grid .grow { grid-column: 1 / -1; }
  .search-actions { grid-column: 1 / -1; }
}

.match-count { margin: 4px 0 12px 0; font-size: 12px; }

.parts-table tr.clickable { cursor: pointer; }
.parts-table tr.clickable:hover { background: var(--accent-soft); }

/* Row hover for both admin (editable) + read-only views. The
   admin grid no longer carries tr.clickable since cells are
   individually interactive, but a subtle row hover still helps
   the eye track horizontally across a wide table. */
.parts-table tbody tr:hover { background: var(--accent-soft); }

/* Part # link inside the editable parts grid — keep the code
   styling but make the link itself unstyled. */
.parts-table .part-link { color: inherit; text-decoration: none; }
.parts-table .part-link:hover { text-decoration: underline; }

/* Checkbox column — minimal width, centered. */
.parts-select-col { width: 32px; text-align: center; padding: 4px 6px; }
.parts-select-col input { cursor: pointer; }

/* Section 232 percentage cells — right-aligned numeric, tabular
   monospace so the digits line up across rows. Narrow column width
   keeps the 6 new percent columns from blowing out horizontal
   scroll. */
.parts-table .pct-cell {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-family: ui-monospace, "Cascadia Code", "Source Code Pro", Menlo, monospace;
  width: 60px;
  min-width: 60px;
}

/* Editable cells — subtle hover affordance so the operator knows
   they can click; focus state gets the accent border so the active
   edit is obvious. */
.parts-table.editable td[contenteditable="true"] {
  cursor: text;
  border-left: 2px solid transparent;
}
.parts-table.editable td[contenteditable="true"]:hover {
  background: var(--accent-soft);
}
.parts-table.editable td[contenteditable="true"]:focus {
  outline: none;
  background: var(--panel);
  border-left-color: var(--accent);
  white-space: normal;  /* let long edits expand instead of clipping */
  text-overflow: clip;
}
/* Save-status flash. .cell-saving = in flight; .cell-saved = brief
   green pulse; .cell-error = persistent red border until next focus. */
.parts-table td.cell-saving  { background: rgba(255, 200, 0, 0.15); }
.parts-table td.cell-saved   { background: rgba(40, 180, 60, 0.20); }
.parts-table td.cell-error {
  background: rgba(220, 50, 50, 0.20);
  border-left: 2px solid #c0392b;
}

/* Selection toolbar — sticky to viewport bottom so it stays visible
   while the operator scrolls a long result set. The [hidden] override
   below restores the HTML attribute's display:none behavior, which
   the explicit `display: flex` here would otherwise mask. */
.parts-selection-toolbar[hidden] { display: none; }
.parts-selection-toolbar {
  position: fixed;
  left: 50%;
  bottom: 18px;
  transform: translateX(-50%);
  display: flex;
  align-items: center;
  gap: 12px;
  padding: 10px 16px;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 999px;
  box-shadow: 0 4px 16px rgba(0, 0, 0, 0.25);
  z-index: 100;
}
.parts-selection-toolbar .psts-count { font-size: 14px; }
.parts-selection-toolbar .psts-count strong { color: var(--accent); }
.parts-selection-toolbar .link {
  background: none;
  border: none;
  color: var(--accent);
  cursor: pointer;
  padding: 0 4px;
  font-size: 12px;
  text-decoration: underline;
}
.parts-selection-toolbar .button.danger {
  background: #c0392b;
  color: white;
  border-color: #c0392b;
}
.parts-selection-toolbar .button.danger:hover {
  background: #a32a1f;
  border-color: #a32a1f;
}

/* Right-click context menu — single instance reused per click. */
.parts-context-menu {
  position: fixed;
  z-index: 200;
  margin: 0;
  padding: 4px 0;
  list-style: none;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 6px 20px rgba(0, 0, 0, 0.25);
  min-width: 180px;
  font-size: 13px;
}
.parts-context-menu .ctx-item {
  padding: 6px 14px;
  cursor: pointer;
  user-select: none;
}
.parts-context-menu .ctx-item:hover {
  background: var(--accent-soft);
  color: var(--accent);
}
.parts-context-menu .ctx-item.danger { color: #c0392b; }
.parts-context-menu .ctx-item.danger:hover {
  background: rgba(220, 50, 50, 0.15);
  color: #c0392b;
}
.parts-context-menu .ctx-item.disabled {
  opacity: 0.4;
  cursor: not-allowed;
}
.parts-context-menu .ctx-item.disabled:hover {
  background: none;
  color: inherit;
}
.parts-context-menu .ctx-sep {
  height: 1px;
  background: var(--border);
  margin: 4px 0;
}

/* Bulk-edit modal. Simple centered card on a translucent backdrop.
   [hidden] override is required because the explicit `display: flex`
   masks the HTML attribute's default display:none. */
.parts-modal[hidden] { display: none; }
.parts-modal {
  position: fixed;
  inset: 0;
  z-index: 300;
  display: flex;
  align-items: center;
  justify-content: center;
}
.parts-modal-backdrop {
  position: absolute;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
}
.parts-modal-card {
  position: relative;
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.4);
  padding: 24px;
  min-width: 480px;
  max-width: 90vw;
}
.parts-modal-row {
  display: flex;
  gap: 12px;
  margin-bottom: 18px;
  align-items: flex-end;
}
.parts-modal-row label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 12px;
}
.parts-modal-row label.grow { flex: 1; }
.parts-modal-row label > span { color: var(--muted); }
.parts-modal-row input,
.parts-modal-row select {
  padding: 6px 10px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--panel-2);
  color: inherit;
  font-size: 14px;
}
.parts-modal-actions {
  display: flex;
  gap: 8px;
  justify-content: flex-end;
}

/* --------------------------------------------------- part detail sections */

.detail-section {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  margin-bottom: 14px;
  box-shadow: var(--shadow);
  overflow: hidden;
}
.detail-section header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  padding: 10px 18px;
  background: var(--panel-2);
  border-bottom: 1px solid var(--border);
}
.detail-section header h3 {
  margin: 0;
  font-size: 13px;
  color: var(--accent);
  font-weight: 600;
  text-transform: none;
  letter-spacing: 0;
  padding: 0;
  border: none;
}
.detail-section > .detail-grid,
.detail-section > .inline-row,
.detail-section > p,
.detail-section > ul,
.detail-section > details,
.detail-section > form {
  padding: 12px 18px;
  margin: 0;
}
.detail-section > .inline-row + .inline-row,
.detail-section > .detail-grid + .inline-row { padding-top: 0; }

.detail-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 6px 32px;
}
.detail-grid .field {
  display: flex;
  gap: 8px;
  font-size: 13px;
  padding: 2px 0;
}
.detail-grid .field .label {
  flex: 0 0 160px;
  color: var(--muted);
  font-weight: 600;
}
.detail-grid .field .value { flex: 1; }
@media (max-width: 720px) {
  .detail-grid { grid-template-columns: 1fr; }
}

.inline-row {
  display: flex;
  gap: 12px;
  font-size: 13px;
  border-top: 1px dashed var(--border);
}
.inline-row .label {
  flex: 0 0 160px;
  color: var(--muted);
  font-weight: 600;
}
.inline-row .value { flex: 1; display: flex; flex-wrap: wrap; gap: 18px; }
.pct-item strong { color: var(--muted); font-weight: 600; }

/* Status badges (top-right of each detail section) */
.status-badge {
  display: inline-block;
  padding: 3px 10px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: 0.5px;
  text-transform: uppercase;
}
.status-ok      { background: rgba(79, 156, 109, 0.18); color: var(--ok); }
.status-warn    { background: rgba(213, 150, 68, 0.20); color: var(--warn); }
.status-info    { background: var(--accent-soft); color: var(--accent); }
.status-danger  { background: rgba(201, 87, 87, 0.18); color: var(--error); }
.status-exempt  { background: rgba(79, 156, 109, 0.18); color: var(--ok); }
.status-na      { background: var(--panel); color: var(--muted); border: 1px solid var(--border); }

/* ----------------------------------------- user management table */

.role-chip {
  display: inline-block;
  padding: 2px 9px;
  border-radius: 4px;
  font-size: 11px;
  font-weight: 600;
  letter-spacing: 0.3px;
}
.role-admin { background: rgba(213, 150, 68, 0.18); color: var(--warn); }
.role-user  { background: var(--accent-soft); color: var(--accent); }

.row-actions {
  text-align: right;
  white-space: nowrap;
}
.row-actions > * { margin-left: 6px; }
.row-action {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 3px 9px;
  color: var(--text);
  font-size: 12px;
  cursor: pointer;
  font-family: inherit;
}
.row-action:hover { border-color: var(--accent); color: var(--accent); }
.row-action.danger:hover { border-color: var(--error); color: var(--error); }

/* MID Management — collapsible per-importer groups. Each <details>
 * is one importer; the <summary> is the twisty header. */
.mid-groups { display: flex; flex-direction: column; gap: 10px; }
.mid-group {
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.mid-group-summary {
  /* `display: flex` on a <summary> removes the browser's built-in
   * disclosure marker (it depends on `display: list-item`). Strip the
   * default explicitly + add our own .mid-group-twisty arrow so the
   * twisty is visible across Chrome/Firefox/Safari. */
  cursor: pointer;
  padding: 10px 14px;
  background: var(--panel);
  font-size: 14px;
  display: flex;
  align-items: center;
  gap: 10px;
  user-select: none;
  list-style: none;
}
.mid-group-summary::-webkit-details-marker { display: none; }
.mid-group-summary:hover { background: var(--accent-soft); }
.mid-group-summary code { font-size: 13px; }
.mid-group-twisty {
  display: inline-block;
  font-size: 11px;
  color: var(--muted);
  width: 12px;
  transition: transform 0.15s ease;
}
.mid-group[open] > .mid-group-summary > .mid-group-twisty {
  transform: rotate(90deg);
}
.mid-group[open] > .mid-group-summary {
  border-bottom: 1px solid var(--border);
}
.mid-group > table.data { margin: 0; }

/* The Edit details/summary widget — `<summary>` is the trigger, the
 * form below pops out when expanded. */
.row-action-details { display: inline-block; }
.row-action-details summary {
  list-style: none;  /* hide the default ▶ marker */
  display: inline-block;
}
.row-action-details summary::-webkit-details-marker { display: none; }
.row-action-details[open] summary { color: var(--accent); border-color: var(--accent); }
/* The injected close-without-saving button (base.html wires it into every
   row-action popover). Deliberately quieter than the Save beside it: the
   primary action keeps the visual weight. */
.row-action-cancel {
  background: transparent; color: var(--muted);
  border: 1px solid var(--border); border-radius: 6px;
  padding: 4px 10px; font-size: 12px; cursor: pointer;
}
.row-action-cancel:hover { color: var(--text); border-color: var(--muted); }
.inline-edit-form {
  position: absolute;
  margin-top: 4px;
  padding: 10px;
  background: var(--panel);
  border: 1px solid var(--accent);
  border-radius: 6px;
  display: flex;
  gap: 6px;
  align-items: center;
  z-index: 10;
  box-shadow: 0 4px 12px rgba(0,0,0,0.15);
}
.inline-edit-form input,
.inline-edit-form select {
  width: auto;
  padding: 6px 8px;
  font-size: 12px;
}
.inline-edit-form button {
  padding: 6px 12px;
  font-size: 12px;
}
.inline-checkbox {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  font-size: 12px;
  white-space: nowrap;
  margin: 0;
}
.inline-checkbox input[type="checkbox"] {
  margin: 0;
}

/* Checkboxes get an explicit empty/checked appearance — the native control
   under the dark theme paints an UNCHECKED box as a filled dark square,
   which reads as checked (Heath 2026-08-04, the "Not subject to Section
   232" what-if box). Empty = outlined + transparent; checked = accent fill
   with a white check. Applies app-wide so every surface agrees. */
input[type="checkbox"] {
  appearance: none;
  -webkit-appearance: none;
  width: 15px;
  height: 15px;
  flex: none;
  border: 1.5px solid var(--muted, #9aa0a6);
  border-radius: 3px;
  background: transparent;
  cursor: pointer;
  position: relative;
  vertical-align: middle;
}
input[type="checkbox"]:checked {
  background: var(--accent);
  border-color: var(--accent);
}
input[type="checkbox"]:checked::after {
  content: "";
  position: absolute;
  left: 4px;
  top: 1px;
  width: 4px;
  height: 8px;
  border: solid #fff;
  border-width: 0 2px 2px 0;
  transform: rotate(45deg);
}
input[type="checkbox"]:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
}

.add-user-form {
  display: grid;
  grid-template-columns: 1fr 1fr 150px auto;
  gap: 12px;
  align-items: end;
}
@media (max-width: 720px) {
  .add-user-form { grid-template-columns: 1fr; }
}

.oneshot-password {
  display: inline-block;
  padding: 4px 10px;
  background: var(--accent);
  color: white;
  border-radius: 4px;
  font-size: 14px;
  margin-left: 8px;
  user-select: all;
}

/* Admin sub-nav (Users / HTS Database tabs inside the /admin/* area) */
.admin-subnav {
  display: flex;
  gap: 4px;
  padding: 0 0 16px 0;
  border-bottom: 1px solid var(--border);
  margin-bottom: 16px;
}
.subtab {
  padding: 6px 14px;
  font-size: 13px;
  color: var(--muted);
  border-radius: 5px 5px 0 0;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.subtab:hover { color: var(--accent); text-decoration: none; }
.subtab.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }

/* HTS Database stats panel + search + import form */
.hts-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 16px;
  margin-top: 16px;
  padding: 14px 16px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
}
.hts-stats > div { display: flex; flex-direction: column; gap: 2px; }
.hts-stats .muted { font-size: 11px; text-transform: uppercase; letter-spacing: 0.4px; }
.hts-stats strong { font-size: 14px; color: var(--text); }

.hts-search {
  display: grid;
  grid-template-columns: 1fr 140px auto;
  gap: 12px;
  align-items: end;
  margin-bottom: 8px;
}
.hts-search .grow { grid-column: span 1; }
@media (max-width: 720px) {
  .hts-search { grid-template-columns: 1fr; }
  .search-actions { grid-column: 1; }
}

.hts-import-form {
  display: grid;
  grid-template-columns: 1fr 1fr auto;
  gap: 12px;
  align-items: end;
}
@media (max-width: 720px) {
  .hts-import-form { grid-template-columns: 1fr; }
}
.hts-import-form input[type="file"] {
  width: 100%;
  padding: 8px;
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  color: var(--text);
}

/* Indeterminate progress bar for the HTS import. CSS-only — there's no
 * server-side progress channel today, so we just show "something's
 * happening" while the synchronous POST is in flight (the page
 * navigates away on success). */
.hts-progress { margin-top: 14px; }
.hts-progress-bar {
  height: 5px;
  background: var(--accent-soft);
  border-radius: 3px;
  overflow: hidden;
  position: relative;
}
.hts-progress-bar > div {
  position: absolute;
  top: 0;
  left: -40%;
  width: 40%;
  height: 100%;
  background: var(--accent);
  border-radius: 3px;
  animation: hts-progress-slide 1.5s ease-in-out infinite;
}
@keyframes hts-progress-slide {
  0%   { left: -40%; }
  100% { left: 100%; }
}
.hts-progress-label {
  margin: 8px 0 0 0;
  font-size: 12px;
  color: var(--muted);
}

/* Section 232 row text colors — match the desktop's preview palette.
 *
 *   row-steel       teal             - default 232 metal (was dark
 *                                      gray, which read as a DISABLED
 *                                      row on the light palette —
 *                                      Heath 2026-08-07)
 *   row-aluminum    blue             - Al-classified HTS
 *   row-copper      orange           - Cu-classified HTS
 *   row-wood        green            - wood
 *   row-auto        purple           - automotive
 *   row-non232      red              - Non-232 (non-metal) line
 *   row-notfound    gold             - part not in parts_master
 *   row-incomplete  pink             - in parts_master but missing data
 *   row-zero-metal  orange-red       - 9903.82.01 zero-metal derivative
 *   row-exempt      muted gold       - 9903.82.03 <15% weight exemption
 *   row-sec301      magenta          - parts_master has a 301 exclusion
 *
 * Applied via a class on each <tr> — see _row_color_class() in routes/web.py.
 * Theme-aware: dark-mode variants brighten the dim ones so they stay
 * visible against the dark panel background. */
table.data tr.row-steel      td { color: #00838f; }
table.data tr.row-aluminum   td { color: #2058c9; }  /* deeper blue — steel is teal; ΔE vs steel 16.6 (was 12.6) */
table.data tr.row-copper     td { color: #e67e22; }
table.data tr.row-wood       td { color: #558b2f; }              /* olive — leaves "true" green for non232 */
table.data tr.row-auto       td { color: #9b59b6; }
table.data tr.row-non232     td { color: #2e7d32; font-weight: 600; }  /* green — "not 232, no action needed" */
table.data tr.row-notfound   td { color: #d32f2f; font-weight: 700; }  /* vibrant red — needs attention */
table.data tr.row-incomplete td { color: #d32f2f; font-weight: 700; }
table.data tr.row-zero-metal td { color: #FF6600; font-weight: 600; }
table.data tr.row-exempt     td { color: #b07d2b; }

/* Preview-only service / packaging lines (mmcité SLU services, OBAL packing /
 * fumigation): shown for operator reference, NOT in the export file. Muted +
 * italic so they read as "informational, not an entry line", with a left bar
 * and a "not exported" tag on the Product No cell. */
table.data tr.row-preview-only td { color: var(--violet-ink); font-style: italic; background: var(--violet-bg); }
table.data tr.row-preview-only td:nth-child(2) { box-shadow: inset 3px 0 0 #7a6aa0; }
table.data tr.row-preview-only td:nth-child(2)::after {
  content: " · not exported";
  font-size: 10px;
  font-style: normal;
  color: #9a8bbf;
  letter-spacing: .02em;
}

/* Section 301 — DON'T tint the whole row, just light up the Product No
 * cell with a magenta pill. The previous row-wide magenta text was
 * too close to row-notfound's red on operator screens (the user
 * couldn't tell sec301 from notfound at a glance). Targeted column
 * highlight makes the 301 status unmistakable without competing with
 * material / data-quality row tints. Product No is the 2nd <td>
 * (the 1st is the row-number index column rendered as <td class="muted">). */
table.data tr.row-sec301 td:nth-child(2) {
  background: rgba(194, 24, 91, 0.18);
  box-shadow: inset 3px 0 0 #c2185b;
  color: #c2185b;
  font-weight: 700;
  border-radius: 3px;
}

/* Dark-mode row-color variants must fire on BOTH dark triggers — the OS
 * media query AND the Appearance-menu's [data-theme="dark"] override
 * (Heath 2026-07-20: forcing Dark from the menu on a light OS left the
 * steel rows at the light-mode #4a4a4a — unreadable on the dark panel).
 * Same dual-selector pattern as the main palette block; the media branch
 * excludes an explicit Light choice. */
@media (prefers-color-scheme: dark) {
  /* Brighten every hue just enough to stay visible on the dark panel. */
  :root:not([data-theme="light"]) table.data tr.row-steel      td { color: #4dd0e1; }
  :root:not([data-theme="light"]) table.data tr.row-aluminum   td { color: #82aaff; }  /* ΔE vs steel 12.8 (was 8.2) */
  :root:not([data-theme="light"]) table.data tr.row-copper     td { color: #f5a05c; }
  :root:not([data-theme="light"]) table.data tr.row-wood       td { color: #9ccc65; }
  :root:not([data-theme="light"]) table.data tr.row-auto       td { color: #c084e0; }
  :root:not([data-theme="light"]) table.data tr.row-non232     td { color: #66bb6a; }
  :root:not([data-theme="light"]) table.data tr.row-notfound   td { color: #ff5252; }
  :root:not([data-theme="light"]) table.data tr.row-incomplete td { color: #ff5252; }
  :root:not([data-theme="light"]) table.data tr.row-zero-metal td { color: #ff944d; }
  :root:not([data-theme="light"]) table.data tr.row-exempt     td { color: #d4a259; }
  /* Dark-mode sec301 pill — brighter rose against the dark panel. */
  :root:not([data-theme="light"]) table.data tr.row-sec301 td:nth-child(2) {
    background: rgba(236, 64, 122, 0.22);
    box-shadow: inset 3px 0 0 #ec407a;
    color: #ec407a;
  }
}
:root[data-theme="dark"] table.data tr.row-steel      td { color: #4dd0e1; }
:root[data-theme="dark"] table.data tr.row-aluminum   td { color: #82aaff; }  /* ΔE vs steel 12.8 (was 8.2) */
:root[data-theme="dark"] table.data tr.row-copper     td { color: #f5a05c; }
:root[data-theme="dark"] table.data tr.row-wood       td { color: #9ccc65; }
:root[data-theme="dark"] table.data tr.row-auto       td { color: #c084e0; }
:root[data-theme="dark"] table.data tr.row-non232     td { color: #66bb6a; }
:root[data-theme="dark"] table.data tr.row-notfound   td { color: #ff5252; }
:root[data-theme="dark"] table.data tr.row-incomplete td { color: #ff5252; }
:root[data-theme="dark"] table.data tr.row-zero-metal td { color: #ff944d; }
:root[data-theme="dark"] table.data tr.row-exempt     td { color: #d4a259; }
:root[data-theme="dark"] table.data tr.row-sec301 td:nth-child(2) {
  background: rgba(236, 64, 122, 0.22);
  box-shadow: inset 3px 0 0 #ec407a;
  color: #ec407a;
}

/* Country-inferred indicator. Applied to CountryofMelt /
 * CountryOfCast / PrimCountryOfSmelt cells when the value came from
 * the MID-prefix fallback (no document-extracted country). Subtle —
 * italic + dotted underline + a trailing asterisk in the template
 * — so multi-country shipments stand out for review without
 * blocking the export or competing with row-level color signals
 * (which are reserved for "needs attention" states like notfound /
 * incomplete / sec301). Hover shows the explanatory tooltip. */
table.data td.country-inferred {
  font-style: italic;
  text-decoration: underline dotted;
  text-decoration-color: var(--muted);
  cursor: help;
}

/* Templates tab — keyword chips + collapsible Map Profile cards */

code.kw {
  display: inline-block;
  padding: 1px 7px;
  margin: 1px 4px 1px 0;
  background: var(--accent-soft);
  color: var(--accent);
  border-radius: 3px;
  font-size: 11px;
}

.profile-card {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 8px;
  padding: 10px 14px;
}
.profile-card summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 2px 0;
}
.profile-card summary::-webkit-details-marker { display: none; }
.profile-card summary::before {
  content: "▸";
  color: var(--muted);
  font-size: 10px;
  width: 12px;
  display: inline-block;
  transition: transform 0.1s;
}
.profile-card[open] summary::before {
  transform: rotate(90deg);
}

/* Client-group twisty (PDF Supplier Templates page) */
.client-group {
  background: var(--panel-2);
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 8px;
  padding: 8px 12px;
}
.client-group summary {
  cursor: pointer;
  list-style: none;
  display: flex;
  gap: 10px;
  align-items: center;
  padding: 6px 0;
  user-select: none;
}
.client-group summary::-webkit-details-marker { display: none; }
.client-group summary::before {
  content: "▸";
  color: var(--muted);
  font-size: 11px;
  width: 14px;
  display: inline-block;
  transition: transform 0.1s;
}
.client-group[open] summary::before { transform: rotate(90deg); }
.client-group .table-scroll { padding-top: 4px; }

/* Data Quality Flag list */
.flag-list { list-style: none; }
.flag-list .flag {
  display: flex;
  gap: 10px;
  padding: 6px 0;
  font-size: 13px;
}
.flag-icon {
  flex: 0 0 18px;
  color: var(--warn);
  font-weight: bold;
}
.flag-danger .flag-icon { color: var(--error); }
.flag-info .flag-icon { color: var(--accent); }

/* ---------------------------------------------------------- column resize
 * Tables marked `class="resizable"` get a thin drag handle on the right
 * edge of every <th>. The handle is invisible until hovered so it
 * doesn't compete with the header text; the cursor switch on hover is
 * the affordance. JS in invoice_processing.html owns the drag state +
 * localStorage persistence keyed by column header text. */
/* table-layout: auto so columns size to their widest cell -- the
 * outer .table-scroll container then horizontally scrolls when the
 * total table width exceeds the viewport. Heath asked 2026-05-30 for
 * left/right scroll on the dashboard's Last Result Preview after the
 * previous `fixed` layout was squeezing 20+ columns into the visible
 * width and ellipsizing headers ("COUN...", "PRIMCOUN..."). With
 * auto, every column shows its full content/header by default and
 * the operator scrolls horizontally to see the wide tail. */
table.resizable { table-layout: auto; width: max-content; min-width: 100%; }
/* th + td still allow ellipsis IF the operator drag-narrows a column
 * (the resize JS sets explicit pixel widths on the <th>, which
 * auto-layout still respects). Without an explicit width set,
 * white-space: nowrap means columns grow to fit content instead of
 * wrapping to multiple lines. */
table.resizable th { position: relative; white-space: nowrap; }
table.resizable td { white-space: nowrap; }
table.resizable th[style*="width"],
table.resizable td[style*="width"] {
  overflow: hidden;
  text-overflow: ellipsis;
}
.col-resize-handle {
  position: absolute;
  top: 0; right: 0;
  width: 5px;
  height: 100%;
  cursor: col-resize;
  user-select: none;
  background: transparent;
  transition: background-color 0.12s;
}
.col-resize-handle:hover,
.col-resize-handle.dragging {
  background-color: var(--accent);
}
body.col-resizing,
body.col-resizing * { cursor: col-resize !important; user-select: none !important; }

/* ------------------------------------------------------------ help tip
 * Small "?" chip whose hover popover holds the explanatory copy that
 * used to occupy three full paragraphs around the Invoice Processing
 * drop zone. Keyboard-accessible via tabindex on the wrapper; the
 * popover also reveals on focus so non-mouse users can read it. */
.help-tip { position: relative; display: inline-block; cursor: help; outline: none; }
.help-tip-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 18px; height: 18px;
  border-radius: 50%;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 12px;
  font-weight: 700;
  font-family: serif;
  user-select: none;
}
.help-tip-body {
  position: absolute;
  top: calc(100% + 8px);
  left: 0;
  min-width: 320px;
  max-width: 440px;
  padding: 10px 12px;
  background: var(--panel);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
  box-shadow: var(--shadow);
  color: var(--text);
  font-size: 12.5px;
  line-height: 1.5;
  z-index: 1000;
  opacity: 0;
  pointer-events: none;
  transform: translateY(-2px);
  transition: opacity 0.12s, transform 0.12s;
}
.help-tip:hover .help-tip-body,
.help-tip:focus .help-tip-body,
.help-tip:focus-within .help-tip-body {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
}

/* ============================================================
 * Dashboard — throughput + time-savings (v0.4.115)
 * ============================================================ */
.dash-toggles { display: flex; gap: 10px; flex-wrap: wrap; }
.seg {
  display: inline-flex;
  border: 1px solid var(--border);
  border-radius: 6px;
  overflow: hidden;
}
.seg-btn {
  padding: 6px 12px;
  font-size: 13px;
  color: var(--muted);
  background: var(--panel);
  border-right: 1px solid var(--border);
}
.seg-btn:last-child { border-right: none; }
.seg-btn:hover { background: var(--accent-soft); }
.seg-btn.active { background: var(--accent); color: #fff; }

.dash-stats {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: 12px;
  margin: 16px 0;
}
@media (max-width: 760px) { .dash-stats { grid-template-columns: repeat(2, 1fr); } }
.stat-card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 16px 18px;
  box-shadow: var(--shadow);
}
.stat-card.accent { border-color: var(--accent); background: var(--accent-soft); }
.stat-value { font-size: 28px; font-weight: 700; color: var(--text); line-height: 1.1; }
.stat-unit { font-size: 16px; font-weight: 600; color: var(--muted); }
.stat-label { font-size: 12px; color: var(--muted); margin-top: 6px; text-transform: uppercase; letter-spacing: 0.04em; }

.cmp-row { display: flex; align-items: center; gap: 12px; margin: 8px 0; }
.cmp-label { width: 200px; flex-shrink: 0; font-size: 13px; color: var(--muted); }
.cmp-bar-wrap { flex: 1; background: var(--panel-2); border-radius: 5px; overflow: hidden; }
.cmp-bar {
  padding: 8px 10px;
  font-size: 12px;
  font-weight: 600;
  color: #fff;
  white-space: nowrap;
  min-width: fit-content;
  border-radius: 5px;
}
.cmp-bar.manual { background: var(--border-strong); }
.cmp-bar.eo { background: var(--ok); }

.day-chart {
  display: flex;
  align-items: flex-end;
  gap: 4px;
  height: 140px;
  padding-top: 8px;
  overflow-x: auto;
}
.day-col {
  flex: 1 0 18px;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: flex-end;
  height: 100%;
}
.day-bar {
  width: 70%;
  min-height: 2px;
  background: var(--accent);
  border-radius: 3px 3px 0 0;
  transition: height 0.2s;
}
.day-col:hover .day-bar { background: var(--accent-hover); }
.day-x { font-size: 9px; color: var(--muted); margin-top: 4px; transform: rotate(-45deg); white-space: nowrap; }

.dash-cols { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
@media (max-width: 760px) { .dash-cols { grid-template-columns: 1fr; } }

.baseline-form {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  max-width: 520px;
}
.baseline-form label { display: flex; flex-direction: column; gap: 4px; font-size: 13px; }
.baseline-form input { padding: 6px 8px; }

/* ============================================================
 * Super-admin tenant monitoring + impersonation (v0.4.117)
 * ============================================================ */
.impersonation-banner {
  background: var(--warn);
  color: #1f1500;
  padding: 8px 16px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 12px;
  font-size: 13px;
  font-weight: 500;
}
.impersonation-banner code { background: rgba(0,0,0,0.12); padding: 1px 5px; border-radius: 3px; }
.impersonation-exit {
  background: #1f1500;
  color: #fff;
  border: none;
  padding: 5px 12px;
  border-radius: 5px;
  font-size: 12px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.impersonation-exit:hover { background: #000; }

/* Small status pills used across the tenant overview/detail. */
.pill {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 10px;
  font-size: 11px;
  font-weight: 600;
  background: var(--panel-2);
  color: var(--muted);
  border: 1px solid var(--border);
}
.pill-plan { background: var(--accent-soft); color: var(--accent-hover); border-color: var(--accent); }
.pill-ok   { background: rgba(79,156,109,0.15); color: var(--ok); border-color: var(--ok); }
.pill-warn { background: rgba(213,150,68,0.18); color: var(--warn); border-color: var(--warn); }

table.data.sa-tenants td { vertical-align: top; }
.stat-card.warn { border-color: var(--warn); background: rgba(213,150,68,0.12); }

/* ============================================================
 * HTS detail / duty stack (v0.4.120)
 * ============================================================ */
/* STATIC field grid (Heath 2026-08-20: "make the field grid static").
   Was a wrapping flexbox, so every field sized itself to its own content and
   its own inline max-width — six different widths on one row, a wrap point
   that moved with the content, and the same form laid out differently on each
   of the three pages that use this class. Now a grid of FIXED columns: a
   field is always the same width and always lands in the same cell, and the
   row wraps a whole column at a time.

   auto-fill (not auto-fit) and a fixed track (not 1fr) are both deliberate —
   1fr would stretch the fields to fill the row, which is the resizing this is
   meant to stop. Leftover space at the right edge is the point. */
.hts-detail-form {
  --lookup-col: 220px;
  display: grid;
  grid-template-columns: repeat(auto-fill, var(--lookup-col));
  gap: 14px 18px;
  align-items: end;
  margin-top: 14px;
  padding-top: 14px;
  border-top: 1px solid var(--border);
}
.hts-detail-form label {
  display: flex;
  flex-direction: column;
  gap: 4px;
  font-size: 13px;
}
/* Text/select controls only. WITHOUT the :not() the 220px min-width also hit
   the "Not subject to Section 232" CHECKBOX, stretching it into a wide filled
   bar that read as a button (Heath 2026-08-04). The checkbox keeps the
   app-wide sizing from the input[type="checkbox"] rules above. */
/* Fill the grid cell rather than declaring a width. min-width:0 is required:
   a grid item's default min-width is auto, so a <select> with long option
   text (the Section 232 "Auto — assess per the HTS" choices) would otherwise
   push its own column wider and break the uniform grid from the inside. */
.hts-detail-form select,
.hts-detail-form input:not([type="checkbox"]):not([type="radio"]) {
  padding: 6px 8px;
  width: 100%;
  min-width: 0;
}
/* Labels wrap inside their column instead of widening it. Rows bottom-align
   (align-items:end), so a two-line caption still leaves every input on the
   same baseline as its neighbours. */
.hts-detail-form label { min-width: 0; }
.hts-detail-form label > span { overflow-wrap: anywhere; }
/* …and the checkbox's LABEL must not use the column layout the field labels
   use, or the box stacks above its caption instead of sitting beside it. */
.hts-detail-form label.inline-checkbox {
  flex-direction: row;
  align-items: center;
  gap: 8px;
  white-space: normal;
  max-width: 260px;
}

.hts-stack {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: 12px;
  margin: 16px 0;
}
@media (max-width: 860px) { .hts-stack { grid-template-columns: 1fr; } }
.hts-stack > .card { margin: 0; }
.card-head {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 8px;
  gap: 10px;
}
.card-head h3 { margin: 0; font-size: 16px; }

table.data tr.row-total td {
  border-top: 2px solid var(--accent);
  padding-top: 10px;
}

/* HTS detail — per-section include toggle (v0.4.126) */
.incl-toggle {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  cursor: pointer;
  user-select: none;
  font-size: 11px;
  font-weight: 500;
  color: var(--muted);
  margin-left: auto;
  margin-right: 8px;
  padding: 3px 8px;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: var(--panel-2);
}
.incl-toggle:hover { background: var(--accent-soft); color: var(--accent); }
.incl-toggle input { margin: 0; cursor: pointer; }
.incl-toggle input:checked + .incl-toggle-label { color: var(--accent); }

/* Update-in-progress banner. Sits at the very top of every page
   (above .topbar) when a new app version has rolled out and the
   currently-loaded page is stale. Sticky so it stays visible while
   the operator scrolls. */
.update-banner {
  display: flex;
  align-items: center;
  gap: 12px;
  position: sticky;
  top: 0;
  z-index: 9999;
  padding: 10px 16px;
  background: #fef3c7;          /* amber-100 */
  border-bottom: 2px solid #d97706;  /* amber-600 */
  color: #78350f;                /* amber-900 */
  font-size: 13px;
  box-shadow: 0 2px 6px rgba(0, 0, 0, 0.08);
}
.update-banner[hidden] { display: none; }
.update-banner-text { flex: 1; }
.update-banner-text strong { color: #78350f; }
.update-banner .primary {
  padding: 4px 12px;
  font-size: 12px;
  background: #d97706;
  color: white;
  border: 1px solid #b45309;
  border-radius: 3px;
  cursor: pointer;
}
.update-banner .primary:hover { background: #b45309; }
.update-banner-dismiss {
  background: transparent;
  border: 0;
  color: #78350f;
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 0 4px;
}
.update-banner-dismiss:hover { color: #92400e; }

/* ---- .data-table -----------------------------------------------------------
 * Shared row/column table for the operator surfaces (ISF, Entry Audit). It was
 * only ever styled inside the Metrics SPA, so on base.html pages the columns
 * rendered flush against each other. Metrics keeps its own copy after this
 * file, so its styling still wins there. */
.data-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
.data-table th,
.data-table td {
  padding: 7px 14px;              /* the breathing room between columns */
  text-align: left;
  vertical-align: top;
  border-bottom: 1px solid var(--border);
}
.data-table th {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.4px;
  color: var(--muted);
  white-space: nowrap;
  border-bottom: 1px solid var(--border-strong);
}
.data-table tbody tr:last-child td { border-bottom: none; }
.data-table tbody tr:hover { background: var(--panel-2); }

/* Filed / not-filed dot (ISF records). Read-only: the value is DERIVED from the
 * CM feed's isf_accepted_date, the same signal Metrics shows — not something an
 * operator sets by hand. */
.status-dot {
  display: inline-block; width: 11px; height: 11px; border-radius: 50%;
  background: var(--border-strong);
}
.status-dot.on { background: var(--ok); }

/* Tools sub-nav — shared pill bar for the /tools/* pages. */
.tools-nav { display: flex; gap: 8px; margin-bottom: 14px; }
.tools-nav a { padding: 6px 14px; border: 1px solid var(--border); border-radius: 999px;
               font-size: 13px; font-weight: 600; text-decoration: none; color: var(--text);
               background: var(--panel); }
.tools-nav a.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.tools-nav a:not(.active):hover { border-color: var(--accent); text-decoration: none; }


/* Compliance dashboard: the calendar / impact feed / new-parts panels stacked
   in ONE card with tabs (Heath 2026-08-05). Sub-tabs inside a card, so they
   read as lighter than the page-level .trade-tabs pills above them. */
.cc-tabs {
  display: flex;
  gap: 4px;
  flex-wrap: wrap;
  margin: 0 0 10px;
  border-bottom: 1px solid var(--border);
}
.cc-tabs button {
  padding: 6px 14px;
  border: 0;
  border-bottom: 2px solid transparent;
  background: none;
  color: var(--muted);
  font: inherit;
  font-size: 13px;
  cursor: pointer;
  border-radius: 0;
}
.cc-tabs button:hover { color: var(--fg); }
.cc-tabs button.active {
  color: var(--accent);
  border-bottom-color: var(--accent);
  font-weight: 600;
}
.cc-panel { display: none; }
.cc-panel.active { display: block; }

/* ---- /trade HTS Lookup: inputs vs operator claims -----------------------
   Two fieldsets, deliberately. The inputs DESCRIBE the line; the claims
   ASSERT something about the goods that the engine refuses to infer. Before
   this they shared one flex row and wrapped mid-group, orphaning two
   checkboxes onto a second line beside the submit button. */
.hts-detail-form > fieldset { border: 0; margin: 0; padding: 0; min-width: 0; }
.hts-detail-form > fieldset.lookup-inputs {
  grid-column: 1 / -1;
  display: grid;
  grid-template-columns: repeat(auto-fill, var(--lookup-col));
  gap: 14px 18px; align-items: end; }

/* The claims fieldset stays a BLOCK. Making it a flex container turns
   <legend> into a flex item, which stops it straddling the top border and
   drops it onto its own line above the box — the misalignment Heath caught.
   The flex row is the inner .claims-row instead. */
.hts-detail-form > fieldset.claims-group {
  grid-column: 1 / -1;
  display: block;
  border: 1px solid var(--border); border-radius: 8px;
  padding: 4px 14px 12px; margin-top: 14px; background: var(--panel-2); }
.hts-detail-form > fieldset.claims-group legend {
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); padding: 0 6px; margin-left: 2px; }
.claims-row {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px 26px;
  padding-top: 2px; }
/* Single-line labels here, so centre the box against the text and drop the
   multi-line wrapping the 232 what-if box needs. */
.claims-row label.inline-checkbox {
  align-items: center; white-space: nowrap; max-width: none;
  gap: 8px; margin: 0; line-height: 1.2; }
.claims-row .inline-checkbox span { font-weight: 500; }
.claims-row input[type="checkbox"] { flex: 0 0 auto; }

/* ---- part detail: SPI claimed + its eligibility hint --------------------
   The hint used to sit UNDER the input inside a narrow flex column, where it
   wrapped to four ragged lines and dwarfed the field it annotates. Beside it
   instead: the input keeps its natural size, the hint takes the slack. */
.spi-field { display: flex; align-items: center; gap: 10px; min-width: 0; }
.spi-field input { flex: 0 0 148px; width: 148px; }
.spi-eligible { flex: 1 1 auto; min-width: 0; font-size: 11px; line-height: 1.35; }
.spi-eligible code { font-size: 10.5px; }
@media (max-width: 900px) {
  /* narrow viewports have no slack to give — stack rather than squeeze the
     input down to nothing */
  .spi-field { flex-direction: column; align-items: stretch; gap: 4px; }
  .spi-field input { flex: 0 0 auto; width: 100%; }
}

/* ---- /trade: HTS reference behind CBP's latest HSU ----------------------
   The loaded tariff is the provenance for every lookup, health check and
   export on the page, so "a Harmonized System Update landed after we loaded"
   is the one thing on this masthead worth interrupting for. */
.trr-stale { display: inline-block; margin-top: 3px; padding: 1px 7px;
  border: 1px solid var(--warn-edge); border-radius: 4px;
  background: var(--warn-bg); color: var(--warn-ink);
  font-weight: 600; font-size: 11px; }


/* ---- CSMS implementation status pill -------------------------------------
   ONE definition, shared by /compliance (the registry table) and the CSMS
   impact feed on /trade. They used to be different things: the feed rendered
   csms_inbox.status -- the MAIL QUEUE's state, 'pending' until an agent
   session drains it -- while /compliance rendered the registry's ruling from
   csms.yaml. So a bulletin ruled `implemented` at the EOD close still read
   "pending" on the feed (Heath 2026-08-20: 14 of 17 live bulletins). Same
   markup and same stylesheet now, so they cannot drift apart again. */
.csms-status {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
}
.csms-status.implemented { background: rgba(40, 180, 60, 0.15); color: #228040; }
.csms-status.partial { background: rgba(255, 200, 0, 0.18); color: #8a6a00; }
.csms-status.pending { background: rgba(120, 120, 120, 0.15); color: var(--muted); }
.csms-status.watch { background: rgba(120, 140, 200, 0.15); color: #4a5f96; }
/* A considered no-op. Muted like `pending`, but it is the opposite
   claim: nothing is coming because nothing is needed. */
.csms-status.not_applicable { background: rgba(120, 120, 120, 0.12);
   color: var(--muted); }
.csms-status.superseded { background: rgba(120, 120, 120, 0.10); color: var(--muted);
                          text-decoration: line-through; text-decoration-thickness: 1px; }
/* ARRIVED BUT NOT RULED ON -- the only state that is an open item, so it is
   the only loud one. Deliberately NOT reusing `pending`: on the registry that
   word means "we looked, work is owed"; here it would mean "nobody has looked
   yet". Two meanings on one screen under one word is what hid this. */
.csms-status.unreviewed {
  background: rgba(213, 150, 68, 0.20); color: var(--warn);
  border: 1px solid rgba(213, 150, 68, 0.45);
}
