:root {
  --bg: #FAF9F5;
  --surface: #FFFFFF;
  --ink: #1C2127;
  --ink-muted: #6B7078;
  --ink-faint: #9AA0A8;
  --primary: #1B2A41;
  /* Sampled from the C Lighting logo (pure #0000FF), darkened + slightly
     desaturated so it works as a large panel while keeping white text at
     8.6:1 contrast. */
  --brand-blue: #1F3FB5;
  --primary-light: #2E4560;
  --accent: #1F3FB5;
  --accent-soft: #E7ECFA;
  --border: #E7E3D8;
  --danger: #B84A2F;
  --danger-soft: #F6E4DE;
  --radius: 8px;
  --shadow: 0 8px 24px rgba(27, 42, 65, 0.10);
  --font-display: ui-serif, Georgia, "Iowan Old Style", "Palatino Linotype", serif;
  --font-body: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, system-ui, sans-serif;
  --font-mono: ui-monospace, "SF Mono", Consolas, monospace;

  /* ================= r231 -- THE MODULE GUTTER =================
     [stated] "Modules should NOT fit the entire width of the screen. There
     should be spacing on all sides. Some have the spacing and some do not...
     quotes do NOT have it, but accounting DOES have it."

     He was reading a real inconsistency. Before this round the inset was
     written FIVE times in four different places and two screens had none:

       .main            no padding of its own; .topbar and .table-wrap each
                        carried their own 32px sides, and #section-wrap (the
                        quotes three-section view, r105) carried NONE -- which
                        is exactly the screen he named
       .quote-editor    24px 32px 40px      <- accounting, the one that looked right
       .settings-screen 24px 32px 60px
       .home-screen     26px 30px 40px
       .accounting-screen (#spec-screen)    no rule at all -> edge to edge
       .qe-screen         (#counter-editor) no rule at all -> edge to edge

     Now there is ONE inset, defined here and applied by ONE rule (search
     "MODULE FRAME"). Content still fills the width -- there is deliberately no
     max-width, per his answer -- it is simply held off all four edges.
     tests/audit-r231-check.js fails if a second copy of this padding
     reappears on any screen-level selector. */
  --module-gutter: 28px;          /* left and right, every module */
  --module-gutter-top: 22px;
  --module-gutter-bottom: 28px;
}

* { box-sizing: border-box; }

html, body {
  margin: 0;
  height: 100%;
  background: var(--bg);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 14px;
  -webkit-font-smoothing: antialiased;
}

button, input, select, textarea { font-family: inherit; font-size: inherit; color: inherit; }

.app {
  display: flex;
  height: 100vh;
}

/* ---------- Sidebar ---------- */
.sidebar {
  width: 232px;
  flex-shrink: 0;
  /* Brand blue, taken from the C Lighting logo (#0000FF) but darkened and
     slightly desaturated for a large surface -- pure #0000FF vibrates badly
     across a whole panel. #1F3FB5 holds an 8.6:1 contrast against white
     text, so every label reads cleanly. */
  background: var(--brand-blue);
  color: #FFFFFF;
  display: flex;
  flex-direction: column;
  padding: 22px 14px;
  overflow-y: auto;
}

.brand {
  display: flex;
  align-items: center;
  gap: 9px;
  padding: 4px 10px 26px 10px;
}

.brand-glow {
  width: 9px;
  height: 9px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 10px 3px rgba(232, 163, 61, 0.65);
  flex-shrink: 0;
}

.brand-word {
  font-family: var(--font-display);
  font-size: 19px;
  letter-spacing: 0.02em;
}

.nav {
  display: flex;
  flex-direction: column;
  gap: 2px;
}

.nav-item {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: none;
  border: none;
  border-left: 2px solid transparent;
  color: #FFFFFF;
  text-align: left;
  padding: 9px 12px;
  border-radius: 0 6px 6px 0;
  cursor: pointer;
  transition: background 0.15s ease, color 0.15s ease, border-color 0.15s ease;
}

.nav-item:hover {
  background: rgba(255,255,255,0.14);
  color: #fff;
}

.nav-item.active {
  background: rgba(255,255,255,0.20);
  border-left-color: var(--accent);
  color: #fff;
  font-weight: 600;
}

.nav-label { font-size: 13.5px; }

/* !! r224 -- .nav-count IS GONE FROM THE SIDEBAR.
   [stated] "You can remove the counts from the left sidebar and move them
   into each module. So when I open up CRM itll say 'XXX' Customers, or if i
   open the quotes itll say 'XXX' quotes."
   The count now renders beside the module's own title (.view-count, below),
   which also killed refreshCounts() -- eighteen list requests fired on every
   single view change to populate numbers nobody was reading. */

/* Modules that aren't built yet: visibly present so the shape of the app is
   clear, but obviously inert rather than looking like a broken link. */
.nav-item-todo { opacity: 0.62; }
.nav-item-todo:hover { background: rgba(255,255,255,0.08); }
.nav-soon {
  font-size: 9px; text-transform: uppercase; letter-spacing: 0.06em;
  background: rgba(255,255,255,0.16); color: #FFFFFF;
  padding: 1px 6px; border-radius: 999px;
}

/* ---------- Sidebar nav groups (collapsible) ---------- */
.nav-group { display: flex; flex-direction: column; }
.nav-group-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 8px;
  background: none;
  border: none;
  /* Group headers were small, grey, and easy to miss -- now full white,
     larger, and heavier so the sections read at a glance. */
  color: #FFFFFF;
  text-align: left;
  padding: 12px 12px 7px;
  font-size: 12.5px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  cursor: pointer;
}
.nav-group-header:hover { color: #fff; background: rgba(255,255,255,0.08); border-radius: 6px; }
.nav-group-chevron {
  /* Was a 10px glyph that basically disappeared. */
  font-size: 17px;
  line-height: 1;
  opacity: 0.95;
  transition: transform 0.18s ease;
}
.nav-group.is-open .nav-group-chevron { transform: rotate(180deg); }
.nav-group-body {
  display: none;
  flex-direction: column;
  gap: 2px;
  padding-bottom: 4px;
}
.nav-group.is-open .nav-group-body { display: flex; }
/* A group containing the active view gets a subtle highlight on its own
   header even while collapsed, so the sidebar still shows roughly where
   you are without forcing every group open at once. */
.nav-group.has-active .nav-group-header { color: #fff; }

.sidebar-foot {
  margin-top: auto;
  font-size: 11.5px;
  line-height: 1.5;
  color: rgba(255,255,255,0.72);
  padding: 10px;
  border-top: 1px solid rgba(255,255,255,0.22);
}

/* ---------- Main ---------- */
.main {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
}
.main[hidden] { display: none; }

/* ================= r231 -- THE MODULE FRAME =================
   Every screen-level container in the app, one inset, taken from the
   --module-gutter variables at the top of this file. Add a new full-screen
   section? Put its class here and it is spaced like everything else.

   !! PADDING, NOT MARGIN, AND NOT max-width.
   Margin on a flex child that also scrolls collapses in ways that differ per
   screen, and a max-width was explicitly NOT what he asked for -- the content
   still fills the space, it is just held off the edges.

   !! DO NOT ADD `display:` TO THIS RULE.
   r206: a display rule on a selector that also matches a [hidden] element
   beats the hidden attribute and paints the screen anyway. This rule sets
   padding and nothing else, which is why tests/check-hidden.js still passes. */
.main,
.home-screen,
.quote-editor,
.accounting-screen,
.qe-screen,
.settings-screen {
  padding: var(--module-gutter-top) var(--module-gutter) var(--module-gutter-bottom);
}
.quote-editor[hidden] { display: none; }
.settings-screen[hidden] { display: none; }

.topbar {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  /* r231: the 32px sides moved to the MODULE FRAME rule above. Only the space
     between the title block and the content below it belongs to the topbar. */
  padding: 0 0 18px 0;
}

.topbar h1 {
  font-family: var(--font-display);
  font-size: 26px;
  margin: 0 0 4px 0;
  font-weight: 600;
}

.view-sub {
  margin: 0;
  color: var(--ink-muted);
  font-size: 13px;
}

/* r224: the count that used to sit in the sidebar, now beside the module's
   own title -- "142 Customers". */
.view-count {
  margin: 2px 0 3px;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink);
}
.view-count .view-count-flag {
  font-weight: 600;
  color: var(--danger, #c62828);
  margin-left: 8px;
}

.topbar-actions {
  display: flex;
  gap: 10px;
  align-items: center;
}

#search {
  border: 1px solid var(--border);
  background: var(--surface);
  border-radius: 6px;
  padding: 8px 12px;
  width: 220px;
  outline: none;
}
#search:focus { border-color: var(--primary-light); }

.btn {
  border: none;
  border-radius: 6px;
  padding: 9px 16px;
  font-size: 13.5px;
  font-weight: 600;
  cursor: pointer;
  transition: filter 0.12s ease, background 0.12s ease;
}
.btn:active { filter: brightness(0.94); }

.btn-primary { background: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary-light); }

.btn-ghost { background: transparent; color: var(--ink-muted); }
.btn-ghost:hover { background: #F0EEE7; color: var(--ink); }

.btn-danger { background: var(--danger-soft); color: var(--danger); }
.btn-danger:hover { background: #EFD2C7; }

/* ==========================================================================
   !! r270 -- A BUTTON THE PROGRAM HAS SWITCHED OFF MUST LOOK SWITCHED OFF.
   ==========================================================================

   `.btn-sm:disabled` has dimmed a small button since round 208. A FULL-SIZE
   `.btn` had no disabled rule at all, so sixty-nine places where app.js writes
   `el.disabled = true` produced a button that is dead and looks exactly like
   one that works. Two of them are on screens this round drove:

     #ret-raise-credit  dead until the manufacturer settles the claim
     #sc-raise          dead when nothing is in default

   -- and in both cases the person's reaction to a solid, hovering, unreactive
   button is to press it again. It is r259's rule read backwards: a UI that
   reads correctly can still do nothing, and a control that does nothing must
   say so without being pressed.

   The hover rules are re-stated per variant because `.btn-primary:hover` would
   otherwise still light up under the cursor; `:disabled:hover` is one class
   more specific and wins. */
.btn:disabled, .btn[disabled] { opacity: 0.45; cursor: not-allowed; filter: none; }
.btn:disabled:active, .btn[disabled]:active { filter: none; }
.btn-primary:disabled:hover, .btn-primary[disabled]:hover { background: var(--primary); }
.btn-ghost:disabled:hover, .btn-ghost[disabled]:hover { background: transparent; color: var(--ink-muted); }
.btn-danger:disabled:hover, .btn-danger[disabled]:hover { background: var(--danger-soft); }

/* ---------- Table ---------- */
.table-wrap {
  flex: 1;
  overflow: auto;
  /* r231: sides and bottom moved to the MODULE FRAME rule. This wrapper is the
     horizontal scroller for a wide grid; it must not also own the gutter, or
     the gutter scrolls away with the table. */
  padding: 0;
}

table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  overflow: hidden;
}

/* ========================================================================
   !! r302 [his 2, 09/22/2026] -- A COLUMN HEADING HAS TO BE READABLE.
   ========================================================================

   [stated] "these columns headers should be bold - I can't read shit"

   His screenshot is the quote line grid, where #/ROW/TYPE/QTY/MFG/PART # sat
   in --ink-faint (#9AA0A8) on the #FCFBF8 header band -- about 2.5:1 against
   its own background, which is below the 4.5:1 that small text needs and is
   exactly why he could not read it.

   Fixed HERE, on the global rule, rather than only on .qe-table: every grid in
   the program had the same faint heading, so fixing the one he photographed
   would have left thirty others to be reported one at a time. Full ink and
   700, which is what "bold" asks for. */
thead th {
  text-align: left;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink);
  font-weight: 700;
  padding: 12px 16px;
  border-bottom: 1px solid var(--border);
  background: #FCFBF8;
}

tbody td {
  padding: 13px 16px;
  border-bottom: 1px solid var(--border);
  vertical-align: top;
}

tbody tr:last-child td { border-bottom: none; }

tbody tr {
  cursor: pointer;
  transition: background 0.1s ease;
}
tbody tr:hover { background: #FBF7EE; }

/* r296: a list whose rows open nothing (the Documents archive) must not offer
   the hand -- the row is not a door. Its own links keep theirs. */
#data-table.no-row-open tbody tr { cursor: default; }
#data-table.no-row-open tbody tr a,
#data-table.no-row-open tbody tr button { cursor: pointer; }

.cell-primary { font-weight: 600; color: var(--ink); }
.cell-muted { color: var(--ink-muted); }
/* r251c: a cell that is telling you something needs doing, on a list where
   most cells are not. Amber and not red -- an open claim is late, not broken. */
.cell-warn { color: #8A5B00; font-weight: 600; }
.cell-danger { color: var(--danger); font-weight: 600; }
.cell-tags { display: flex; flex-wrap: wrap; gap: 5px; }

/* !! r279 -- `[hidden]` HAS TO BEAT `display: inline-block`, AND IT DID NOT.
   r279 put a hidden QUICK QUOTE badge in the order header with class `tag`,
   and `check-hidden` went red on the first battery run: a `display` rule
   beats the `hidden` attribute, so the badge would have shown on every
   order whether or not it was a quick quote. This is r261's specificity
   lesson on a different property, and the guard existed for exactly it. */
.tag[hidden] { display: none; }
.tag {
  display: inline-block;
  font-size: 11px;
  background: var(--accent-soft);
  color: #8A5A17;
  padding: 2px 8px;
  border-radius: 999px;
  white-space: nowrap;
}

.empty-state {
  text-align: center;
  padding: 80px 20px;
  color: var(--ink-muted);
}
#empty-title { font-family: var(--font-display); font-size: 18px; color: var(--ink); margin: 0 0 6px 0; }
#empty-sub { margin: 0; font-size: 13px; }

/* ---------- Slide-over panel ---------- */
.overlay {
  position: fixed; inset: 0;
  background: rgba(27, 33, 39, 0.32);
  z-index: 20;
}

.panel {
  position: fixed;
  top: 0; right: 0; bottom: 0;
  width: 420px;
  max-width: 92vw;
  background: var(--surface);
  box-shadow: var(--shadow);
  z-index: 21;
  display: flex;
  flex-direction: column;
  animation: slide-in 0.18s ease-out;
}

/* A bare .panel{display:flex} has equal specificity to the browser's
   built-in [hidden]{display:none}, and source order would let it win --
   this rule makes sure setting the `hidden` attribute always actually hides it. */
.panel[hidden] {
  display: none;
}

@keyframes slide-in {
  from { transform: translateX(24px); opacity: 0; }
  to { transform: translateX(0); opacity: 1; }
}

.panel-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 20px 22px;
  border-bottom: 1px solid var(--border);
}
.panel-head h2 { font-family: var(--font-display); font-size: 19px; margin: 0; }

.icon-btn {
  background: none; border: none; font-size: 20px; line-height: 1;
  color: var(--ink-muted); cursor: pointer; padding: 4px;
}
.icon-btn:hover { color: var(--ink); }

.panel-form {
  flex: 1;
  overflow-y: auto;
  padding: 20px 22px;
  display: flex;
  flex-direction: column;
  gap: 16px;
}

.field { display: flex; flex-direction: column; gap: 2px; }
.field[hidden] { display: none; }
.field label {
  font-size: 11.5px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--ink-muted);
}
.field input, .field select, .field textarea {
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 6px 9px;
  outline: none;
  background: var(--surface);
}
.field input:focus, .field select:focus, .field textarea:focus { border-color: var(--primary-light); }
.field textarea { resize: vertical; min-height: 64px; }
.field input:disabled { background: #F3F1EB; color: var(--ink-muted); cursor: not-allowed; }

.field-row { display: flex; gap: 12px; }
.field-row .field { flex: 1; }

.checkbox-grid {
  display: flex;
  flex-direction: column;
  gap: 8px;
  max-height: 160px;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
}
.checkbox-row {
  display: flex;
  align-items: center;
  gap: 8px;
  font-size: 13px;
  /* ROUND 105 [stated 1.4]: "Clicking outside of the tick mark... clicks it
     anyway - fix this... It should only click if that box is clicked, no
     where else on the screen." A <label> is clickable across its whole box,
     and a full-width flex label means the empty space beside the words
     toggles the tick. Shrink the label to exactly its tick and its words. */
  width: fit-content;
  max-width: 100%;
  align-self: flex-start;
}
.checkbox-empty { color: var(--ink-faint); font-size: 12.5px; font-style: italic; }

.panel-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 22px;
  border-top: 1px solid var(--border);
}
.panel-foot-right { display: flex; gap: 10px; margin-left: auto; }

/* ---------- Quote editor (full-screen, replaces .main) ---------- */
.quote-editor {
  flex: 1;
  min-width: 0; /* flex items default to min-width:auto, which lets the 980px-wide
                   table below force this whole box (and the page) to overflow
                   horizontally instead of scrolling inside .qe-table-wrap */
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  /* r231: padding comes from the MODULE FRAME rule near .main. */
}

.qe-topbar {
  display: flex;
  align-items: center;
  gap: 20px;
  margin-bottom: 20px;
}

.qe-heading { flex: 1; }
.qe-heading h1 {
  font-family: var(--font-display);
  font-size: 22px;
  margin: 0;
}

.qe-actions { display: flex; align-items: center; gap: 12px; }
.qe-unsaved {
  font-size: 12px;
  color: var(--danger);
  background: var(--danger-soft);
  padding: 4px 10px;
  border-radius: 999px;
}

/* r224: the autosave indicator on the submittal screen. Quiet when it is
   working, loud only when a write actually failed. */
.sub-save-state {
  font-size: 12px;
  padding: 4px 10px;
  border-radius: 999px;
  white-space: nowrap;
  font-variant-numeric: tabular-nums;
}
.sub-save-state.is-saving { color: var(--ink-muted); background: #F1F3F7; }
.sub-save-state.is-saved  { color: #2e7d32; background: #EAF3EB; }
.sub-save-state.is-error  { color: var(--danger); background: var(--danger-soft); font-weight: 600; }

.settings-confirm {
  font-size: 12px;
  color: #24793E;
  background: #E3F3E8;
  padding: 4px 10px;
  border-radius: 999px;
}

.qe-fields {
  display: grid;
  /* ROUND 77 density pass: 12px between every field, repeated down a long
     form, was most of the scrolling. */
  gap: 8px 16px;
  margin-bottom: 8px;
}
.qe-fields-title { grid-template-columns: 1fr; }
.qe-fields-row-2 { grid-template-columns: 2fr 1fr !important; }
/* Fill the width available rather than stopping at four columns. */
/* !! r231 -- THESE TWO CLASSES SET COLUMNS AND NEVER SET `display: grid`.
   [stated] "Warranty claim -- clean this all up. I HATE rows being the full
   length of the page." / "Do the same with service calls... This is all full
   extended rows as well."

   That is this line. `.qe-fields` carries the `display: grid` (see above), and
   TWENTY-EIGHT blocks across nine editors are written
   `class="qe-fields-row settings-grid"` with no `qe-fields` on them -- so
   `grid-template-columns` applied to a plain block box, which ignores it, and
   every field inside stacked at the full width of the screen. The r52 density
   pass capped a `.field` standing ALONE at 340px, which is why the problem
   looked confined to a few screens: a lone field was capped, a field inside
   one of these pretend-grids was not.

   Fixed here, once, rather than by adding a class to 28 divs -- a class list
   that has to be right in 28 places is the same maintenance shape as the
   module gutter this round also collapsed. Warranty and Service are the two he
   named; Problems, RMAs, O&M, Register, RFIs, Transmittals and the Settings
   panels were all doing it too. */
.qe-fields-row, .settings-grid { display: grid; }
.qe-fields-row { grid-template-columns: repeat(auto-fit, minmax(180px, 1fr)); }
.qe-fields-row-5 { grid-template-columns: repeat(auto-fit, minmax(155px, 1fr)); }
/* !! r283 -- `qe-fields-row-4` WAS USED ON TWO PANELS AND DEFINED NOWHERE.
   Found by rendering the refund panel and LOOKING at it: the amount box ran
   the whole width of the page and the two buttons sat one above the other.
   `.qe-fields` carries `display: grid` and nothing set the columns, so both
   panels have been a single stacked column since they were written -- which is
   the very thing he asked to be cleaned up in r231 ("I HATE rows being the
   full length of the page"). The class was a name that styled nothing. One
   rule, so the card form and the refund panel are fixed by the same line
   rather than by two. */
.qe-fields-row-4 { grid-template-columns: repeat(auto-fit, minmax(175px, 1fr)); }

.qe-notes-field { margin-bottom: 10px; }
.qe-notes-field textarea { width: 100%; min-height: 40px; }

.qe-lines-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: 8px;
}
.qe-lines-head h2 {
  font-family: var(--font-display);
  font-size: 17px;
  margin: 0;
}
.qe-lines-actions { display: flex; gap: 8px; }

.qe-table-wrap {
  overflow-x: auto;
  overflow-y: auto;
  max-height: 46vh;
  /* This sits inside a column flex container (.quote-editor). Flex children
     shrink by default, so as the editor gained more sections above/below it
     the wrap got squeezed far below its content height -- the table then
     rendered outside its own clipped box, landing under the totals bar
     where its rows couldn't be clicked at all. It should keep its natural
     height (capped at max-height) and scroll internally instead. */
  flex-shrink: 0;
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
}

.qe-table {
  width: 100%;
  border-collapse: separate;
  border-spacing: 0;
  min-width: 980px;
  overflow: visible; /* overrides the generic `table { overflow: hidden }`
    rule elsewhere -- that hidden overflow on the table itself was clipping
    the sticky header's positioning context, breaking it silently. */
}

.qe-table thead th {
  text-align: left;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  /* r302 [his 2]: this rule overrides the global `thead th`, so the grid he
     photographed needed the same colour and weight set again here. */
  color: var(--ink);
  font-weight: 700;
  padding: 10px 10px;
  border-bottom: 1px solid var(--border);
  background: #FCFBF8;
  white-space: nowrap;
  position: sticky;
  top: 0;
  z-index: 2;
}

.qe-table td {
  padding: 6px 8px;
  border-bottom: 1px solid var(--border);
  vertical-align: middle;
}

.qe-table tbody tr:last-child td { border-bottom: none; }
.qe-table tbody tr:hover { background: #FBF9F4; }
/* Selected row: tinted fill PLUS a left accent bar, so a selected row is
   unmistakable at a glance rather than a subtle shade difference. */
.qe-table tbody tr.qe-row-selected > td { background: #EFEAE0; }
.qe-table tbody tr.qe-row-selected:hover > td { background: #E9E2D3; }
.qe-table tbody tr.qe-row-selected > td:first-child { box-shadow: inset 3px 0 0 0 var(--primary); }

/* Selection checkbox + row-number gutter */
.qe-table th.col-sel, .qe-table td.qe-sel-cell { width: 30px; text-align: center; padding-left: 4px; padding-right: 2px; }
.qe-table th.col-rownum, .qe-table td.qe-rownum-cell {
  width: 30px; text-align: right; padding-right: 8px;
  font-size: 11px; color: var(--ink-muted); font-variant-numeric: tabular-nums; user-select: none;
}
.qe-table td.qe-sel-cell input[type="checkbox"] { margin: 0; cursor: pointer; }

.btn-sm { padding: 5px 10px; font-size: 12px; }
.btn-sm:disabled { opacity: 0.4; cursor: default; }

.qe-line-toolbar {
  display: flex;
  align-items: center;
  gap: 6px;
  margin: 8px 0 2px;
  min-height: 26px;
}
.qe-line-toolbar .qe-selected-hint {
  font-size: 11.5px;
  color: var(--ink-muted);
  margin-right: 4px;
}

.qe-table input, .qe-table select {
  width: 100%;
  border: 1px solid transparent;
  background: transparent;
  border-radius: 4px;
  padding: 6px 7px;
  font-size: 13px;
}
.qe-table input:hover, .qe-table select:hover { border-color: var(--border); }
.qe-table input:focus, .qe-table select:focus {
  border-color: var(--primary-light);
  background: var(--surface);
  outline: none;
}

.col-type { width: 64px; }
.col-qty { width: 60px; }
.col-money { width: 100px; }
.col-pct { width: 84px; }
.col-lead { width: 100px; }
.col-del { width: 36px; }

.qe-table input[type="number"] { text-align: right; font-variant-numeric: tabular-nums; }
.qe-ext-cell {
  text-align: right;
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  padding-right: 12px !important;
  color: var(--ink);
}

.qe-row-note td {
  background: var(--accent-soft);
}
.qe-row-note input[type="text"] {
  font-style: italic;
  color: #7A5417;
}

.row-del-btn {
  background: none;
  border: none;
  color: var(--ink-faint);
  cursor: pointer;
  font-size: 15px;
  padding: 4px 6px;
  border-radius: 4px;
}
.row-del-btn:hover { background: var(--danger-soft); color: var(--danger); }

.qe-totals {
  display: flex;
  gap: 28px;
  justify-content: flex-end;
  margin-top: 18px;
  padding: 16px 20px;
  background: var(--primary);
  border-radius: var(--radius);
  color: #fff;
}
.qe-total { display: flex; flex-direction: column; gap: 3px; text-align: right; }
/* !! r265 -- AND IT HAS TO BE ABLE TO HIDE. `[hidden]` is a UA rule at
   specificity zero and `.qe-total` sets `display: flex`, so a totals row with
   the hidden attribute on it STAYS ON SCREEN -- which is how the new "In
   transit" row appeared, reading $0.00, on every purchase order that had not
   shipped. Caught by this round's own browser probe on its first run, and it
   is the r262 finding in a new place (the company-signature box, "found by
   check-hidden, which had never run"). Ten rules in this file already carry
   this fix; this is the eleventh. */
.qe-total[hidden] { display: none !important; }
.qe-total-label {
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: #97A1B1;
}
.qe-total span:last-child {
  font-family: var(--font-mono);
  font-size: 17px;
  font-variant-numeric: tabular-nums;
}
.qe-total-earn span:last-child { color: var(--accent); }

.status-pill {
  display: inline-block;
  font-size: 11px;
  padding: 2px 9px;
  border-radius: 999px;
  background: #EEF1F5;
  color: var(--ink-muted);
}
/* ROUND 213 furniture: the section heading inside an export window, the
   "where it went" block on a sent transmittal, and a warning line. Defined
   rather than assumed -- a class name invented in markup and never written
   here renders as nothing and looks like a layout bug. */
.export-step-head {
  margin: 16px 0 6px; font-size: 10.5px; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--ink-muted);
  border-bottom: 1px solid var(--border, #D8D4C8); padding-bottom: 4px;
}
/* ROUND 213 [his 2]: the cut-sheet search result, with the found sheet
   previewed in place so it can be checked before it is attached. */
.sheet-result-dialog { max-width: 860px; width: 92vw; }
.sheet-result-frame {
  width: 100%; height: 46vh; min-height: 260px; margin-top: 12px;
  border: 1px solid var(--border, #D8D4C8); border-radius: 4px; background: #fff;
}
.sheet-result-frame[hidden] { display: none !important; }

/* ================= ROUND 214 [his 3] — THE SUBMITTAL FORM ==================
   "only the line items should be full width, too much wasted space. The notes
   and disclaimer should be next to each other half screen each longer.
   Everything else could practically be on the same row as nothing needs to be
   full width."

   Four columns. The spec section and the signing block take two each and share
   a row; revision history spans the width because its rows are records, not
   inputs; Notes and Disclaimer take two each and are seven rows tall instead of
   two, which is the "longer" half of that sentence. The line-item grid below is
   untouched and stays full width, which is the one thing that needs it.
   Scoped to .sub-form so no other screen's field spacing moves. */
.sub-form {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 10px 16px;
  align-items: start;
  margin-bottom: 16px;
}
.sub-form .field { margin: 0; }
.sub-form .field > label { display: block; margin-bottom: 3px; }
.sub-form .field textarea { width: 100%; resize: vertical; }
.sub-inline-field { display: flex; gap: 8px; align-items: center; }
.sub-inline-field input { flex: 1; }
.sub-span-2 { grid-column: span 2; }
.sub-span-4 { grid-column: 1 / -1; }
.sub-span-2[hidden], .sub-span-4[hidden] { display: none !important; }
@media (max-width: 1000px) {
  .sub-form { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

.tr-sent-detail { margin-top: 8px; }

/* ================= ROUND 214 [his 2 + 4] — THE TRANSMITTAL FORM =============
   "the transmittal page takes up way too much space with full width sections"
   and "we need to tighten up all spaces between the lines to gain more space."

   Every field on this screen was its own full-width row: eight inputs and
   three textareas stacked down a 1,400px-wide page, each one line of content
   with a whole row to itself. This is a four-column grid with tight gutters;
   only the two long textareas and the purpose ticks get any width at all, and
   nothing is full-bleed. The rest of the app's field spacing is untouched --
   these rules are scoped to .tr-form and cannot leak. */
.tr-form {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 8px 14px;
  align-items: start;
  margin-bottom: 14px;
}
.tr-form .field { margin: 0; }
/* The label sat on its own line with generous leading above and below. Tight
   here, and only here. */
.tr-form .field > label {
  display: block; margin-bottom: 2px;
  font-size: 10.5px; letter-spacing: 0.05em;
}
.tr-form .field input,
.tr-form .field select,
.tr-form .field textarea { width: 100%; padding-top: 5px; padding-bottom: 5px; }
.tr-form .field textarea { resize: vertical; }
.tr-span-2 { grid-column: span 2; }
.tr-span-all { grid-column: 1 / -1; }
.tr-span-all[hidden], .tr-span-2[hidden] { display: none !important; }
/* The purpose ticks ran three to a row across the full width, so each label
   wrapped onto three lines ("FOR / REVIEW AND / APPROVAL" in his screenshot).
   Auto-fit gives them the width they need and stops the wrapping. */
.tr-purpose-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(215px, 1fr));
  gap: 2px 16px;
}
.tr-purpose-grid[hidden] { display: none !important; }
.tr-purpose-grid label { white-space: nowrap; }
/* r291 E2: `.tr-form .field label` (block) and `.tr-form .field input`
   (width: 100%) reached these ticks too, so each box floated half a column
   away from its words and read as belonging to the purpose beside it. */
.tr-form .tr-purpose-grid label { display: flex; align-items: center; gap: 8px; margin: 0; }
.tr-form .tr-purpose-grid input[type="checkbox"] { width: auto; flex: 0 0 auto; margin: 0; }
/* [his 2] the Sent record sits at the top of the page now, so it needs the
   separation from the form that being at the bottom used to give it. */
.tr-sent-top { margin-bottom: 18px; }
.tr-sent-top[hidden] { display: none !important; }
@media (max-width: 900px) {
  .tr-form { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .tr-span-2 { grid-column: span 2; }
}

/* A class of its own rather than a descendant rule: check-hidden reads
   selectors, and `.tr-sent-detail .x { display: grid }` reads to it as
   ".tr-sent-detail is a grid", which would demand a [hidden] rule for an
   element that is never hidden. Its caution is right; the fix is to stop
   writing selectors that can be misread. */
.fact-grid { display: grid; grid-template-columns: 1fr; gap: 2px; }
.fact-grid[hidden] { display: none !important; }
.fact-row { display: flex; gap: 12px; align-items: baseline; }
.fact-label {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-muted); min-width: 88px; flex-shrink: 0;
}
.fact-value { font-size: 12.5px; word-break: break-word; }
.warn-p { color: var(--danger, #A3372A); font-size: 12px; }

/* ROUND 213: the pill became HIDEABLE this round -- the submittal and
   transmittal screens swap it for a status PICKER and hide whichever of the
   two does not apply. A `display` rule beats the `hidden` attribute, so
   without this both would show at once. check-hidden caught it. */
.status-pill[hidden] { display: none !important; }
/* r247: the revision pill sits beside the job's own status pill and has to be
   readable as a DIFFERENT kind of fact -- the job status is where the work is,
   this is what the client is holding. Outlined rather than filled, so the two
   do not read as a pair of equal states. */
.design-rev-pill {
  background: transparent;
  border: 1px solid var(--border, #D8D4C8);
  color: var(--ink, #1C2127);
}
/* The picker wears the pill's own shape so the header does not change size
   when a status becomes editable. */
select.status-select {
  border: 1px solid var(--border, #D8D4C8);
  cursor: pointer;
  padding-right: 22px;
  font-family: inherit;
  max-width: 190px;
}
select.status-select:disabled { cursor: default; opacity: 0.75; }
.status-pill.status-won { background: #E3F3E8; color: #24793E; }
.status-pill.status-lost { background: var(--danger-soft); color: var(--danger); }
.status-pill.status-sent { background: var(--accent-soft); color: #8A5A17; }
.status-pill.status-signed { background: #E3F3E8; color: #24793E; }
/* r291 [his item 5]: "the order was paid for- this should turn the next color
   ready to be picked up or shipped out". The same orange the What's-next chip
   uses for "able to be done next" (.next-step.now), so the pill and the chip
   read as one statement about the order. */
.status-pill.status-ready { background: #FFF1DC; color: #8A5200; }
.status-pill.status-draft { background: #EFEAE0; color: var(--ink-muted); }
.status-pill.status-superseded { background: #EFEAE0; color: var(--ink-faint); }
.status-pill.status-pending { background: #EFEAE0; color: var(--ink-muted); }
.status-pill.status-approved { background: #E3F3E8; color: #24793E; }
.status-pill.status-needs-revision { background: var(--danger-soft); color: var(--danger); }
/* r227 [stated]: an issued invoice is OPEN. It wears the same amber the
   transmittal's Sent pill wears -- both mean "it has left the building and is
   waiting on somebody else". */
.status-pill.status-open { background: var(--accent-soft); color: #8A5A17; }

/* ---------- r227 [stated]: when the client opened this invoice ---------- */
/* "There should also be a record of when that invoice was opened by the
   client, each time. With view counts." A quiet line by default -- it is
   evidence, not an alert -- that opens into the individual opens. */
.inv-opens { margin: 0 0 14px; }
.inv-opens-summary {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; font-size: 12px; color: var(--ink-muted); text-align: left;
  border-bottom: 1px dotted var(--border, #D8D4C8);
}
.inv-opens-summary:hover { color: var(--ink); }
.inv-opens-summary .inv-opens-count { font-weight: 600; color: var(--ink); }
.inv-opens-list {
  margin-top: 8px; padding: 8px 12px; max-width: 460px;
  border: 1px solid var(--border, #D8D4C8); border-radius: 6px;
  background: var(--surface-2, #FAF9F5);
}
.inv-opens-row { display: flex; justify-content: space-between; gap: 16px; font-size: 12px; padding: 2px 0; }
.inv-opens-row + .inv-opens-row { border-top: 1px solid var(--border, #D8D4C8); }
.inv-opens-who { color: var(--ink-muted); }

/* r227: a one-line explanation under a picker whose effect is not obvious
   from its label -- the parent company decides whose terms print. */
.field-hint { margin: 4px 0 0; font-size: 11.5px; color: var(--ink-muted); line-height: 1.4; }

/* ---------- r227 [his item 4]: the transmittal line on a submittal ---------- */
/* [stated] "Make the transmittal number and date and sent marker smaller in
   width. It should take up the entire screen, that's ridiculous."
   (He means it SHOULDN'T -- the screenshot shows TR-1 / Sent 09/02/2026 /
   Sent stretched the full width of a 2000px window with the pill marooned at
   the far right.)

   The cause is that this reuses the quote screen's PO list, where a row IS a
   full-width record with a total right-aligned at the end. Here it is one
   label naming one transmittal, so it is sized to its own content. Scoped to
   this section on purpose -- the PO and project lists that share these class
   names are full-width rows for a good reason and are left alone. */
#sub-transmittal-section { align-items: flex-start; }
#sub-transmittal-section .qe-po-row {
  display: inline-flex;
  width: auto;
  max-width: 100%;
  gap: 12px;
  padding: 7px 12px;
}
/* The date stops being the elastic middle that pushes the pill away. */
#sub-transmittal-section .qe-po-mfg { flex: 0 0 auto; }
#sub-transmittal-section .qe-po-num { min-width: 0; }

/* r227 [his items 3 + 5]: this screen's top bar now carries TWO back buttons
   and two more action buttons, and `.qe-heading { flex: 1 }` was letting the
   title be the only thing that gave way -- "SUB-001 · rev 0" wrapped to four
   lines while the buttons stayed at their natural width. The buttons stop
   shrinking; the heading keeps the room it needs and the breadcrumb is what
   truncates, since it is the one piece that reads fine cut short. */
#submittal-editor .qe-topbar { gap: 14px; flex-wrap: wrap; }
#submittal-editor .qe-topbar > .btn { flex: 0 0 auto; white-space: nowrap; }
#submittal-editor .qe-heading { flex: 1 1 240px; min-width: 200px; }
#submittal-editor .qe-heading h1 { white-space: nowrap; }
#submittal-editor .qe-heading .view-sub {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
#submittal-editor .qe-actions { flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; }
/* r291 E2 (r290's leftover): the change order's bar squeezed "CO-01" and its
   breadcrumb into a sliver at 1400px -- "Client PO HB-PO- / 7781" broke across
   lines. The heading keeps room for the breadcrumb on one line; the buttons
   move to a row of their own when the two do not fit side by side. */
#co-editor .qe-topbar { gap: 14px; flex-wrap: wrap; row-gap: 8px; }
#co-editor .qe-topbar > .btn { flex: 0 0 auto; white-space: nowrap; }
#co-editor .qe-heading { flex: 1 1 460px; min-width: 300px; }
#co-editor .qe-heading h1 { white-space: nowrap; }
#co-editor .qe-actions { flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; margin-left: auto; }
/* r291 E1-D3: the ORDER's bar at 1400px. The moment "Unsaved changes" joined
   the buttons, the heading gave way and "+ Customer Details" / "+ Project
   Details" stacked on two lines under a breadcrumb squeezed beside them. The
   heading keeps its room; the detail buttons stay on one line. */
#order-editor .qe-topbar { gap: 14px; flex-wrap: wrap; row-gap: 8px; }
#order-editor .qe-topbar > .btn { flex: 0 0 auto; white-space: nowrap; }
#order-editor .qe-heading { flex: 1 1 300px; min-width: 300px; }
#order-editor .order-detail-links > .btn { white-space: nowrap; }
#order-editor .qe-actions { flex: 0 0 auto; flex-wrap: wrap; justify-content: flex-end; margin-left: auto; }
#order-editor .qe-actions > * { white-space: nowrap; }

/* ---------- Lots (quote editor) ---------- */
.qe-lots-section { margin-bottom: 22px; }
.qe-lots-hint {
  font-size: 11.5px;
  font-weight: 400;
  color: var(--ink-muted);
  text-transform: none;
  letter-spacing: 0;
  margin-left: 8px;
}
.qe-lots-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.qe-lot-row {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--accent-soft);
  border: 1px solid #EAD3A0;
  border-radius: 6px;
  padding: 8px 12px;
}
.qe-lot-row input[type="text"] {
  flex: 1;
  border: 1px solid transparent;
  background: #fff;
  border-radius: 4px;
  padding: 6px 9px;
  font-weight: 600;
}
.qe-lot-row .qe-lot-price {
  width: 130px;
  border: 1px solid transparent;
  background: #fff;
  border-radius: 4px;
  padding: 6px 9px;
  text-align: right;
  font-variant-numeric: tabular-nums;
}
.qe-lot-row .qe-lot-member-count {
  font-size: 11.5px;
  color: #8A5A17;
  white-space: nowrap;
}
.qe-lots-empty {
  font-size: 12.5px;
  color: var(--ink-faint);
  font-style: italic;
  padding: 4px 2px;
}
.col-lot { width: 110px; }
.col-linenote { width: 170px; }
/* ROUND 98: one grip per row instead of two arrow buttons -- drag and drop. */
.col-move { width: 30px; }
.qe-move-cell { white-space: nowrap; text-align: center; }
.row-drag-grip {
  display: inline-block;
  color: var(--ink-faint);
  cursor: grab;
  font-size: 13px;
  line-height: 1;
  padding: 2px 4px;
  user-select: none;
  touch-action: none;
}
.row-drag-grip:hover { color: var(--primary); }
tr.row-dragging { opacity: 0.45; }
tr.row-dragging .row-drag-grip { cursor: grabbing; }
/* The landing line: where the dragged row will be inserted. */
tr.row-drop-before td { box-shadow: inset 0 2px 0 var(--primary); }
tr.row-drop-end td { box-shadow: inset 0 -2px 0 var(--primary); }
/* No accidental text selection mid-drag, anywhere on the page. */
body.drag-no-select, body.drag-no-select * { user-select: none !important; }
/* Header drag: the column being moved and where it will land. */
th.col-dragging { opacity: 0.55; cursor: grabbing; }
th.col-drop-before { box-shadow: inset 2px 0 0 var(--primary); }
th.col-drop-end { box-shadow: inset -2px 0 0 var(--primary); }
.qe-table th[data-col] { cursor: grab; }
.col-status { width: 150px; }
.col-reviewer-notes { width: 220px; }
.approval-select { width: 100%; font-weight: 500; }
.approval-select.status-pending { color: var(--ink-muted); }
.approval-select.status-approved { color: #24793E; }
.approval-select.status-needs-revision { color: #B23B2E; }
.sub-item-readonly { color: var(--ink); }

/* ---------- Purchase Orders summary (inside the Quote editor) ---------- */
.qe-po-section { margin-top: 26px; }
.qe-po-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.qe-po-row {
  display: flex;
  align-items: center;
  gap: 14px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 14px;
}
.qe-po-row.qe-po-existing { cursor: pointer; }
.qe-po-row.qe-po-existing:hover { background: #FBF9F4; }
.qe-po-num { font-weight: 600; font-size: 13px; min-width: 110px; }
.qe-po-mfg { flex: 1; font-size: 13px; color: var(--ink-muted); }
/* r269: what a receipt was worth, on the receipt row. Right-aligned and
   tabular so a column of them reads as money rather than as text. */
.qe-po-amt { flex: 0 0 auto; font-size: 13px; font-variant-numeric: tabular-nums;
             text-align: right; min-width: 92px; }
.qe-po-total { font-variant-numeric: tabular-nums; font-size: 13px; font-weight: 600; }
.qe-po-create-btn {
  border: 1px dashed var(--border);
  background: transparent;
  border-radius: 6px;
  padding: 9px 14px;
  font-size: 13px;
  color: var(--ink-muted);
  cursor: pointer;
  text-align: left;
}
.qe-po-create-btn:hover { border-color: var(--primary-light); color: var(--ink); }
.qe-po-empty { font-size: 12.5px; color: var(--ink-faint); font-style: italic; padding: 4px 2px; }
/* r268: a section the account may not read. It must NOT look like "there are
   none" -- that is the whole reason this class exists rather than reusing the
   empty note on its own. Not italic, and it carries the warning colour. */
.qe-po-denied { color: var(--warn, #9a6b00); font-style: normal; font-weight: 500; }
.qe-po-denied[hidden] { display: none !important; }

/* ---------- Order editor ---------- */
.order-locked-banner {
  background: var(--danger-soft);
  color: var(--danger);
  border-radius: 6px;
  padding: 10px 14px;
  font-size: 13px;
  margin-bottom: 18px;
}
/* r290: the "changed somewhere else" line -- news, not a warning. */
.order-locked-banner.order-live-note { background: #EAF3EC; color: #1F5F33; }
.order-live-note[hidden] { display: none; }
.order-live-note .btn { margin-left: 8px; }
/* r238: the counter sale's banner has two meanings now, and only one of them
   is a warning. Signed with the goods gone and nothing billed keeps the red
   above; a sale that has been invoiced is finished business and goes quiet.
   Same rule as r206's credit banner -- a red box that says "everything is
   fine" is how a warning colour stops being read at all. */
.order-locked-banner.is-done {
  background: #F2F5F8;
  color: var(--ink);
  border: 1px solid var(--border);
}
.order-divergence-banner {
  background: var(--accent-soft);
  color: #8A5A17;
  border-radius: 6px;
  padding: 10px 14px;
  font-size: 13px;
  margin-bottom: 18px;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
}
.order-divergence-banner[hidden] { display: none; }
.order-divergence-banner strong { display: block; }
.order-divergence-banner span { color: #8A5A17; opacity: 0.9; }
.order-waiver-banner {
  background: var(--accent-soft);
  color: #8A5A17;
  border-radius: 6px;
  padding: 10px 14px;
  font-size: 13px;
  line-height: 1.5;
  margin-bottom: 18px;
}
.order-submittal-ref {
  font-size: 12.5px;
  color: var(--ink-muted);
  margin-bottom: 18px;
}
.order-sign-section {
  margin-top: 26px;
  padding-top: 20px;
  border-top: 1px solid var(--border);
  max-width: 700px;
}
.sig-pad-wrap {
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #fff;
  margin: 10px 0 12px 0;
  padding: 4px;
}
#sig-canvas {
  display: block;
  width: 100%;
  height: 160px;
  cursor: crosshair;
  touch-action: none;
}
.sig-pad-actions { display: flex; justify-content: flex-end; padding: 4px 4px 0 4px; }
.order-signed-block { margin-top: 26px; padding-top: 20px; border-top: 1px solid var(--border); max-width: 500px; }
.order-signed-block img { max-width: 320px; border: 1px solid var(--border); border-radius: 6px; background: #fff; display: block; }

/* ---------- Settings screen (full-screen, same slot pattern as .quote-editor) ---------- */
.settings-screen {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow-y: auto;
  /* r231: padding comes from the MODULE FRAME rule near .main. */
}

.settings-section {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 22px 24px;
  margin-bottom: 20px;
  max-width: 760px;
}
.settings-section h2 {
  font-family: var(--font-display);
  font-size: 16px;
  margin: 0 0 4px 0;
}
.settings-sub {
  margin: 0 0 16px 0;
  font-size: 12.5px;
  color: var(--ink-muted);
}
/* r316: the test copy's own section -- a copy that could not be taken says so in red. */
.test-site-section { border-left: 4px solid #C62828; padding-left: 12px; }
.test-site-failed { color: #B42318; font-weight: 600; }
.settings-grid { grid-template-columns: 1fr 1fr; }
.settings-section textarea {
  width: 100%;
  min-height: 190px;
  font-family: var(--font-mono);
  font-size: 12.5px;
  line-height: 1.6;
}

.settings-tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-bottom: 14px;
}
.settings-tag {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  background: var(--accent-soft);
  color: #8A5A17;
  padding: 5px 8px 5px 12px;
  border-radius: 999px;
  font-size: 12.5px;
}
.settings-tag button {
  background: none;
  border: none;
  color: #8A5A17;
  cursor: pointer;
  font-size: 14px;
  line-height: 1;
  padding: 0 2px;
  opacity: 0.7;
}
.settings-tag button:hover { opacity: 1; }
.settings-add-row {
  display: flex;
  gap: 8px;
  max-width: 360px;
}
.settings-add-row input {
  flex: 1;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
}

/* ---------- Customer editor ---------- */
.qe-fields-row-3 { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 12px; margin-top: 10px; }
.cust-address-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 20px;
  margin-bottom: 22px;
}
.cust-address-block {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 16px 18px;
}
.cust-address-block h3 {
  margin: 0 0 12px 0;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
}
.cust-contacts-list {
  display: flex;
  flex-direction: column;
  gap: 8px;
  margin-bottom: 22px;
}
/* r265 [his ask]: the duplicate-name warning on the CRM. Amber, not red: it is
   a WARNING and two customers really can share a name -- two Smith Electrics in
   two counties -- so it says what it found and lets him decide. */
.cust-dup-field[hidden] { display: none !important; }
.cust-dup-warn {
  background: var(--warn-soft, #fdf6e3);
  border: 1px solid var(--warn, #c9a227);
  border-radius: 6px;
  padding: 10px 12px;
  font-size: 13px;
}
.cust-dup-warn strong { font-weight: 600; }
.cust-dup-warn .cust-dup-open {
  background: none;
  border: none;
  padding: 0 0 0 6px;
  color: var(--primary, #2f5bea);
  cursor: pointer;
  font-size: 13px;
  text-decoration: underline;
}
.cust-dup-warn ul { margin: 6px 0 0; padding-left: 18px; }
.cust-dup-warn li { margin: 2px 0; }

/* r265: the empty state is where the whole explanation of the contact model
   lives now -- it used to render only once a contact existed, which is after
   the moment somebody needed it. Bordered so it reads as guidance rather than
   as a row that failed to load. */
.cust-contacts-empty {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 14px 16px;
  margin-bottom: 22px;
  max-width: 900px;
}
.cust-contacts-empty p { margin: 0 0 8px; font-size: 13px; }
.cust-contacts-empty p:last-child { margin-bottom: 0; }
.cust-contacts-empty code {
  font-size: 12px;
  background: var(--bg, #f4f2ee);
  border-radius: 3px;
  padding: 1px 4px;
}
/* An individual with no contact is FLAGGED, not blocked -- his rule is that
   anything can be pushed through as long as it is flagged appropriately, and
   the four hard blocks in this program are a closed list this is not on. */
.cust-contacts-needed {
  background: var(--danger-soft);
  border-color: var(--danger);
}
.cust-contact-row {
  display: grid;
  /* r260: a seventh column at the front for the contact number. Without this
     the number would be squeezed into the Name column's track and the whole
     row would shift left of its header. */
  grid-template-columns: 72px 1.3fr 1.1fr 1.4fr 1.1fr auto auto;
  gap: 8px;
  align-items: center;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 10px;
}
.cust-contact-row input[type="text"] {
  border: 1px solid transparent;
  background: transparent;
  border-radius: 4px;
  padding: 6px 8px;
  font-size: 13px;
  width: 100%;
}
.cust-contact-row input[type="text"]:hover { border-color: var(--border); }
.cust-contact-row input[type="text"]:focus { border-color: var(--primary-light); background: #fff; outline: none; }
.cust-contact-primary {
  display: flex;
  align-items: center;
  gap: 5px;
  font-size: 11px;
  color: var(--ink-muted);
  white-space: nowrap;
}

@media (max-width: 720px) {
  .sidebar { width: 76px; }
  .brand-word, .nav-label, .sidebar-foot { display: none; }
  .nav-item { justify-content: center; }
  .topbar { flex-direction: column; gap: 14px; }
  #search { width: 160px; }
}

/* !! r293 -- THE PHONE BREAKPOINT IS AT THE FOOT OF THIS FILE, NOT HERE.
   It belongs beside this rule and it cannot live here: `.sidebar-toggle`,
   `body.sidebar-collapsed .sidebar` and several others are declared ~850
   lines BELOW this point, at the same specificity, so a phone rule written
   here loses to them and the drawer opens with its own button underneath it.
   Found by driving it at 390px. Search "THE PHONE. HIS WORDS, TWICE." */

/* ---------- Print / Export dialog ---------- */
.export-modal {
  position: fixed;
  inset: 0;
  background: rgba(28, 33, 39, 0.45);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
  padding: 24px;
}
.export-modal[hidden] { display: none; }
.export-dialog {
  background: var(--surface);
  border-radius: var(--radius);
  width: 100%;
  max-width: 640px;
  max-height: 88vh;
  display: flex;
  flex-direction: column;
  box-shadow: 0 18px 48px rgba(28, 33, 39, 0.28);
}
.export-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 16px 20px;
  border-bottom: 1px solid var(--border);
}
.export-head h2 { margin: 0; font-size: 16px; }
.export-step { padding: 18px 20px; overflow-y: auto; }
.export-step[hidden] { display: none; }
.export-step .field { margin-bottom: 16px; }
.export-doctype-list { display: flex; flex-direction: column; gap: 8px; }
.export-radio {
  display: flex;
  align-items: flex-start;
  gap: 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 10px 12px;
  cursor: pointer;
}
.export-radio:hover { border-color: var(--primary-light); }
.export-radio input { margin-top: 3px; }
.export-radio span { display: flex; flex-direction: column; }
.export-radio strong { font-size: 13.5px; }
.export-radio em { font-style: normal; font-size: 12px; color: var(--ink-muted); margin-top: 2px; }
.export-options-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 7px 18px;
}
.export-opt {
  display: flex;
  align-items: center;
  gap: 7px;
  font-size: 12.5px;
  cursor: pointer;
}
.export-opt.export-opt-wide { grid-column: 1 / -1; }
.export-opt-draft { color: var(--danger); font-weight: 600; }
.export-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 20px;
  border-top: 1px solid var(--border);
  margin: 18px -20px -18px -20px;
}
.export-foot > div { display: flex; gap: 8px; }
/* r284 -- FOUND BY LOOKING AT THE EMAIL BOX. The negative margins above pull a
   footer out to the edges of a PADDED .export-step, which is where half the
   dialogs put it. The other half put it straight inside .export-dialog, which
   has no padding -- so their footer ran 20px past both sides of the box, its
   rule sticking out and Cancel adrift. The email dialog, the one he uses most,
   is one of those. A footer that is the dialog's own child sits flush. */
.export-dialog > .export-foot { margin: 0; }

/* ---------- Home screen ---------- */
/* r231: padding comes from the MODULE FRAME rule near .main. */
.home-screen { overflow-y: auto; }
.home-screen[hidden] { display: none; }
.home-section-title {
  font-size: 12px;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-muted);
  margin: 26px 0 12px;
  font-weight: 600;
}
.home-section-title:first-of-type { margin-top: 10px; }
.home-tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 12px;
}
.home-tile {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: var(--radius);
  padding: 14px 16px;
  cursor: pointer;
}
.home-tile:hover { border-color: var(--primary-light); }
.home-tile-value { font-size: 24px; font-weight: 600; line-height: 1.1; }
.home-tile-label { font-size: 12px; color: var(--ink-muted); margin-top: 5px; }
.home-tile.is-alert { border-color: var(--danger); }
.home-tile.is-alert .home-tile-value { color: var(--danger); }
.home-tile.is-zero .home-tile-value { color: var(--ink-faint); }

.home-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 24px; }
.home-list { display: flex; flex-direction: column; gap: 7px; }
.home-list-row {
  display: flex;
  align-items: center;
  gap: 10px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-left: 3px solid var(--danger);
  border-radius: 6px;
  padding: 9px 12px;
  font-size: 12.5px;
  cursor: pointer;
}
.home-list-row:hover { background: #FBF9F4; }
.home-list-row .hl-num { font-weight: 600; min-width: 110px; }
.home-list-row .hl-body { flex: 1; color: var(--ink-muted); }
.home-empty { font-size: 12.5px; color: var(--ink-faint); font-style: italic; padding: 6px 2px; }
.home-margin-table td { font-size: 12.5px; }
.margin-earn-pos { color: #24793E; }
.margin-earn-neg { color: var(--danger); }
.cost-estimated { color: var(--ink-faint); font-size: 11px; }

/* Rows needing attention, across every module list */
#data-table tbody tr.row-alert { background: #FDF3F2; }
#data-table tbody tr.row-alert:hover { background: #FBE9E7; }
#data-table tbody tr.row-alert td:first-child { box-shadow: inset 3px 0 0 var(--danger); }
.cell-alert { color: var(--danger); font-weight: 600; }

/* ---------- Change Order / Invoice / RMA editors ---------- */
.col-action { width: 120px; }
/* ROUND 45 [stated]: "that whole row is cut in half and can't see the bottom
   of those." Labels like "Prior approved changes" wrap to two lines, but
   `overflow: hidden` on the bar clipped anything past the shortest cell.
   Cells now stretch to the tallest, and nothing is clipped. */
.co-totals-bar {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(135px, 1fr));
  grid-auto-rows: 1fr;
  align-items: stretch;
  gap: 1px;
  background: var(--border);
  border: 1px solid var(--border);
  border-radius: 6px;
  margin-bottom: 20px;
}
.co-total-cell {
  background: var(--surface);
  padding: 11px 14px;
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  min-height: 62px;
  overflow: visible;
}
.co-totals-bar > .co-total-cell:first-child { border-radius: 5px 0 0 5px; }
.co-totals-bar > .co-total-cell:last-child { border-radius: 0 5px 5px 0; }
.co-total-cell .cot-label {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--ink-muted); margin-bottom: 4px;
  line-height: 1.35; overflow-wrap: break-word;
}
.co-total-cell .cot-value { font-size: 17px; font-weight: 600; font-variant-numeric: tabular-nums; }
.co-total-cell.cot-delta .cot-value.pos { color: #24793E; }
.co-total-cell.cot-delta .cot-value.neg { color: var(--danger); }
.co-total-cell.cot-new { background: var(--primary); }
.co-total-cell.cot-new .cot-label { color: rgba(255,255,255,0.72); }
.co-total-cell.cot-new .cot-value { color: #fff; }

/* ROUND 100: the remove tint is ONE left border on the row, not a red line on
   every cell -- `td` unqualified painted a bar at every column boundary and
   made the whole grid look broken. */
/* ROUND 101 [stated 2.9]: HIS color code, program-wide: brown = remove,
   blue = modify, green = unchanged, red = add. */
.co-row-add td:first-child { box-shadow: inset 3px 0 0 #C0392B; }
.co-row-modify td:first-child { box-shadow: inset 3px 0 0 #2456A6; }
.co-row-remove td:first-child { box-shadow: inset 3px 0 0 #8B5E34; }
.co-row-unchanged td:first-child { box-shadow: inset 3px 0 0 #24793E; }
/* ROUND 101 [stated 2.3]: a row carrying an INTERNAL note is yellow on every
   form -- "this makes it quickly known that there is an internal note and we
   need to read it." Internal notes never print. */
tr.row-has-internal-note td { background: #FFF7C7 !important; }
/* The two-computations money check under the totals bar. */
.co-reconcile { margin: 8px 0 0; padding: 7px 12px; border-radius: 6px; font-size: 12.5px; }
.co-reconcile-ok { background: #E3F3E8; color: #1A6B33; }
.co-reconcile-bad { background: #FBE5E2; color: #A3372A; font-weight: 600; }
/* What this CO's line actions do to each lot's money. */
.co-lot-impact { padding: 6px 10px; margin-bottom: 6px; border-radius: 6px;
  background: #FBF0DC; font-size: 12.5px; }
.co-lot-impact-out { background: #FBE5E2; }
/* Freight rows: new figure, what it was, and the change. */
/* ROUND 101 [stated 1.2]: the CO freight rows sat inside the ORDER's
   side-by-side auto-fit grid, so every manufacturer's field piled onto one
   line and overlapped clean off the screen. A vertical stack of aligned rows. */
.co-freight-stack { display: block; margin-bottom: 8px; }
.co-freight-field { display: grid; grid-template-columns: 180px 150px 110px 90px;
  gap: 12px; align-items: center; padding: 3px 0; }
.co-freight-field label { margin: 0; font-weight: 600; font-size: 12.5px; }
.co-freight-total-line { margin: 8px 0 0; font-size: 13px; }
/* [stated 1.1] Approved change orders wear it plainly, with the date. */
.co-approved-stamp { display: inline-block; margin-left: 10px; padding: 3px 10px;
  border: 2px solid #24793E; border-radius: 5px; color: #24793E; font-weight: 700;
  font-size: 12px; letter-spacing: 0.05em; text-transform: uppercase; }
.co-was-cell { font-size: 11.5px; color: var(--ink-muted); }
.co-delta-cell { font-variant-numeric: tabular-nums; font-weight: 600; }
.co-delta-cell.pos { color: #24793E; }
.co-delta-cell.neg { color: var(--danger); }

.pay-row {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: 6px; padding: 9px 12px; font-size: 12.5px;
}
.pay-row .pay-amount { font-weight: 600; font-variant-numeric: tabular-nums; min-width: 100px; }
.pay-row .pay-meta { flex: 1; color: var(--ink-muted); }
.pay-row .row-del-btn { margin-left: auto; }
/* r221 [his 3]: the row opens the payment, so it has to LOOK like it opens
   something. The buttons inside it keep the default cursor -- they do their
   own thing and are not the row's click. */
.pay-row-open { cursor: pointer; }
.pay-row-open:hover { border-color: var(--accent, #3355aa); background: var(--surface-2, #f4f6fa); }
.pay-row-open .btn, .pay-row-open .row-del-btn { cursor: pointer; }
.pay-row .pay-number {
  font-variant-numeric: tabular-nums; font-weight: 600; color: var(--ink-muted);
  min-width: 84px;
}
.balance-clear { color: #24793E; font-weight: 600; }
/* r286: a draft, a void or an invoice that bills nothing is neither owing nor settled. */
.balance-muted { color: var(--ink-muted); font-weight: 600; }
.balance-owing { color: var(--danger); font-weight: 600; }

/* ---------- Settings accordion ---------- */
.settings-accordion { display: flex; flex-direction: column; gap: 10px; }
.settings-group {
  border: 1px solid var(--border);
  border-radius: var(--radius);
  background: var(--surface);
  overflow: hidden;
}
.settings-group-header {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 14px 18px;
  background: none;
  border: none;
  font-family: var(--font-display);
  font-size: 15.5px;
  font-weight: 600;
  color: var(--ink);
  cursor: pointer;
  text-align: left;
}
.settings-group-header:hover { background: #FBF9F4; }
.settings-group-chevron {
  font-size: 12px;
  color: var(--ink-faint);
  transition: transform 0.15s ease;
}
.settings-group.is-open .settings-group-chevron { transform: rotate(180deg); }
.settings-group-body {
  display: none;
  padding: 4px 18px 18px 18px;
  border-top: 1px solid var(--border);
}
.settings-group.is-open .settings-group-body { display: block; }
.settings-group-body .settings-section { margin-top: 16px; }
.settings-group-body .settings-section:first-child { margin-top: 18px; }

/* ---------- Centered "+ Add" row ---------- */
.add-btn-row {
  display: flex;
  justify-content: center;
  /* r231: sides come from the MODULE FRAME rule; keeping them here as well
     double-indented the Add button relative to the table under it. */
  padding: 6px 0 18px;
}
.add-btn-row[hidden] { display: none; }
.btn-add-centered {
  padding: 10px 30px;
  font-size: 14px;
  text-align: center;
}

/* ---------- PDF annotation editor ---------- */
.pdf-editor-modal {
  position: fixed; inset: 0; background: rgba(20, 24, 30, 0.7);
  display: flex; align-items: center; justify-content: center;
  z-index: 200; padding: 16px;
}
.pdf-editor-modal[hidden] { display: none; }
.pdf-editor-dialog {
  background: #22262E; border-radius: 8px; width: 100%; height: 100%;
  max-width: 1400px; display: flex; flex-direction: column; overflow: hidden;
}
.pdf-editor-head {
  display: flex; align-items: center; gap: 14px;
  padding: 10px 14px; background: #1A1D23; border-bottom: 1px solid #333;
  flex-wrap: wrap;
}
.pdf-editor-head-left { display: flex; flex-direction: column; color: #E8E4DA; font-size: 13px; min-width: 190px; }
.pdf-editor-page-label { font-size: 11px; color: #9aa0a8; }
/* ROUND 213 [his 3]: "the type and part# should show on top... This should be
   bold and easily visible." The Type is the thing the eye hunts for on a
   family sheet, so it gets the same yellow it gets on the printed cut-sheet
   header -- the reviewer's own landmark, used consistently. */
.pdf-editor-type {
  font-size: 17px; font-weight: 800; line-height: 1.15;
  color: #1C2127; background: #FFEE58;
  padding: 1px 8px; border-radius: 3px; align-self: flex-start;
  letter-spacing: 0.01em;
}
.pdf-editor-type[hidden] { display: none !important; }
.pdf-editor-part {
  font-size: 13.5px; font-weight: 700; color: #FFFFFF;
  letter-spacing: 0.01em; word-break: break-all; margin-top: 2px;
}
.pdf-editor-part[hidden] { display: none !important; }
/* The filename drops to a caption now that the type and part are above it. */
.pdf-editor-filename { font-size: 11px; color: #9aa0a8; }
.pdf-editor-toolbar { display: flex; gap: 4px; flex-wrap: wrap; }
.pdf-tool-btn {
  background: #2C313A; border: 1px solid #3A404B; color: #C4CCD9;
  padding: 6px 10px; border-radius: 5px; font-size: 12px; cursor: pointer;
}
.pdf-tool-btn:hover { background: #363C47; }
.pdf-tool-btn.active { background: var(--primary); border-color: var(--primary); color: #fff; }
.pdf-editor-page-nav { display: flex; gap: 4px; }
.pdf-editor-head-actions { display: flex; gap: 6px; margin-left: auto; }
.pdf-editor-body { flex: 1; display: flex; overflow: hidden; }
.pdf-editor-canvas-wrap {
  flex: 1; position: relative; overflow: auto; background: #3A3F47;
  display: flex; align-items: flex-start; justify-content: center; padding: 20px;
}
#pdf-editor-canvas { background: #fff; box-shadow: 0 2px 12px rgba(0,0,0,0.4); display: block; }
.pdf-editor-overlay {
  position: absolute; top: 20px; left: 50%; transform: translateX(-50%);
  cursor: crosshair;
}
.pdf-mark {
  position: absolute; box-sizing: border-box; cursor: move;
}
.pdf-mark.selected { outline: 2px dashed #4A90E2; outline-offset: 2px; }
.pdf-mark-text {
  white-space: nowrap; padding: 1px 2px; line-height: 1.1;
  font-family: Helvetica, Arial, sans-serif;
}
.pdf-editor-props {
  width: 260px; background: #1A1D23; border-left: 1px solid #333;
  padding: 14px; overflow-y: auto; color: #E8E4DA; font-size: 12.5px;
}
.pdf-editor-props-empty { color: #7A8394; font-style: italic; }
.pdf-prop-row { margin-bottom: 12px; }
.pdf-prop-row label { display: block; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em; color: #9aa0a8; margin-bottom: 4px; }
.pdf-prop-row input[type="text"], .pdf-prop-row input[type="number"], .pdf-prop-row select, .pdf-prop-row textarea {
  width: 100%; background: #2C313A; border: 1px solid #3A404B; color: #E8E4DA;
  border-radius: 4px; padding: 6px 8px; font-size: 12.5px;
}
.pdf-prop-row input[type="color"] { width: 100%; height: 30px; border: 1px solid #3A404B; border-radius: 4px; background: #2C313A; }
.pdf-prop-row input[type="range"] { width: 100%; }
.pdf-prop-inline { display: flex; gap: 8px; }
.pdf-prop-inline > div { flex: 1; }
.pdf-style-toggles { display: flex; gap: 4px; }
.pdf-style-toggles button {
  flex: 1; background: #2C313A; border: 1px solid #3A404B; color: #C4CCD9;
  padding: 6px 0; border-radius: 4px; cursor: pointer; font-size: 12px;
}
.pdf-style-toggles button.active { background: var(--primary); border-color: var(--primary); color: #fff; }

/* ---------- Cut-sheet attach button (submittal line items) ---------- */
.cutsheet-btn {
  border: 1px solid var(--border); background: var(--surface);
  border-radius: 5px; padding: 4px 8px; font-size: 11.5px; cursor: pointer;
  white-space: nowrap;
}
.cutsheet-btn.cutsheet-missing { color: var(--danger); border-color: #F0C4BE; }
.cutsheet-btn.cutsheet-present { color: #1F3FB5; border-color: #C3CCE6; }
.cutsheet-btn.cutsheet-marked { background: #EAF7EE; }

/* ---------- RFI cost/schedule impact fields ---------- */
.checkbox-field { display: flex; align-items: center; gap: 10px; }
.checkbox-field label { display: flex; align-items: center; gap: 6px; font-size: 13px; white-space: nowrap; }
.checkbox-field input[type="checkbox"] { width: auto; }
.checkbox-field input[type="number"] { width: 120px; }

/* ---------- RFI question list ---------- */
.rfi-question-row { align-items: flex-start; }
.rfi-question-body { flex: 1; display: flex; flex-direction: column; gap: 6px; }
.rfi-question-text { font-size: 13px; color: var(--ink); }
.rfi-answer-field {
  width: 100%; background: var(--surface); border: 1px solid var(--border);
  border-radius: 5px; padding: 6px 8px; font-size: 12.5px; resize: vertical;
}

/* ---------- O&M Manuals document slots ---------- */
.om-doc-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 16px; margin-bottom: 20px; }
.om-doc-slot { display: flex; flex-direction: column; gap: 6px; }
.om-doc-slot label { font-size: 11px; text-transform: uppercase; letter-spacing: 0.03em; color: var(--ink-muted); }
.om-doc-slot input[type="text"] {
  background: var(--surface); border: 1px solid var(--border); border-radius: 5px;
  padding: 7px 9px; font-size: 13px;
}
.om-doc-file-row { display: flex; align-items: center; gap: 8px; }
.om-doc-filename { font-size: 12px; color: var(--ink-muted); flex: 1; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* ---------- Project editor ---------- */
.proj-address-block { max-width: 560px; margin-bottom: 20px; display: flex; flex-direction: column; gap: 10px; }
.qe-po-empty-hint { font-size: 12px; color: var(--ink-muted); margin: -6px 0 10px; }

/* ---------- Inline picker (select + quick-add button) ---------- */
.qe-inline-picker { display: flex; gap: 6px; align-items: stretch; }
.qe-inline-picker select { flex: 1; min-width: 0; }
.qe-inline-picker .btn { white-space: nowrap; flex-shrink: 0; }

/* ---------- Quote options / add-alternates ---------- */
.qe-options-list { display: flex; flex-direction: column; gap: 14px; margin-bottom: 20px; }
.qe-option-block { border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; background: var(--surface); }
.qe-option-head { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
.qe-option-name { flex: 1; font-weight: 600; }
.qe-option-status { width: 110px; flex-shrink: 0; }
.qe-option-subtotal { font-weight: 600; font-variant-numeric: tabular-nums; min-width: 90px; text-align: right; }
.qe-option-table { margin-top: 6px; }
.qe-option-table th { font-size: 10.5px; }
.qe-options-empty { font-size: 12.5px; color: var(--ink-muted); font-style: italic; margin: 0 0 4px; }
.qe-lot-repquote { width: 130px; flex-shrink: 0; }
/* r228 [his 4D]: the sales-rep dropdown on the lot strip, beside the rep's
   quote number. A select in a row of text inputs, so it gets the same box. */
.qe-lot-row select.qe-lot-rep {
  width: 175px; flex-shrink: 0; border: 1px solid transparent; background: #fff;
  border-radius: 4px; padding: 6px 6px; font-weight: 600; font-size: 12.5px;
}

/* ---------- Round 19 line types in the quote grid ---------- */
.qe-rowtype-cell {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--ink-muted); white-space: nowrap; font-weight: 600;
}
/* Internal notes are yellow -- a standing visual reminder that this text
   never reaches a client document. */
.qe-row-intnote > td { background: #EEF1F7; }
.qe-row-intnote .qe-rowtype-cell { color: #5B6068; }
.qe-row-blank > td { height: 18px; }
.qe-row-subtotal > td { border-top: 1px solid var(--border); }
.qe-row-subtotal input[data-field="description"] { font-weight: 600; }
/* Profit rows never appear on any document -- flag them clearly in-app. */
.qe-row-profit > td { background: #E7ECFA; }
.qe-row-no-quote > td { background: #F4F4F6; font-style: italic; }

/* ---------- Configurable line-item columns (item 10) ---------- */
.qe-columns-panel {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 12px 14px; margin-bottom: 10px;
}
.qe-columns-panel[hidden] { display: none; }
.qe-columns-head { display: flex; align-items: center; gap: 10px; margin-bottom: 4px; }
.qe-columns-head strong { flex: 1; }
.qe-columns-hint { font-size: 11.5px; color: var(--ink-muted); margin: 0 0 10px; }
.qe-columns-list { display: grid; grid-template-columns: repeat(auto-fill, minmax(215px, 1fr)); gap: 4px 14px; }
.qe-col-off { font-size: 10.5px; color: var(--ink-muted); }
/* Resize grip on each header cell. */
.qe-table th { position: relative; }
.qe-col-resize {
  position: absolute; top: 0; right: 0; width: 7px; height: 100%;
  cursor: col-resize; user-select: none;
}
.qe-col-resize:hover { background: var(--primary); opacity: 0.35; }
/* Internal-note cells are tinted wherever they appear, matching the
   internal-note ROW type -- same meaning, so same visual language. */
.qe-internal-field { background: #EEF1F7; }

/* ---------- Bulk selection bar on list views ---------- */
.bulk-bar {
  display: flex; align-items: center; gap: 12px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 9px 14px; margin-bottom: 10px;
}
.bulk-bar[hidden] { display: none; }
.bulk-count { font-weight: 600; font-size: 13px; }
.bulk-actions { display: flex; gap: 8px; flex: 1; }
#data-table th.col-sel, #data-table td.bulk-sel-cell {
  width: 34px; text-align: center; padding-left: 6px; padding-right: 2px;
}
#data-table td.bulk-sel-cell input { cursor: pointer; margin: 0; }

/* ---------- Item 26: tighten the quote editor's top sections ----------
   The header, meta fields, notes and Lots block were eating most of the
   first screen before a single line item appeared. These rules pull that
   whole area in so the grid -- the part actually being worked on -- starts
   higher up. Scoped to #quote-editor so no other editor shifts. */
#quote-editor .qe-topbar { margin-bottom: 12px; }
#quote-editor .qe-heading h1 { font-size: 20px; }
#quote-editor .qe-fields { gap: 9px; margin-bottom: 9px; }
#quote-editor .qe-fields .field label { font-size: 10.5px; }
#quote-editor .qe-fields input,
#quote-editor .qe-fields select { padding: 6px 9px; }
#quote-editor .qe-notes-field { margin-bottom: 10px; }
#quote-editor .qe-notes-field textarea { min-height: 46px; }
#quote-editor .qe-lines-head { margin-top: 10px; margin-bottom: 6px; }
#quote-editor .qe-lines-head h2 { font-size: 14px; }
#quote-editor .qe-lots-section { margin-bottom: 10px; }
#quote-editor .qe-lots-empty,
#quote-editor .qe-options-empty { margin: 2px 0 6px; }

/* ---------- Reports screen ---------- */
.report-root[hidden] { display: none; }
.report-tiles {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 12px; margin-bottom: 22px;
}
.report-tile {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 14px 16px;
}
.report-tile-label {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-muted); font-weight: 600; margin-bottom: 4px;
}
.report-tile-value { font-size: 21px; font-weight: 700; font-variant-numeric: tabular-nums; }
.report-tile-sub { font-size: 11.5px; color: var(--ink-muted); margin-top: 2px; }
.report-section { margin-bottom: 26px; }
.report-table td, .report-table th { padding: 7px 10px; }
.report-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.report-est {
  font-size: 9px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--ink-muted); background: var(--border); padding: 1px 4px; border-radius: 3px;
}

/* ---------- Schedule image import review ---------- */
.schedimport-warning {
  background: #EEF1F7; border: 1px solid #C3CCE6; border-radius: var(--radius);
  padding: 11px 14px; margin-bottom: 14px; font-size: 12.5px; line-height: 1.5;
}
.sched-conf {
  font-size: 9.5px; text-transform: uppercase; letter-spacing: 0.05em;
  padding: 2px 7px; border-radius: 999px; font-weight: 600;
}
.sched-conf-high { background: #E7ECFA; color: #1F3FB5; }
.sched-conf-medium { background: #EEF1F7; color: #5B6068; }
.sched-conf-low { background: #FBE0DC; color: #A3372A; }
.sched-low > td { background: #FDF7F6; }
.sched-unmatched { font-size: 10.5px; color: #A3372A; margin-top: 3px; }

/* Item 15: the lot markup field is a percentage -- say so in the field
   itself rather than relying on a placeholder that vanishes once typed in. */
.qe-lot-pct-wrap { position: relative; display: inline-flex; align-items: center; }
.qe-lot-pct { padding-right: 20px !important; }
.qe-lot-pct-sign {
  position: absolute; right: 7px; font-size: 11.5px;
  color: var(--ink-muted); pointer-events: none;
}

/* ---------- Quote navigator (round 23) ---------- */
.qe-nav {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 8px 12px; margin-bottom: 12px;
}
.qe-nav-row { display: flex; align-items: center; gap: 10px; padding: 4px 0; flex-wrap: wrap; }
.qe-nav-row + .qe-nav-row { border-top: 1px solid var(--border); }
.qe-nav-label {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-muted); font-weight: 700; min-width: 54px; flex-shrink: 0;
}
.qe-nav-tabs { display: flex; gap: 6px; flex: 1; flex-wrap: wrap; }
.qe-nav-actions { display: flex; gap: 4px; flex-shrink: 0; }
.qe-nav-tab {
  display: inline-flex; align-items: center; gap: 7px;
  border: 1px solid var(--border); background: var(--bg);
  border-radius: 999px; padding: 4px 11px; cursor: pointer;
  font-size: 12.5px; color: var(--ink);
}
.qe-nav-tab:hover { border-color: var(--primary); }
.qe-nav-tab.is-active {
  background: var(--brand-blue); border-color: var(--brand-blue); color: #fff;
}
.qe-nav-tab.is-active .qe-nav-tab-sub,
.qe-nav-tab.is-active .qe-nav-tab-count { color: rgba(255,255,255,0.85); }
.qe-nav-tab-name { font-weight: 600; }
.qe-nav-tab-sub { font-size: 11px; color: var(--ink-muted); }
.qe-nav-tab-count {
  font-size: 10.5px; color: var(--ink-muted);
  background: rgba(0,0,0,0.06); border-radius: 999px; padding: 0 6px;
}
.qe-nav-rename { font-size: 11px; opacity: 0.55; }
.qe-nav-rename:hover { opacity: 1; }
.qe-nav-empty { font-size: 12px; color: var(--ink-muted); font-style: italic; }

/* ---------- Inline new-customer form in the quote editor ---------- */
.qe-cust-form {
  border: 1px solid var(--border); border-radius: var(--radius);
  background: var(--surface); padding: 11px 13px; margin-bottom: 11px;
}
.qe-cust-form[hidden] { display: none; }
.qe-cust-head { display: flex; align-items: center; gap: 12px; margin-bottom: 9px; }
.qe-cust-kind { font-size: 12.5px; display: inline-flex; align-items: center; gap: 5px; cursor: pointer; }
.qe-cust-spacer { flex: 1; }
.qe-cust-fields { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 9px; }
.qe-cust-fields .field[hidden] { display: none; }

/* ---------- Item 17: row-type tag in the Type column ---------- */
.qe-type-tag {
  display: block; font-size: 9px; text-transform: uppercase;
  letter-spacing: 0.05em; font-weight: 700; margin-bottom: 2px;
  padding: 1px 5px; border-radius: 3px; text-align: center;
  background: var(--border); color: var(--ink-muted);
}
.qe-type-int-note { background: #E3E7EF; color: #4A5060; }
.qe-type-profit { background: #E7ECFA; color: #1F3FB5; }
.qe-type-no-quote { background: #F4F4F6; color: #5B6068; }
.qe-type-subtotal { background: #E7ECFA; color: #1F3FB5; }

/* ---------- Item 18: per-row type picker in its own gutter ---------- */
#data-table th.col-rowtype, .qe-table th.col-rowtype { width: 92px; }
.qe-rowtype-pick { width: 92px; }
.qe-rowtype-pick select {
  width: 100%; font-size: 11.5px; padding: 3px 4px;
  border: 1px solid var(--border); border-radius: 4px; background: var(--bg);
}

/* ---------- Item 19: horizontal scrolling inside the line-item list ----------
   The grid can carry 17 columns and each is resizable, so it will outgrow the
   window. The wrap already scrolls; these make it actually usable -- the table
   keeps its natural width instead of being squashed, and the scrollbar is
   visible rather than an overlay you have to discover. */
.qe-table-wrap { overflow-x: auto; scrollbar-width: auto; }
.qe-table-wrap::-webkit-scrollbar { height: 12px; }
.qe-table-wrap::-webkit-scrollbar-track { background: var(--bg); border-radius: 6px; }
.qe-table-wrap::-webkit-scrollbar-thumb {
  background: #B9C0CC; border-radius: 6px; border: 2px solid var(--bg);
}
.qe-table-wrap::-webkit-scrollbar-thumb:hover { background: #8F98A8; }
.qe-table { width: max-content; min-width: 100%; }

/* MFG ID tag in the manufacturers list */
.mfg-id-tag {
  font-family: var(--font-mono); font-size: 11px; font-weight: 600;
  background: var(--border); color: var(--ink); padding: 1px 6px; border-radius: 3px;
}

/* ---------- Items 22/23: symbols inside money and percent cells ---------- */
.qe-unit-wrap { position: relative; display: block; }
.qe-unit-wrap input { width: 100%; }
.qe-unit-sign {
  position: absolute; top: 50%; transform: translateY(-50%);
  font-size: 11.5px; color: var(--ink-muted); pointer-events: none;
}
.qe-unit-money .qe-unit-sign { left: 7px; }
.qe-unit-money input { padding-left: 17px !important; }
.qe-unit-pct .qe-unit-sign { right: 7px; }
.qe-unit-pct input { padding-right: 17px !important; }

/* ---------- Item 13: options-only totals, visually paired with the main
   totals bar but clearly its own scope ---------- */
.qe-totals-options {
  margin-top: 8px;
  background: var(--brand-blue);
  opacity: 0.92;
}

/* ---------- Items 7/8/9: sortable headers, group rows, row delete ---------- */
.th-sortable { cursor: pointer; user-select: none; white-space: nowrap; }
.th-sortable:hover { color: var(--ink); }
.th-arrow { font-size: 9px; margin-left: 5px; color: var(--brand-blue); }
.group-row td {
  background: #EEF1F7; font-weight: 700; font-size: 12px;
  text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink);
  padding: 7px 12px; border-top: 1px solid var(--border);
}
.group-level-1 td {
  background: #F6F7FA; font-size: 11px; font-weight: 600;
  letter-spacing: 0.04em; color: var(--ink-muted); padding-left: 26px;
  text-transform: none;
}
/* r224 -- the submittal list: a project heading, then the revision chain
   indented under its own first revision. Same visual grammar as a change
   order nested under its order (.co-child-row), because it is the same idea:
   a follow-on document that belongs to the one above it. */
.sub-rev-row { background: #fafbfc; }
.sub-rev-row:hover { background: #eef2f7; }
.sub-rev-indent { color: var(--ink-muted); margin-right: 6px; }
.sub-tr-tag {
  display: inline-block; font-size: 11px; padding: 1px 7px;
  border: 1px solid var(--border); border-radius: 10px;
  color: var(--ink-muted); white-space: nowrap;
}
#data-table td.row-del-cell { width: 34px; text-align: center; }
.qe-subtotal-fig {
  text-align: right; font-variant-numeric: tabular-nums;
  font-weight: 600; font-size: 12px; white-space: nowrap;
}
.qe-subtotal-label {
  display: block; font-size: 9px; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--ink-muted); font-weight: 600;
}

/* ---------- Help tooltips ---------- */
.help-tip {
  position: fixed;
  z-index: 9999;
  max-width: 280px;
  background: var(--primary);
  color: #fff;
  font-size: 12px;
  line-height: 1.5;
  padding: 8px 11px;
  border-radius: 6px;
  box-shadow: 0 4px 14px rgba(0,0,0,0.22);
  pointer-events: none;
}

/* ---------- Row actions stay reachable when the grid scrolls sideways ----------
   Item 19 let the line-item table grow to its natural width, which pushed the
   reorder arrows and the delete button off the right edge -- they became
   unclickable without scrolling, and the checkbox could scroll off the left.
   Pinning both gutters keeps every row action in reach at any scroll offset. */
.qe-table th.col-sel,
.qe-table td.qe-sel-cell {
  position: sticky; left: 0; z-index: 3; background: var(--surface);
}
.qe-table th.col-move, .qe-table th.col-del,
.qe-table td.qe-move-cell, .qe-table tbody td:last-child {
  position: sticky; z-index: 3; background: var(--surface);
}
.qe-table th.col-del, .qe-table tbody td:last-child { right: 0; }
.qe-table th.col-move, .qe-table td.qe-move-cell { right: 34px; }
/* Selected and type-tinted rows must keep their own colour under the pinned
   cells, or the sticky background would punch a hole through the highlight. */
.qe-row-selected td.qe-sel-cell,
.qe-row-selected td:last-child { background: #E7ECFA; }
.qe-row-intnote td.qe-sel-cell,
.qe-row-intnote td:last-child { background: #EEF1F7; }
.qe-row-profit td.qe-sel-cell,
.qe-row-profit td:last-child { background: #E7ECFA; }

/* ---------- Item 5: Settings sections as top tabs ---------- */
.settings-tabs {
  display: flex; gap: 4px; flex-wrap: wrap;
  border-bottom: 2px solid var(--border); margin-bottom: 18px;
}
.settings-tab {
  border: none; background: none; cursor: pointer;
  padding: 9px 16px; font-size: 13px; font-weight: 600;
  color: var(--ink-muted); border-bottom: 3px solid transparent;
  margin-bottom: -2px;
}
.settings-tab:hover { color: var(--ink); }
.settings-tab.is-active { color: var(--brand-blue); border-bottom-color: var(--brand-blue); }
/* The group bodies were only revealed by an accordion class; with tabs the
   visible group must always show its body. */
.settings-group[hidden] { display: none; }
.settings-group .settings-group-body { display: block; }
.settings-group-header[hidden] { display: none; }

/* ---------- Item 4: rep agency contacts ---------- */
.contact-row {
  display: grid; grid-template-columns: 1.2fr 1fr 1.4fr 1fr 28px;
  gap: 6px; margin-bottom: 6px; align-items: center;
}
.contact-row input { width: 100%; }

/* ---------- Manufacturer typeahead ---------- */
.mfg-ta { position: relative; display: block; }
.mfg-ta-input { width: 100%; }
.mfg-ta-list {
  position: fixed; z-index: 9998; min-width: 260px; max-width: 340px;
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: 0 6px 20px rgba(0,0,0,0.16);
  max-height: 280px; overflow-y: auto;
}
.mfg-ta-opt {
  display: flex; justify-content: space-between; align-items: center; gap: 10px;
  padding: 6px 10px; cursor: pointer; font-size: 12.5px;
}
.mfg-ta-opt:hover, .mfg-ta-opt.is-active { background: #E7ECFA; }
.mfg-ta-name { color: var(--ink); }
.mfg-ta-id {
  font-family: var(--font-mono); font-size: 11px; color: var(--ink-muted);
  background: var(--bg); padding: 1px 6px; border-radius: 3px; flex-shrink: 0;
}
.mfg-ta-none { padding: 8px 10px; font-size: 12px; color: var(--ink-muted); font-style: italic; }

/* ---------- Header rows: section titles inside the schedule ---------- */
.qe-row-header > td { background: #EDF0F6; }
.qe-type-header { background: var(--brand-blue); color: #fff; }
.qe-header-text {
  font-weight: 700 !important;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.qe-row-header td.qe-sel-cell,
.qe-row-header td:last-child { background: #EDF0F6; }

/* ---------- Accounts / session ---------- */
.sidebar-user { display: flex; align-items: center; justify-content: space-between; gap: 8px; }
#sidebar-username { font-size: 11.5px; opacity: 0.85; }
.sidebar-signout {
  background: none; border: 1px solid rgba(255,255,255,0.28); color: inherit;
  font-size: 11px; padding: 3px 9px; border-radius: 5px; cursor: pointer;
}
.sidebar-signout:hover { background: rgba(255,255,255,0.12); }
.acct-row {
  /* ROUND 106 [stated 3]: the LAST column holds every action, so a row with an
     extra button can never push one onto a second line.
     ROUND 206: a fifth column for the person's home location. The count here
     must match what renderUserList emits -- adding a cell without widening
     this is exactly how the row silently wrapped the first time. */
  display: grid;
  grid-template-columns: minmax(0, 1.4fr) 130px 150px minmax(0, 1fr) auto;
  gap: 10px; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--border);
}
.acct-actions { display: flex; align-items: center; gap: 8px; justify-content: flex-end; white-space: nowrap; }
.acct-locked { font-size: 13px; opacity: 0.55; padding: 0 4px; cursor: default; }
.acct-name { font-size: 13px; font-weight: 600; }
.acct-username { font-weight: 400; color: var(--ink-muted); font-size: 11.5px; margin-left: 6px; }
.acct-last { font-size: 11.5px; color: var(--ink-muted); }
.settings-group[data-admin-only][hidden] { display: none; }

/* Item 9: name each totals bar so it's obvious which scope it covers. */
.qe-totals-title {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.07em;
  font-weight: 700; color: rgba(255,255,255,0.9); margin-right: auto;
}
/* Item 8: "Earn" was a dark accent on a dark bar -- unreadable. */
.qe-totals .qe-total-earn span,
.qe-totals .qe-total-earn,
#qe-total-earn, #qe-opt-total-earn { color: #fff !important; }

/* Item 7: save confirmation, directly under the button */
.qe-save-wrap { position: relative; display: inline-block; }
.qe-saved-flag {
  position: absolute; top: calc(100% + 4px); right: 0;
  font-size: 11px; font-weight: 600; color: #1F3FB5;
  background: #E7ECFA; border-radius: 4px; padding: 2px 8px; white-space: nowrap;
}
.qe-saved-flag[hidden] { display: none; }

/* ---------- Item 4: collapsible sidebar ---------- */
.sidebar-toggle {
  position: fixed; top: 10px; left: 10px; z-index: 60;
  width: 30px; height: 30px; line-height: 1;
  background: rgba(255,255,255,0.14); color: #fff;
  border: 1px solid rgba(255,255,255,0.22); border-radius: 6px;
  cursor: pointer; font-size: 14px;
}
.sidebar-toggle:hover { background: rgba(255,255,255,0.24); }
body.sidebar-collapsed .sidebar { width: 0; min-width: 0; padding: 0; overflow: hidden; }
body.sidebar-collapsed .sidebar-toggle {
  background: var(--brand-blue); border-color: var(--brand-blue);
}
/* The toggle sits over the sidebar header when expanded, so nudge the brand
   across rather than letting them overlap. */
.sidebar .brand { padding-left: 34px; }
body.sidebar-collapsed .sidebar .brand { padding-left: 0; }
.qe-nav-delphase { font-size: 13px; }
.qe-nav-tab.is-active .qe-nav-delphase { color: rgba(255,255,255,0.8); }

/* ---------- Record type-ahead (customers, projects) ---------- */
.ta-input { width: 100%; }
.record-ta-list { min-width: 260px; }
.record-ta-create {
  border-top: 1px solid var(--border);
  color: var(--brand-blue);
  font-weight: 600;
}

/* Customer details panel on a quote (round 30 item 6) */
.qe-cust-details { padding: 12px 14px; }

/* ---------- Project editor layout (round 30 items 1-3) ---------- */
.proj-notes-top { margin: 0 0 18px; }
.proj-two-col { display: grid; grid-template-columns: 1fr 1fr; gap: 26px; align-items: start; }
@media (max-width: 1100px) { .proj-two-col { grid-template-columns: 1fr; } }
.proj-phase-block { border: 1px solid var(--border); border-radius: 8px; margin-bottom: 12px; overflow: hidden; }
.proj-phase-bar {
  display: flex; justify-content: space-between; align-items: center;
  padding: 9px 12px; background: var(--surface-2); border-bottom: 1px solid var(--border);
}
.proj-phase-bar h3 { margin: 0; font-size: 14px; }
.proj-phase-quotes { padding: 4px 0; }
.proj-phase-quotes .qe-po-row { border: 0; border-radius: 0; margin: 0; }
.proj-phase-empty { padding: 10px 14px; color: var(--ink-muted); font-size: 12.5px; }
.proj-won-badge {
  background: #E6F4EA; color: #1B5E20; border: 1px solid #B7DFC2;
  border-radius: 999px; padding: 1px 9px; font-size: 11px; font-weight: 700;
}
tr.row-won td { background: #F2FAF4; }

/* Projects list: phases and their quotes inline (round 30 item 4) */
.proj-row-phases { display: flex; flex-direction: column; gap: 3px; }
.proj-row-phase { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
.proj-row-phase-name { font-size: 11.5px; font-weight: 600; color: var(--ink-muted); min-width: 74px; }
.proj-row-quote {
  font-size: 11px; padding: 1px 7px; border-radius: 999px;
  border: 1px solid var(--border); background: var(--surface-2);
}
.proj-row-quote.status-won { background: #E6F4EA; border-color: #B7DFC2; color: #1B5E20; font-weight: 700; }

/* Round 31: order release state in the list */
.hold-pill {
  display: inline-block; border: 1px solid var(--ink); border-radius: 3px;
  padding: 0 7px; font-size: 11px; font-weight: 700; letter-spacing: 0.03em;
}
.waiting-on { display: block; font-size: 11px; margin-top: 2px; }
tr.row-hold td { background: #FAFAFB; }

/* Per-project tax exemption (round 36b) */
.proj-tax-block { margin-bottom: 16px; }

.proj-tax-check input { width: 15px; height: 15px; }
.proj-tax-fields { display: grid; grid-template-columns: 1fr 1fr; gap: 18px; margin-top: 10px; }
@media (max-width: 900px) { .proj-tax-fields { grid-template-columns: 1fr; } }
.proj-tax-cert-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

/* Round 36: money flags. Red is reserved for money we're chasing or risk. */
.overdue-flag {
  display: inline-block; color: #A32020; font-weight: 700; font-size: 11px;
  letter-spacing: 0.02em;
}
.credit-flag {
  display: inline-block; background: #FBE9E9; color: #A32020;
  border: 1px solid #E7B7B7; border-radius: 3px;
  padding: 0 6px; font-size: 10.5px; font-weight: 700; letter-spacing: 0.03em;
}
tr.row-overdue td { background: #FDF6F6; }
/* r255: T&C §5.4. Darker than OVER LIMIT on purpose -- over the limit is a
   judgement about how much more we will carry; on hold is a clause that
   withholds work on every order the account has. */
.hold-flag {
  display: inline-block; background: #7A1414; color: #fff;
  border: 1px solid #5E0F0F; border-radius: 3px;
  padding: 0 6px; font-size: 10.5px; font-weight: 700; letter-spacing: 0.03em;
  margin-left: 4px;
}
tr.row-on-hold td { background: #F7ECEC; }
/* r312 [his item 5]: the account note -- what is held and why, one sentence a
   line, readable down the phone to a client. */
ul.account-note { margin: 6px 0 6px 18px; padding: 0; }
ul.account-note li { margin: 2px 0; line-height: 1.45; }
/* r255: the service-charge screen reuses the .settings-log table, which has no
   numeric column rule of its own -- money that is not right-aligned is money
   nobody can add up down a column. */
.settings-log td.num, .settings-log th.num { text-align: right; font-variant-numeric: tabular-nums; }
.settings-log tr.row-muted td { color: var(--muted, #6b7280); }

.credit-banner {
  border: 2px solid #A32020; background: #FBE9E9; border-radius: 6px;
  padding: 10px 14px; margin-bottom: 14px;
}
.credit-banner h3 {
  margin: 0 0 3px; font-size: 12px; text-transform: uppercase;
  letter-spacing: 0.06em; color: #A32020;
}
.credit-banner p { margin: 0 0 8px; font-size: 12.5px; }
.credit-banner .overridden {
  font-size: 11.5px; color: var(--ink-muted); margin-top: 6px;
}
/* ROUND 206 -- RED MEANS MONEY AT RISK, AND NOTHING ELSE.
   The existing .credit-banner is red because it only ever appeared when there
   WAS a problem. The counter screen shows the credit position on every sale
   [stated: "credit banner we need more than ever with this as a lot of these
   the clients have accounts"], so a customer who is on terms and inside their
   limit was being shown a red box saying everything is fine. That is exactly
   how red stops meaning anything. Good news is quiet; .is-bad keeps the red. */
.credit-banner.credit-ok {
  border-color: var(--border); background: var(--surface-2); color: var(--ink-muted);
}
.credit-banner.credit-ok strong { color: var(--ink); }
.credit-banner.is-bad { border-color: #A32020; background: #FBE9E9; }
.next-strip {
  border: 1px solid var(--border); border-radius: 6px;
  padding: 10px 14px; margin-bottom: 14px; background: var(--surface-2);
}
.next-strip h3 {
  margin: 0 0 7px; font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--ink-muted);
}
.next-steps { display: flex; gap: 8px; flex-wrap: wrap; }
.next-step {
  border: 1px solid var(--border); border-radius: 5px; background: #fff;
  padding: 6px 11px; font-size: 12.5px; cursor: pointer;
}
/* ROUND 76 [stated]: "Have completed ones filled green, ones that need to be
   done and are able to be done next in orange, and ones that need to be done
   but are waiting on something in red."
   Filled, not outlined -- the state should read from across the room. */
.next-step.done {
  background: #E3F3E8; border-color: #9FD2B0; color: #1F6B39; font-weight: 600;
}
.next-step.now {
  background: #FFF1DC; border-color: #E0A75E; color: #8A5200;
  border-width: 2px; font-weight: 700;
}
/* Blocked: needed, but waiting on something else. Red is used sparingly in
   this app for money at risk -- a blocked step qualifies, because it is the
   thing holding the job up. */
.next-step.todo {
  background: var(--danger-soft); border-color: #E5B8B8; color: var(--danger);
  font-weight: 600;
}
.next-step.todo::before { content: '\26A0\FE0E'; margin-right: 5px; }

.cust-credit-summary { font-size: 13px; margin-bottom: 10px; }

.pay-transit {
  display: inline-block; background: #FFF6E5; border: 1px solid #E8D3A8;
  color: #7A5B14; border-radius: 3px; padding: 0 6px;
  font-size: 10.5px; font-weight: 700; letter-spacing: 0.03em;
}

/* r221 [his 3]: the two states a payment received can be in, on the Payments
   Received list. They carry the SAME palette as .pay-transit above and the
   green used for a settled balance, so the state reads identically whether it
   is seen inside the invoice or on the list. Money in the bank is green;
   money still on its way is the amber this app already uses for "promised,
   not yet true". */
.status-pill.status-in-transit { background: #FFF6E5; color: #7A5B14; }
.status-pill.status-paid { background: #E3F3E8; color: #24793E; }


.submittal-gate {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 8px 2px 2px; font-size: 12.5px;
}

/* Money inputs carry a formatted value when they aren't focused (round 38). */
.money-input { text-align: right; font-variant-numeric: tabular-nums; }
.qe-unit-money .money-input { text-align: right; }

/* Sale-type marker on the quote — internal only, never printed (round 38). */
.sale-marker {
  border: 2px solid #A32020; background: #FBE9E9; color: #A32020;
  border-radius: 5px; padding: 7px 12px; margin-bottom: 12px;
  font-size: 12px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.06em;
}
.sale-marker .sm-sub {
  display: block; font-weight: 400; text-transform: none;
  letter-spacing: 0; font-size: 11.5px; color: var(--ink-muted); margin-top: 2px;
}

#qe-tax-exempt-note {
  display: block; margin-top: 3px; color: #A32020; font-weight: 700;
  font-size: 11px; letter-spacing: 0.03em;
}
/* r289: the note now says which named rate the quote carries, which is not a
   warning. Red bold is kept for the old-style exempt project only. */
#qe-tax-exempt-note.tax-note-plain { color: var(--ink-muted); font-weight: 400; letter-spacing: 0; }
#qe-tax-exempt-note.tax-note-exempt { color: #24793E; font-weight: 600; letter-spacing: 0; }

/* ROUND 40 — client signature is RECORDED, not captured on our screen. */
.order-signed-meta { font-size: 13px; margin: 2px 0 12px; }
.order-signed-meta strong { font-size: 15px; }
.signed-file-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; }
.signed-file-row a { color: var(--accent); font-weight: 600; font-size: 13px; }

/* ROUND 44 [stated]: "Drop-ship button to click is extended all the way across
   the screen. Should be only able to click it within the box parameters."
   A <label> is block-level, so it stretched the full width of the editor and
   every stray click miles from the tick toggled it. inline-flex shrinks the
   clickable area to the tick plus its text. */
.order-dropship {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  cursor: pointer;
  width: auto;
  max-width: max-content;
  padding: 6px 10px 6px 2px;
  margin-bottom: 12px;
  border-radius: 4px;
  user-select: none;
}
.order-dropship:hover { background: var(--surface-2, #f4f6fa); }
.order-dropship input[type="checkbox"] { width: 15px; height: 15px; margin: 0; cursor: pointer; }
.order-dropship span { font-size: 13px; }


/* ROUND 45 [stated]: "tax exempt button is still the entire row and not just a
   clickable square." Same fault as the drop-ship tick: a <label> is block-level
   so it spanned the whole panel and any click on that row toggled it.
   NOTE: there was an earlier .proj-tax-check rule -- this replaces it rather
   than competing with it, because duplicate rules are how this regresses. */
.proj-tax-check {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  width: auto;
  max-width: max-content;
  cursor: pointer;
  padding: 6px 10px 6px 2px;
  border-radius: 4px;
  user-select: none;
}
.proj-tax-check:hover { background: var(--surface-2, #f4f6fa); }
.proj-tax-check input[type="checkbox"] { width: 15px; height: 15px; margin: 0; cursor: pointer; }
.proj-tax-check span { font-size: 13px; }

/* ROUND 47 — the statement ledger on screen. */
.stmt-screen td.num, .stmt-screen th.num { text-align: right; font-variant-numeric: tabular-nums; }
.stmt-screen tr.opening td { background: var(--surface-2, #f4f6fa); font-weight: 600; }
.stmt-screen tr.closing td { border-top: 2px solid var(--ink); font-size: 14px; }
.stmt-screen .stmt-against { color: var(--ink-muted); }

/* ROUND 48 — remaining RFQ links when the browser blocks extra tabs. */
.rfq-open-list-box {
  border: 1px solid var(--accent); background: #f4f7ff;
  border-radius: 6px; padding: 14px 18px; margin: 16px 0;
}
.rfq-open-list-box h3 { margin: 0 0 4px; font-size: 14px; }
.rfq-open-links { margin: 8px 0 0; padding-left: 18px; }
.rfq-open-links li { margin-bottom: 5px; font-size: 13px; }
.rfq-open-links a { color: var(--accent); font-weight: 600; }

/* ROUND 49 — the project overview: every related document, grouped. */
.proj-overview { margin-bottom: 20px; }
.proj-ov-group {
  border: 1px solid var(--border); border-radius: 6px; margin-bottom: 10px; overflow: hidden;
}
.proj-ov-head {
  background: var(--surface-2, #f4f6fa); padding: 8px 14px; font-weight: 700;
  font-size: 12.5px; text-transform: uppercase; letter-spacing: 0.04em;
  display: flex; align-items: center; gap: 8px;
}
.proj-ov-count {
  background: var(--accent); color: #fff; border-radius: 9px;
  font-size: 10.5px; padding: 1px 7px; font-weight: 700;
}
.proj-ov-rows { display: flex; flex-direction: column; }
.proj-ov-row {
  display: flex; align-items: center; gap: 12px; padding: 8px 14px;
  border-top: 1px solid var(--border); cursor: pointer; font-size: 13px;
}
.proj-ov-row:hover { background: var(--surface-2, #f4f6fa); }
.proj-ov-label { font-weight: 600; min-width: 110px; }
.proj-ov-sub { color: var(--ink-muted); flex: 1; }
.proj-ov-money { font-variant-numeric: tabular-nums; font-weight: 600; }
.sale-marker-mini {
  background: #FBE9E9; color: #A32020; font-size: 9.5px; font-weight: 800;
  padding: 1px 5px; border-radius: 3px; letter-spacing: 0.04em;
}

/* ROUND 49 — send-a-signing-link buttons and the resulting copyable link. */
.sign-btn-row { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 6px; }
.sign-link-box {
  margin-top: 10px; padding: 12px 14px; border: 1px solid var(--accent);
  background: #f4f7ff; border-radius: 6px; font-size: 13px;
}
.sign-link-box .slb-url {
  display: flex; align-items: center; gap: 8px; margin-top: 6px;
}
.sign-link-box input {
  flex: 1; font-size: 12.5px; padding: 6px 8px; border: 1px solid var(--border);
  border-radius: 4px; background: #fff; font-family: monospace;
}
.sign-link-box .slb-note { color: var(--ink-muted); font-size: 12px; margin-top: 4px; }

/* ===================================================================
   ROUND 52 — FORM DENSITY [stated]: "clean up all the forms on the UI.
   Small things like terms or date should not take up an entire row on
   the screen. Consolidate and make the screen look cleaner for all
   modules."

   The grids were already multi-column, but two things still ate rows:
   a `.field` sitting on its own outside any grid stretched the full
   width, and the fixed 4/5-column grids forced short fields to share a
   row with nothing when the window was narrow. Both fixed globally
   here rather than editor-by-editor, so every module benefits and new
   screens inherit it.
   =================================================================== */

/* A field standing alone is almost always something short — a date, a
   term, a number. Cap it so it reads as a field, not a banner. */
/* ROUND 208: the record body now sits inside a tab panel, so the panel is a
   direct parent too. Without this line every standalone field on a quote,
   order, PO, invoice or customer would have lost its width cap the moment the
   tabs went in -- a silent layout regression across five screens. */
.quote-editor > .field,
.record-tab-panel > .field,
.settings-section > .field {
  max-width: 340px;
}
/* Notes/textarea fields are the deliberate exception — they want room. */
.quote-editor > .field.qe-notes-field,
.record-tab-panel > .field.qe-notes-field,
.settings-section > .field.qe-notes-field,
.quote-editor > .field:has(textarea),
.record-tab-panel > .field:has(textarea),
.settings-section > .field:has(textarea) {
  max-width: none;
}

/* Fixed column counts become minimums, so fields pack in on a wide
   screen and wrap sensibly on a narrow one instead of leaving gaps. */
.qe-fields-row   { grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); }
.qe-fields-row-5 { grid-template-columns: repeat(auto-fit, minmax(165px, 1fr)); }
.settings-grid   { grid-template-columns: repeat(auto-fit, minmax(240px, 1fr)); }

/* Tighter vertical rhythm — this is most of the scrolling he's hitting. */
.settings-section { margin-bottom: 20px; }
.settings-section h2 { margin-bottom: 4px; }
.settings-section .settings-sub { margin-top: 0; margin-bottom: 10px; }

/* The consolidated numbering grid: one compact row per document type. */
.numbering-grid { display: flex; flex-direction: column; gap: 6px; }
.numbering-row {
  display: grid;
  /* r244 [his item 2]: a YEAR column between the prefix and the next number,
     so the year can be taken off one document type without taking it off the
     rest. Narrow, because it is a tick. */
  grid-template-columns: 150px 1fr 54px 110px 110px 1fr;
  gap: 10px;
  align-items: center;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: 5px;
  background: #fff;
}
.numbering-row:hover { background: var(--surface-2, #f7f9fc); }
/* r255: the service-charge rate table reuses the numbering grid's chrome with
   four columns of its own. It is declared AFTER .numbering-row deliberately --
   both selectors are one class deep, so the later rule wins, and putting this
   above would have left the grid silently laid out in six columns. */
.numbering-head.sc-rate-head, .numbering-row.sc-rate-row {
  grid-template-columns: 200px 110px 120px 1fr;
}
.sc-rate-note { font-size: 11px; line-height: 1.35; }
.numbering-row.sc-rate-row .nr-preview { font-weight: 700; text-align: left; }
.numbering-row.sc-rate-row .nr-preview.cell-alert { color: #A32020; }
.numbering-row .nr-label { font-weight: 600; font-size: 13px; }
.numbering-row input { width: 100%; }
.numbering-row .nr-preview {
  font-family: var(--font-mono, monospace);
  font-size: 12.5px;
  color: var(--accent);
  font-weight: 600;
  text-align: right;
}
.numbering-head {
  display: grid;
  grid-template-columns: 150px 1fr 54px 110px 110px 1fr;
  gap: 10px;
  padding: 0 10px 2px;
  font-size: 10.5px;
  text-transform: uppercase;
  letter-spacing: 0.05em;
  color: var(--ink-muted);
}
.numbering-head span:last-child { text-align: right; }

/* ==========================================================================
   r260 [his item 1(c)] -- A SEVENTH COLUMN: THE PLAIN-ID TICK.
   ==========================================================================
   [stated] "anything with an 'ID' should be on the list also except there is
   no prefix, no dash, and no date -- just the ID number of that unit."

   Declared AFTER .numbering-row / .numbering-head for the same reason r255's
   rate table is: both selectors are one class deep, so the later rule wins,
   and putting this above would leave the grid laid out in six columns with the
   preview pushed off the end. The r255 rule is one class DEEPER
   (.numbering-row.sc-rate-row) so it still beats this one, which is what keeps
   the rate table on its own four columns. */
.numbering-head-r260, .numbering-row-r260 {
  grid-template-columns: 150px 1fr 54px 66px 110px 110px 1fr;
}
/* The plain tick reads as the stronger of the two, because it overrides the
   year tick beside it -- and a tick that silently disables its neighbour has
   to look like it does something. */
.nr-plain { accent-color: var(--accent); }

/* ==========================================================================
   r260 [his item 4] -- THE TERMS TAB.
   ==========================================================================
   Every block on it arrived from a different settings group, each with its own
   spacing habits, so they are given one rule here rather than looking like
   seven things stacked on top of each other. The rule is on the class
   relocateTermsBlocks() adds, not on the ids, so a block added to the tab next
   year is laid out with the rest without this file being touched. */
.terms-tab-block {
  margin: 0 0 18px;
  padding: 14px 16px 4px;
  border: 1px solid var(--border);
  border-left: 3px solid var(--accent);
  border-radius: 0 6px 6px 0;
  background: #fff;
}
.terms-tab-block > h2:first-child,
.terms-tab-block > label:first-child { margin-top: 0; }
.terms-tab-block textarea { width: 100%; }

/* r260 [his ask]: the contact number, which is also the merge field's number.
   Monospace and muted -- it is a label, not a field, and it must not look
   editable. */
/* r260: the saved company signature, shown in place of the pad. */
.cs-saved { display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  border:1px solid var(--border); border-radius:6px; padding:8px 12px; background:#fff; }
.cs-saved img { max-height:74px; max-width:100%; }
/* r262: found by tests/check-hidden.js, which had never been in the battery.
   `display:flex` above outranks the browser's own [hidden] rule, so with no
   signature saved the countersign window still showed an empty box with a
   broken image and "The company signature, from Settings. Change it there."
   beside the pad -- telling him to change a signature that does not exist. */
.cs-saved[hidden] { display: none; }

.cust-contact-n {
  font-family: var(--font-mono, monospace);
  font-size: 11px; color: var(--ink-muted); white-space: nowrap;
  align-self: center;
}

/* ROUND 52 — the three-way Yes / No / Cancel ask. Cancel is deliberately
   separated from the answer buttons: it abandons the request entirely, which
   is a different kind of thing from answering "no". */
.ask3-dialog { max-width: 460px; }
.ask3-message { font-size: 14px; line-height: 1.55; margin: 0 0 6px; }
.ask3-foot { display: flex; align-items: center; gap: 8px; }
.ask3-spacer { flex: 1; }

/* ROUND 53 — job number lookup: one box that finds every document on a job. */
#job-search { min-width: 190px; }
.job-search-results {
  border: 1px solid var(--accent);
  background: #f6f9ff;
  border-radius: 6px;
  padding: 14px 16px;
  margin-bottom: 16px;
}
.jsr-head {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  margin-bottom: 10px;
}
.jsr-num {
  font-family: var(--font-mono, ui-monospace, monospace);
  font-weight: 700; font-size: 16px; letter-spacing: 0.04em;
}
.jsr-name { font-weight: 600; }
.jsr-meta { color: var(--ink-muted); font-size: 12.5px; }
.jsr-groups { display: flex; flex-wrap: wrap; gap: 8px; }
.jsr-group {
  border: 1px solid var(--border); background: #fff; border-radius: 5px;
  padding: 8px 11px; min-width: 150px;
}
.jsr-group-head {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--ink-muted); margin-bottom: 4px;
}
.jsr-doc {
  display: block; font-size: 12.5px; padding: 2px 0; cursor: pointer;
  color: var(--accent); font-weight: 600;
}
.jsr-doc:hover { text-decoration: underline; }
.jsr-empty { color: var(--ink-muted); font-size: 13px; }

/* ROUND 53 — the column holding the job search plus everything below it.
   .app is a flex row (sidebar | content), so this keeps the search bar and
   the screens stacked rather than sitting side by side. */
.content-col {
  flex: 1;
  min-width: 0;
  display: flex;
  flex-direction: column;
  overflow: auto;
}
.content-col > .main { overflow: visible; }
.job-search-bar {
  /* r231: same gutter as every module, so the lookup bar lines up with the
     screen underneath it instead of sitting 4px inside it. */
  padding: 10px var(--module-gutter) 0;
  flex-shrink: 0;
}
.job-search-bar input {
  width: 100%;
  max-width: 460px;
  padding: 7px 11px;
  font-size: 13px;
  border: 1px solid var(--border);
  border-radius: 5px;
}
.job-search-bar input:focus {
  outline: none;
  border-color: var(--accent);
}
.job-search-results { margin: 10px var(--module-gutter) 0; }

/* ROUND 54 — submittal dispositions in the architect's own vocabulary.
   Approved and Approved as Noted both PROCEED, so both read green; the two
   that stop the job read red. The distinction is the whole point. */
.status-pill.status-approved-as-noted { background: #E3F3E8; color: #1F6B37; }
.status-pill.status-revise-\&-resubmit { background: var(--danger-soft); color: var(--danger); }
.status-pill.status-rejected { background: var(--danger-soft); color: var(--danger); }
.approval-select.status-approved-as-noted { color: #1F6B37; }
.approval-select.status-revise-\&-resubmit { color: var(--danger); }
.approval-select.status-rejected { color: var(--danger); }

/* ROUND 54 — lien rights. Red means the security behind real money is at
   risk, which is exactly what red is reserved for in this app. */
.proj-lien { margin-bottom: 22px; }
.lien-banner {
  padding: 10px 14px; border-radius: 6px; margin-bottom: 10px;
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap; font-size: 13.5px;
}
.lien-banner.lien-overdue { background: var(--danger-soft); color: var(--danger); border: 2px solid var(--danger); }
.lien-banner.lien-urgent  { background: #FFF4E5; color: #9A5B00; border: 1px solid #E0A75E; }
.lien-banner.lien-ok      { background: #EEF3FF; color: var(--accent); border: 1px solid var(--accent); }
.lien-banner.lien-done    { background: #E3F3E8; color: #24793E; border: 1px solid #7CBF95; }
.lien-banner.lien-muted   { background: var(--surface-2, #f4f6fa); color: var(--ink-muted); border: 1px solid var(--border); }
.lien-state { font-size: 11.5px; font-weight: 700; letter-spacing: 0.04em; opacity: 0.85; }
.lien-rule-note { margin: -4px 0 10px; font-size: 12px; }
.lien-caveat { margin-top: 6px; font-size: 11.5px; }
.lien-check { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; max-width: max-content; }
.lien-check input { width: 15px; height: 15px; margin: 0; }
.status-pill.status-lien-overdue { background: var(--danger-soft); color: var(--danger); }
.status-pill.status-lien-urgent  { background: #FFF4E5; color: #9A5B00; }
.status-pill.status-lien-ok      { background: #EEF3FF; color: var(--accent); }
.status-pill.status-lien-unknown { background: var(--surface-2, #f4f6fa); color: var(--ink-muted); }

/* ROUND 54 — the editable lien deadline table. */
.lien-grid { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
.lien-grid-head, .lien-grid-row {
  display: grid; grid-template-columns: 70px 110px 110px 1fr; gap: 10px; align-items: center;
}
.lien-grid-head {
  padding: 0 10px 2px; font-size: 10.5px; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--ink-muted);
}
.lien-grid-row {
  padding: 6px 10px; border: 1px solid var(--border); border-radius: 5px; background: #fff;
}
.lien-grid-row .lg-state { font-weight: 700; font-family: var(--font-mono, monospace); }
.lien-grid-row input { width: 100%; }

/* ROUND 55 — checkbox fields in the slide-over panel. */
/* r291: the card status line carries a REFUSAL now, and a refusal that is
   worth reading is more than one sentence -- "Card Transaction cannot be
   refunded", then what that means, then the two ways out. Written with blank
   lines between them, and they have to survive to the screen or the paragraphs
   run into each other ("...cannot be refunded That is what a card charge
   answers..."), which is what the first render of it did. */
#card-status { white-space: pre-line; }
/* !! r291 [his report, 09/18/2026] -- "This shouldn't be editable if the
   payment was taken and made. Notes can be edited but that should be it."
   A field a record has locked reads as a FACT, not as a box somebody has yet
   to fill in: no white well, no caret, and the cursor says so. It is not
   `disabled`-grey, because the value has to stay legible -- the whole point of
   the panel on a taken payment is to READ it. */
/* `.field input` carries its own border, so the selector has to be at least as
   specific or the well stays and only the weight changes -- which is what the
   first version of this rule did, and it was visible in the screenshot. */
.field input.panel-locked-field {
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--border, #D5D9E0);
  border-radius: 0;
  padding-left: 0;
  cursor: default;
  font-weight: 600;
}
.field input.panel-locked-field:focus { outline: none; box-shadow: none; }
.panel-check { display: inline-flex; align-items: center; gap: 8px; cursor: pointer; max-width: max-content; }
.panel-check input { width: 15px; height: 15px; margin: 0; }

/* ROUND 55 item 11 — the design team on the order screen. */
.order-team-block { margin-bottom: 18px; }
.order-team-grid { display: flex; flex-wrap: wrap; gap: 8px; }
.order-team-cell {
  border: 1px solid var(--border); border-radius: 5px; background: #fff;
  padding: 7px 12px; min-width: 160px;
}
.order-team-cell .otc-role {
  font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--ink-muted);
}
.order-team-cell .otc-name { font-weight: 600; font-size: 13px; }

/* ROUND 55 item 15 — what the order printout is for. */
.opp-opt { display: flex; gap: 10px; align-items: flex-start; padding: 9px 4px; cursor: pointer; }
.opp-opt input { margin-top: 3px; width: 15px; height: 15px; flex-shrink: 0; }
.opp-opt span { display: flex; flex-direction: column; gap: 2px; font-size: 13.5px; }
.opp-opt em { font-style: normal; font-size: 12px; color: var(--ink-muted); }
.opp-opt:hover { background: var(--surface-2, #f7f9fc); border-radius: 5px; }
.opp-rule { border: none; border-top: 1px solid var(--border); margin: 8px 0 2px; }

/* ROUND 55 item 2 — the two company addresses side by side rather than one
   long column of fields to scroll past. */
.addr-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); gap: 18px; }
.addr-col { border: 1px solid var(--border); border-radius: 6px; padding: 12px 14px; background: #fff; }
.addr-head {
  margin: 0 0 8px; font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.06em; color: var(--ink-muted);
}

/* ROUND 56 — stock. */
.stock-alert {
  background: #FFF4E5; border: 1px solid #E0A75E; color: #9A5B00;
  padding: 9px 13px; border-radius: 5px; margin-bottom: 12px;
  font-size: 13px; font-weight: 600;
}
.stock-alert-neg { background: var(--danger-soft); border-color: #E5B8B8; color: var(--danger); }
.stock-table td.num, .stock-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.stock-table tbody tr[data-stock-product] { cursor: pointer; }
.stock-table tbody tr[data-stock-product]:hover { background: var(--surface-2, #f7f9fc); }
.stock-table tr.row-low td { background: #FFFBF4; }
.stock-where { font-size: 12px; color: var(--ink-muted); }
/* r241: the aisle, beside the quantity. Quiet by default -- it is a
   navigation aid, not a number -- and visibly absent when nothing is set, so
   "no shelf" reads as a job to do rather than as missing data. */
.stock-where-one { white-space: nowrap; }
.stock-loc {
  font-variant-numeric: tabular-nums; font-size: 11px; letter-spacing: 0.02em;
  background: #eef1f5; border-radius: 3px; padding: 0 5px; color: #4a5462;
}
.stock-loc-none { background: transparent; color: #a6adb8; font-style: italic; }

/* ---------- r241: the look-before-you-add panel ----------
   One set of rules for both places it is drawn -- the stock row detail and the
   counter lookup card. See stockLookHtml in app.js. */
.look-panel { background: #fff; border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; margin-bottom: 10px; }
.look-nums { display: flex; gap: 20px; flex-wrap: wrap; margin-bottom: 8px; padding-bottom: 8px; border-bottom: 1px solid #EEF1F5; }
.look-num { display: flex; flex-direction: column; line-height: 1.25; }
.look-num em { font-style: normal; font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-muted); }
.look-num strong { font-size: 15px; font-variant-numeric: tabular-nums; }
.look-blocks { display: flex; gap: 22px; flex-wrap: wrap; align-items: flex-start; }
.look-block { min-width: 190px; flex: 1 1 210px; }
.look-block h5 {
  margin: 0 0 4px; font-size: 10px; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--ink-muted); font-weight: 600;
}
.look-list { list-style: none; margin: 0; padding: 0; font-size: 12.5px; }
.look-list li { padding: 2px 0; display: flex; gap: 7px; flex-wrap: wrap; align-items: baseline; }
.look-list strong { font-variant-numeric: tabular-nums; min-width: 22px; }
.look-wh { color: var(--ink); }
.look-loc { font-size: 11px; background: #eef1f5; border-radius: 3px; padding: 0 5px; color: #4a5462; }
.look-loc-none { background: transparent; color: #a6adb8; font-style: italic; }
.look-doc { font-size: 11.5px; color: #5a6472; }
.look-for { color: var(--ink); }
/* A stock PO and a job PO are the same KIND of fact, so they are the same
   kind of chip -- only the colour differs. r241 D9. */
.look-for-stock { color: #235c33; }
.look-for-job { color: #1F3FB5; }
.look-when, .look-status { font-size: 11px; color: var(--ink-muted); }
.look-note { margin: 3px 0 0; font-size: 11.5px; color: var(--ink-muted); }
.look-compact { border: 0; padding: 0; margin: 6px 0 0; background: transparent; }
.look-compact .look-nums { margin-bottom: 6px; padding-bottom: 6px; }

.loc-table { width: 100%; border-collapse: collapse; font-size: 12.5px; background: #fff; }
.loc-table th {
  text-align: left; padding: 4px 6px; border-bottom: 1px solid var(--border);
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink); font-weight: 700;
}
.loc-table td { padding: 3px 6px; border-bottom: 1px solid #EEF1F5; }
.loc-table input { width: 100%; min-width: 54px; font-size: 12.5px; padding: 2px 5px; }
.look-locedit { flex-basis: 100%; margin-top: 10px; }
.stock-detail-row td { background: var(--surface-2, #f4f6fa); padding: 0; }
.stock-detail { padding: 12px 16px; }
.stock-detail-head { font-size: 11.5px; color: var(--ink-muted); margin-bottom: 7px; }
.stock-mv { width: 100%; border-collapse: collapse; font-size: 12.5px; background: #fff; }
.stock-mv th {
  text-align: left; padding: 5px 8px; border-bottom: 1px solid var(--border);
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink); font-weight: 700;
}
.stock-mv td { padding: 5px 8px; border-bottom: 1px solid #EEF1F5; }
.stock-mv td.num, .stock-mv th.num { text-align: right; font-variant-numeric: tabular-nums; }

/* Serial lookup — the warranty question. */
.serial-result {
  border: 1px solid var(--accent); background: #f6f9ff;
  border-radius: 6px; padding: 12px 15px; margin-bottom: 14px;
}
.serial-hit { padding: 5px 0; border-bottom: 1px solid #E4E8EE; font-size: 13px; }
.serial-hit:last-child { border-bottom: none; }
.serial-where { color: var(--ink-muted); font-size: 12px; margin-top: 2px; }
.serial-status {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; padding: 1px 6px; border-radius: 3px; margin-left: 6px;
}
.serial-status.status-in_stock { background: #E3F3E8; color: #24793E; }
.serial-status.status-shipped { background: #EEF3FF; color: var(--accent); }
.serial-status.status-returned { background: #FFF4E5; color: #9A5B00; }

/* ROUND 58 — the four stock numbers. Incoming and allocated are context, not
   alarms, so they read quieter than on-hand and available. */
.cell-incoming { color: var(--accent); }
.cell-alloc { color: #9A5B00; }

/* ROUND 58 — the pull sheet. Where each line is coming from. */
.pull-sheet { margin-bottom: 22px; }
.pull-summary {
  display: flex; gap: 18px; flex-wrap: wrap; padding: 9px 13px; margin-bottom: 10px;
  background: var(--surface-2, #f4f6fa); border: 1px solid var(--border);
  border-radius: 5px; font-size: 13px;
}
.pull-summary .pull-warn { color: var(--danger); }
.pull-table td.num, .pull-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.pull-table tr.row-toorder td { background: #FFFBF4; }
.pull-badge {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.04em; padding: 2px 7px; border-radius: 3px; white-space: nowrap;
}
.pull-badge.pull-stock   { background: #E3F3E8; color: #24793E; }
.pull-badge.pull-part    { background: #FFF4E5; color: #9A5B00; }
.pull-badge.pull-onorder { background: #EEF3FF; color: var(--accent); }
.pull-badge.pull-toorder { background: var(--danger-soft); color: var(--danger); }

/* ROUND 59 — what the holding is worth. */
.stock-value-banner {
  background: #EEF3FF; border: 1px solid var(--accent); color: var(--accent);
  padding: 9px 13px; border-radius: 5px; margin-bottom: 12px; font-size: 13px;
}

/* ROUND 60/61 — ACCOUNTING. */
.acct-tabs { display: flex; gap: 4px; flex-wrap: wrap; margin-bottom: 16px; border-bottom: 1px solid var(--border); }
.acct-tab {
  background: none; border: none; border-bottom: 2px solid transparent;
  padding: 8px 14px; font-size: 13px; cursor: pointer; color: var(--ink-muted);
}
.acct-tab:hover { color: var(--ink); }
.acct-tab.is-active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
.acct-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(360px, 1fr)); gap: 20px; }
.acct-grid-3 { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 10px; margin: 10px 0; }
.acct-stat {
  border: 1px solid var(--border); border-radius: 5px; padding: 9px 13px; background: #fff;
  display: flex; flex-direction: column; gap: 2px;
}
.acct-stat span { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-muted); }
.acct-stat strong { font-size: 16px; font-variant-numeric: tabular-nums; }
.acct-report { margin-bottom: 26px; }
/* r295: the ST-100 block inside the sales-tax report. Ruled off and inset,
   because it is the same money said a second way -- the three boxes he
   transcribes onto the form -- and it must read as a restatement rather than as
   a second set of figures to reconcile. */
.acct-report-sub {
  margin: 16px 0 6px; padding: 14px 16px 10px;
  border: 1px solid var(--border, #D5D9E0); border-left: 3px solid var(--brand, #1F3FB5);
  border-radius: 8px; background: rgba(31, 63, 181, .03);
}
.acct-report-sub h3 {
  font-size: 13px; letter-spacing: .05em; text-transform: uppercase;
  margin: 0 0 10px; color: var(--ink, #1C2127);
}
.acct-report h2 { font-family: var(--font-display, Georgia, serif); font-size: 15px; margin-bottom: 8px; }
.acct-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.acct-table td, .acct-table th { padding: 5px 8px; border-bottom: 1px solid #EEF1F5; }
.acct-table th { text-align: left; font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink); font-weight: 700; }
.acct-table td.num, .acct-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.acct-table .acct-code { font-family: var(--font-mono, monospace); color: var(--ink-muted); width: 52px; }
.acct-table tr.acct-section td {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-muted); padding-top: 12px; border-bottom: none;
}
.acct-table tr.acct-subtotal td { font-weight: 600; border-top: 1px solid var(--border); }
.acct-table tr.acct-total td { font-weight: 700; border-top: 2px solid var(--ink); border-bottom: none; }
.acct-table tr.acct-note td { color: var(--ink-muted); font-size: 11.5px; border-bottom: none; }
.acct-warn {
  background: #FFF4E5; border: 1px solid #E0A75E; color: #9A5B00;
  padding: 9px 13px; border-radius: 5px; margin-bottom: 14px; font-size: 13px;
}
.acct-warn-bad { background: var(--danger-soft); border-color: #E5B8B8; color: var(--danger); }
/* The gap warning NAMES the documents now. A bare count cannot be acted on --
   it is what sent him back to ask which three they were. */
.acct-gap-list { margin: 7px 0 0; padding-left: 18px; }
.acct-gap-list li { margin: 2px 0; }
/* r314 -- the Reports tab: the list in QuickBooks' groups, then one report at
   a time on a white sheet, drawn by report-render.js exactly as it prints. */
.rpt-list { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 16px 22px; }
.rpt-group { border: 1px solid var(--border); border-radius: 8px; background: #fff; padding: 12px 14px 8px; }
.rpt-group h3 { margin: 0 0 6px; font-size: 12px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-muted); }
.rpt-item { display: block; width: 100%; text-align: left; background: none; border: none; border-top: 1px solid #EEF1F5;
  padding: 8px 2px; cursor: pointer; font: inherit; color: var(--ink); }
.rpt-item:first-of-type { border-top: none; }
.rpt-item:hover .rpt-item-name { color: var(--accent); text-decoration: underline; }
.rpt-item-name { display: block; font-weight: 600; font-size: 13.5px; }
.rpt-item-sub { display: block; font-size: 12px; color: var(--ink-muted); margin-top: 2px; }
.rpt-list[hidden], .rpt-view[hidden] { display: none; }
.rpt-bar { display: flex; align-items: center; gap: 14px; margin-bottom: 10px; flex-wrap: wrap; }
.rpt-title { margin: 0; font-family: var(--font-display, Georgia, serif); font-size: 19px; flex: 1 1 auto; }
.rpt-controls { align-items: flex-end; flex-wrap: wrap; justify-content: flex-start; }
.rpt-controls .field { flex: 0 0 auto; min-width: 0; }
.rpt-controls input[type="date"] { width: 160px; }
.rpt-controls select { min-width: 150px; }
.rpt-out-buttons { display: flex; gap: 8px; flex-wrap: wrap; }
.rpt-sheet-wrap { background: #fff; border: 1px solid var(--border); border-radius: 6px; padding: 36px 40px 28px; margin-top: 8px; }
.rpt-sheet-wrap .rp-sheet { max-width: 8in; }
.rpt-sheet-wrap .rp-link { cursor: pointer; }
.rpt-sheet-wrap .rp-link:hover { color: var(--accent); text-decoration: underline; }
.row-inactive td { opacity: 0.55; }
.tag-ok { background: #E3F3E8; color: #24793E; }
.tag-warn { background: #FFF4E5; color: #9A5B00; }
/* r238: goods gone, nothing billed. Red is for money at risk (r206's rule
   about the credit banner), and a counter sale whose material has left with
   no invoice raised against it is exactly that. */
.tag-bad { background: #FBE7E7; color: #A32020; }

/* Journal */
.je { border: 1px solid var(--border); border-radius: 5px; margin-bottom: 9px; background: #fff; }
.je-head {
  display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
  padding: 8px 12px; border-bottom: 1px solid #EEF1F5; font-size: 12.5px;
}
.je-head .je-memo { color: var(--ink-muted); flex: 1; }
.je-lines { width: 100%; border-collapse: collapse; font-size: 12px; }
.je-lines td { padding: 4px 12px; }
.je-lines td.num { text-align: right; font-variant-numeric: tabular-nums; width: 100px; }

/* Bank reconciliation */
.bank-summary {
  display: flex; gap: 22px; flex-wrap: wrap; padding: 10px 14px;
  border-radius: 5px; margin-bottom: 12px; font-size: 13px; border: 1px solid var(--border);
}
.bank-summary.is-ok { background: #E3F3E8; border-color: #7CBF95; color: #1F6B37; }
.bank-summary.is-off { background: #FFF4E5; border-color: #E0A75E; color: #9A5B00; }
.bank-table td.num, .bank-table th.num { text-align: right; font-variant-numeric: tabular-nums; }
.bank-table tr.row-unmatched td { background: #FFFBF4; }

/* ===================================================================
   ROUND 62 [stated]: "the side panel bar make the headers of each
   section a different color easily to read on the blue background."

   Colours chosen for LEGIBILITY on the sidebar's #1F3FB5, not for
   variety: each is a light, high-contrast tint rather than a
   saturated hue, because saturated colours vibrate against a strong
   blue and get harder to read, not easier. A left rule in the same
   colour gives the eye an anchor even in peripheral vision.

   Deliberately NOT red — red means money at risk everywhere else in
   this app, and spending it on a navigation label would weaken it.
   =================================================================== */
.nav-group-header {
  border-left: 3px solid transparent;
  padding-left: 9px;
  border-radius: 0 6px 6px 0;
}
/* r310 [his 17] "The coloring of it all it terrible and hard to see." The
   per-group tints above (ROUND 62) are retired: five pastel colours on a
   saturated blue read as noise, and two groups never had one. Every heading is
   now the same colour -- where you are is shown by the open group and the
   highlighted item, not by the heading's hue. See the r310 sidebar block. */
.nav-group-header:hover { background: rgba(255,255,255,0.08); }

/* ROUND 66 — the lien flag on the home screen. Red because this is money at
   risk with a clock on it, which is exactly what red is reserved for here. */
.home-lien-flags {
  border: 2px solid var(--danger); border-radius: 6px;
  background: var(--danger-soft); padding: 12px 15px; margin-bottom: 20px;
}
.lien-flag-head { font-size: 13px; color: var(--danger); margin-bottom: 10px; }
.lien-flag {
  background: #fff; border: 1px solid #E5B8B8; border-radius: 5px;
  padding: 10px 13px; margin-bottom: 8px;
  display: grid; grid-template-columns: 1fr auto; gap: 6px 14px; align-items: center;
}
.lien-flag:last-child { margin-bottom: 0; }
.lien-flag-main { display: flex; gap: 14px; align-items: baseline; flex-wrap: wrap; cursor: pointer; }
.lien-flag-money { color: var(--danger); font-weight: 700; font-variant-numeric: tabular-nums; }
.lien-flag-days { color: var(--ink-muted); font-size: 12px; }
.lien-flag-sub { grid-column: 1; font-size: 11.5px; color: var(--ink-muted); }
.lien-flag button { grid-row: 1 / span 2; grid-column: 2; }

/* r277 -- PAST DUE, WITH NO LIEN CLOCK RUNNING.
   [stated 09/14/2026] "This should just be an Past Due Invoice, nothing about
   the lien rights." It reuses the flag furniture -- one shape for "something
   on the home screen needs attention" -- but not the colour: red on this
   screen is reserved for money at risk with a deadline on it, and this money
   has no deadline attached to it. Amber is the same ink the lien disclaimer
   already uses, so nothing new was invented for it. */
.home-past-due {
  border-color: #E0A75E;
  background: #FFF4E5;
}
.home-past-due .lien-flag-head { color: #9A5B00; }
.home-past-due .lien-flag { border-color: #E7C79A; }
.home-past-due .lien-flag-money { color: #9A5B00; }

/* The letter modal — rides on .export-modal / .export-dialog, which already
   handle the backdrop, centring and scroll. */
.lien-letter-box { max-width: 760px; width: 92vw; padding: 20px 24px; }
.lien-letter-box h2 { font-family: var(--font-display, Georgia, serif); margin: 0 0 4px; }
.lien-letter-box .export-actions { display: flex; gap: 8px; justify-content: flex-end; margin-top: 14px; }
.lien-letter-box .field-label {
  display: block; font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.05em; color: var(--ink-muted); margin: 10px 0 4px;
}
.lien-letter-box textarea {
  width: 100%; font-family: var(--font-mono, monospace); font-size: 12.5px;
  line-height: 1.55; padding: 10px 12px;
}
.lien-disclaimer {
  background: #FFF4E5; border: 1px solid #E0A75E; color: #9A5B00;
  padding: 8px 12px; border-radius: 4px; margin-bottom: 12px; font-size: 11.5px;
}

/* ---------------------------------------------------------------------------
   ROUND 69 -- ALTERNATES / VALUE ENGINEERING, and warranty evidence.

   [stated] "Alternates" = a very similar product, cost isn't the driver.
   "Value Engineering" = solely cost savings. Today he fakes both with two line
   items, one zeroed out. The badge is the clickable flag that replaces that.

   NO RED HERE. Red means money at risk everywhere else in this app, and an
   alternate that wasn't chosen is a normal part of quoting, not a risk.
--------------------------------------------------------------------------- */
.qe-alt-badge {
  display: inline-block; margin-left: 5px; padding: 0 5px;
  border: 1px solid var(--accent); border-radius: 3px; background: var(--accent-soft);
  color: var(--accent); font-size: 9.5px; font-weight: 700; letter-spacing: 0.04em;
  line-height: 15px; cursor: pointer; vertical-align: middle;
}
.qe-alt-badge:hover { filter: brightness(0.94); }
.qe-alt-ve { border-color: #2F7D55; background: #E3F3E8; color: #24793E; }
.qe-alt-alternate { border-color: #9A5B00; background: #FFF4E5; color: #9A5B00; }
/* A line that is NOT going to be sold: still legible, obviously not scope. */
.qe-alt-muted { opacity: 0.55; }
.qe-row-alt-off td { opacity: 0.6; }
.qe-row-alt-off td input,
.qe-row-alt-off td select { font-style: italic; }

/* The per-item evidence answer on a warranty claim. */
.wr-ev-cell { white-space: nowrap; }
.wr-ev-cell .btn { width: 100%; }

/* ROUND 73 [stated]: "Customer details in quotes looks terrible. Add some
   spaces at least so it's easier to read." Grouped blocks with real vertical
   rhythm, and the panel toggles from the same button that opened it. */
.qe-cust-details { padding: 14px 16px; }
.qe-cust-details-head {
  display: flex; align-items: center; justify-content: space-between;
  margin: 0 0 10px; padding-bottom: 8px; border-bottom: 1px solid var(--border);
}
.qe-cust-details-head h3 { margin: 0; font-size: 14.5px; }
.qe-cust-group:last-of-type { margin-bottom: 6px; }
.qe-cust-group h4 {
  margin: 0 0 6px; font-size: 10px; text-transform: uppercase;
  letter-spacing: 0.07em; color: var(--ink-muted); font-weight: 700;
}
.qe-cust-group .meta-row { padding: 3px 0; }
.qe-cust-addr { font-size: 12.5px; line-height: 1.65; }

/* ROUND 74 [stated]: "you have alllll of this space but you went straight down
   with everything? SO much room is taken up when not needed... Make it clean,
   use less space, condense it. Add some : and spaces."
   Groups flow across the available width instead of one long column, and each
   row is a real two-column label/value pair with a colon and a gap. */
.qe-cust-cols {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(240px, 1fr));
  gap: 4px 28px; align-items: start;
}
.qe-cust-row {
  display: grid; grid-template-columns: minmax(88px, auto) 1fr;
  gap: 0 10px; padding: 1px 0; font-size: 12.5px; line-height: 1.5;
}
.qe-cust-k { color: var(--ink-muted); white-space: nowrap; }
.qe-cust-v { color: var(--ink); word-break: break-word; }
.qe-cust-foot { margin-top: 8px; padding-top: 6px; border-top: 1px solid var(--border); }
.qe-cust-group { margin-bottom: 10px; }

/* ROUND 76: delete affordance on a quote tab, matching the phase tab. */
.qe-nav-delquote { margin-left: 2px; }
.qe-nav-delquote:hover { color: var(--danger); }

/* ROUND 76: collapsible sections on the order screen. The pull sheet is a
   fulfilment step and must not fill the screen above the line items. */
.order-collapse {
  border: 1px solid var(--border); border-radius: 6px;
  margin: 14px 0; background: #fff;
}
.order-collapse > summary {
  cursor: pointer; padding: 9px 14px; font-weight: 600; font-size: 13.5px;
  display: flex; align-items: baseline; gap: 10px; list-style: revert;
}
.order-collapse > summary:hover { background: var(--accent-soft); }
.order-collapse[open] > summary { border-bottom: 1px solid var(--border); }
.order-collapse-body { padding: 10px 14px 14px; }

/* ROUND 77: ship-to destination and its editable address. Compact grid, not a
   column running down the page. */
.order-shipto { margin: 10px 0 14px; }
.order-shipto-choice {
  display: flex; align-items: center; gap: 16px; flex-wrap: wrap; margin-bottom: 8px;
}
.order-shipto-fields {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 8px 14px; padding: 12px 14px;
  border: 1px solid var(--border); border-radius: 6px; background: #fff;
}
.order-shipto-fields .field { margin: 0; }

/* ===========================================================================
   ROUND 77 -- DENSITY PASS
   [stated] "you have alllll of this space but you went straight down with
   everything? SO much room is taken up when not needed. You do this all over
   with rows and columns. Clean it up means tighten it all up too. This looks
   terrible. Make it clean, use less space, condense it."

   Applied at the BASE classes, not per screen, so it reaches every form in
   the app at once rather than the one screen that got complained about.
   Nothing here changes what is on screen -- only how much room it takes.
   =========================================================================== */

/* Label sits tight to its control. 6px of air between a label and its own
   input, repeated down a long form, is most of the scrolling. */

/* Rows of fields: tighter vertically, unchanged horizontally so nothing
   collides. Columns FILL the width available instead of stopping at four. */

/* Section headings stop being a full line of wasted height. */

/* Settings pages: same treatment. */
.settings-grid { gap: 8px 16px; }
.settings-sub { margin-top: 2px; margin-bottom: 6px; line-height: 1.45; }

/* Tables: the line-item grid is where he spends the day. */
.qe-table td { padding: 3px 5px; }
.qe-table thead th { padding: 5px 5px; }
.qe-table input, .qe-table select { padding: 4px 6px; font-size: 12.5px; }

/* Every label/value pair in the app gets a separator and real spacing --
   [stated] "Add some : and spaces too shit is right next to each other." */
.meta-row { display: flex; gap: 10px; justify-content: space-between; padding: 2px 0; }
.meta-label::after { content: ':'; }
.meta-label { color: var(--ink-muted); white-space: nowrap; }
.meta-value { text-align: right; }

/* ROUND 80: per-manufacturer freight entry on the order. */
.order-freight-rows {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
  gap: 8px 16px; margin-bottom: 8px;
}

/* ROUND 85 [stated 3.8]: the Select tool gets a normal arrow. A crosshair
   means "you are about to draw" -- with Select active you are not. */
.pdf-editor-overlay.is-selecting { cursor: default; }
.pdf-editor-overlay.is-selecting .pdf-mark { cursor: move; }

/* ROUND 86 [stated 3.5]: the four submittal line colours, read at a glance.
   Yellow = left out of this submittal. Orange = submitted, waiting.
   Green = approved. Red = rejected or needs updating.
   Tints only -- the row must stay readable, this is a status band not a fill. */
.sub-row-excluded .sub-item-readonly { opacity: 0.6; font-style: italic; }
.sub-status-yellow  { background: #FFF9E0; }
.sub-status-orange  { background: #FFF3E2; }
.sub-status-green   { background: #EDF7F0; }
.sub-status-red     { background: #FBEDEA; }
.sub-row td:first-child { box-shadow: inset 3px 0 0 0 transparent; }
.sub-status-yellow td:first-child { box-shadow: inset 3px 0 0 0 #E0B84E; }
.sub-status-orange td:first-child { box-shadow: inset 3px 0 0 0 #E0A75E; }
.sub-status-green  td:first-child { box-shadow: inset 3px 0 0 0 #4FA96C; }
.sub-status-red    td:first-child { box-shadow: inset 3px 0 0 0 #C4634A; }

/* Blank pages: obviously not a fixture row. */
.sub-row-blank td { background: #F4F6FA; }
.sub-blank-tag {
  display: inline-block; margin-right: 10px; padding: 1px 8px;
  border-radius: 999px; background: #E3E8F2; color: #47536B;
  font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: 0.05em;
}
.sub-row-blank input { width: 60%; }

.sub-row-tools { white-space: nowrap; text-align: right; }
.sub-move, .sub-remove {
  border: 1px solid var(--border); background: #fff; border-radius: 4px;
  cursor: pointer; font-size: 11px; line-height: 1; padding: 3px 6px; margin-left: 2px;
}
.sub-move:disabled { opacity: 0.3; cursor: default; }
.sub-remove:hover { color: var(--danger); border-color: var(--danger); }

/* ROUND 90: the find-cut-sheet button beside the paperclip. */
.sub-find-sheet {
  border: 1px solid var(--border); background: #fff; border-radius: 4px;
  cursor: pointer; font-size: 12px; line-height: 1; padding: 3px 6px; margin-left: 3px;
}
.sub-find-sheet:hover { border-color: var(--primary-light); }
.sub-find-sheet:disabled { opacity: 0.5; cursor: default; }

/* ROUND 90: AI alternate proposals. Deliberately verbose -- "what is NOT the
   same" is the line that stops a rejected submittal. */
.export-dialog-wide { max-width: 720px; }
.qe-alt-ai {
  border: 1px solid var(--border); background: #fff; border-radius: 4px;
  cursor: pointer; font-size: 11px; line-height: 1; padding: 2px 5px; margin-left: 3px;
}
.alt-ai-card {
  border: 1px solid var(--border); border-radius: 6px;
  padding: 10px 12px; margin-bottom: 10px; background: #fff;
}
.alt-ai-head { display: flex; align-items: center; gap: 8px; margin-bottom: 4px; }
.alt-ai-desc { margin: 0 0 4px; font-size: 12.5px; }
.alt-ai-why, .alt-ai-diff { margin: 0 0 3px; font-size: 12px; line-height: 1.45; }
.alt-ai-diff { color: var(--danger); }
.alt-ai-actions { display: flex; gap: 8px; align-items: center; margin-top: 8px; }

/* ROUND 91: the spec sheet library. */
.spec-row {
  display: grid; grid-template-columns: 1fr auto auto; gap: 14px;
  align-items: center; padding: 9px 12px;
  border: 1px solid var(--border); border-radius: 6px;
  background: #fff; margin-bottom: 6px;
}
.spec-row-meta { display: flex; gap: 8px; align-items: center; font-size: 12px; }
.spec-row-actions { display: flex; gap: 6px; }
#spec-search { min-width: 320px; }

/* ROUND 95 [stated 1]: the reorder arrows sit beside the column they move. */
/* ROUND 96 [stated]: the arrows must read as belonging to the column beside
   them. `space-between` pushed them to the far right edge, leaving a wide gap
   -- with several rows stacked, the arrows lined up in their own column and it
   was not obvious which row each pair moved. They now sit directly after the
   name, and the row only takes the width it needs. */
.qe-col-row {
  display: flex; align-items: center; justify-content: flex-start;
  gap: 8px; padding: 3px 6px;
}
.qe-col-label { display: flex; align-items: center; gap: 8px; cursor: pointer; }
.qe-col-name { min-width: 0; }
/* ROUND 98: the arrows are gone -- each visible column carries a grip and is
   dragged into place. The empty grip keeps hidden columns' names aligned. */
.qe-col-grip {
  display: inline-block; width: 16px; text-align: center; flex-shrink: 0;
  color: var(--ink-faint); cursor: grab; font-size: 12px; line-height: 1;
  user-select: none; touch-action: none;
}
.qe-col-grip:hover { color: var(--primary); }
.qe-col-grip-empty { cursor: default; }
.col-row-dragging { opacity: 0.45; }
.col-row-drop-before { box-shadow: inset 0 2px 0 var(--primary); }
.col-row-drop-end { box-shadow: inset 0 -2px 0 var(--primary); }
.qe-columns-saved { font-size: 11.5px; color: #1F6B39; margin-right: 8px; }
/* Selection column, same as the quote grid. */
#co-tbody .col-check, #co-editor th.col-check { width: 30px; text-align: center; }

/* ROUND 95: lot rows on a change order. */
.co-lots-list { display: flex; flex-direction: column; gap: 6px; }
/* r209 [stated]: the CO lot strip gained MFG and Rep quote #, so it reads the
   same as a blank order lot. Two more columns, and the strip wraps rather than
   squeezing every cell on a narrow screen. */
.co-lot-row {
  display: grid;
  grid-template-columns: 1.4fr minmax(180px, auto) auto auto auto auto auto auto auto 28px;   /* r228: +Rep */
  gap: 10px; align-items: center;
}
.co-lot-cell { display: flex; align-items: center; gap: 5px; font-size: 11.5px; }
.co-lot-cell input { width: 110px; }
.co-lot-rep select { width: 140px; }   /* r228 [his 4D] */
/* r228 [his 4E]: the freight pair on the factory-pricing dialog. */
.rfq-pricing-freight { display: flex; gap: 22px; margin: 12px 0 4px; flex-wrap: wrap; }
.rfq-pricing-freight label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--ink-muted); }
.rfq-pricing-freight label > span { font-size: 13.5px; color: var(--ink); font-weight: 600; font-variant-numeric: tabular-nums; padding: 6px 0; }
.rfq-pricing-freight input { width: 150px; }
/* r306: the answer grew a description, a note, a markup and the factory's
   quote number -- ten columns, so the dialog is as wide as the screen allows. */
.rfq-pricing-dialog { max-width: min(1320px, 96vw); }
/* r307 -- the product import. As wide and as tall as the screen allows: the
   preview is the point of it, and a price list is long. */
.import-dialog { max-width: min(1240px, 96vw); height: 92vh; max-height: 92vh; }
.import-body { flex: 1; display: flex; flex-direction: column; gap: 18px; }
.import-body h3 { margin: 0 0 6px; font-size: 14px; }
.import-file { display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px; }
.import-file-name { font-size: 13px; color: var(--ink-muted); }
.import-file-opts { display: flex; flex-wrap: wrap; gap: 10px 20px; width: 100%; }
.import-file-opts[hidden] { display: none; }
.import-file-opts label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--ink-muted); }
/* The rule above is more specific than [hidden], so the one-sheet case needs
   saying: a file with one sheet has no sheet to choose. */
.import-file-opts label[hidden] { display: none; }
.import-file-opts input[type=number] { width: 90px; }
.import-file-opts select { min-width: 200px; }
.import-error { width: 100%; margin: 0; padding: 8px 12px; border-radius: 6px; background: var(--danger-soft); color: var(--danger); font-size: 13px; }
.import-error[hidden] { display: none; }
.import-match[hidden], .import-preview[hidden], .import-done[hidden] { display: none; }
.import-match-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(360px, 1fr)); gap: 6px 22px; }
.import-map-row { display: grid; grid-template-columns: 150px 1fr; align-items: center; gap: 4px 10px; font-size: 13px; }
.import-map-row > span:first-child { font-weight: 600; }
.import-map-row .req { color: var(--danger); }
.import-map-row select { width: 100%; }
.import-map-row .import-sample { grid-column: 2; font-size: 11.5px; color: var(--ink-faint); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-height: 14px; }
.import-counts { display: flex; flex-wrap: wrap; gap: 8px; margin: 2px 0 8px; }
.import-count { border: 1px solid var(--border); border-radius: 999px; padding: 4px 12px; font-size: 12.5px; background: var(--surface); cursor: pointer; }
.import-count strong { font-variant-numeric: tabular-nums; }
.import-count[aria-selected=true] { border-color: var(--primary-light); background: var(--accent-soft); }
.import-table-wrap { border: 1px solid var(--border); border-radius: 6px; overflow: auto; max-height: 46vh; }
.import-table { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.import-table th { position: sticky; top: 0; background: var(--bg); text-align: left; font-weight: 600; padding: 6px 10px; border-bottom: 1px solid var(--border); white-space: nowrap; }
.import-table td { padding: 6px 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
.import-table td.import-n { color: var(--ink-faint); font-variant-numeric: tabular-nums; width: 44px; }
.import-table td.import-st { width: 86px; }
.import-table tr.st-refused td { background: #FCF4F1; }
.import-table .import-why { color: var(--danger); }
.import-table .import-note { display: block; color: var(--ink-muted); font-size: 11.5px; margin-top: 2px; }
.import-table .import-chg { font-variant-numeric: tabular-nums; }
.import-table .import-chg s { color: var(--ink-faint); }
.import-remember { display: flex; align-items: center; gap: 6px; font-size: 12.5px; margin-right: 8px; }
.import-remember[hidden] { display: none; }
.import-done { padding: 14px 16px; border-radius: 6px; background: #E3F3E8; color: #24793E; font-size: 13.5px; }
@media (max-width: 700px) {
  .import-dialog { height: 100vh; max-height: 100vh; }
  .import-map-row { grid-template-columns: 1fr; }
  .import-map-row .import-sample { grid-column: 1; }
}
.rfq-pricing-mfgquote { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; margin: 0 0 12px; }
.rfq-pricing-mfgquote label { display: flex; flex-direction: column; gap: 4px; font-size: 12px; color: var(--ink-muted); }
.rfq-pricing-mfgquote input { width: 200px; }
.rfq-pricing-table td.rfq-pricing-words { min-width: 190px; }
.rfq-pricing-table td.rfq-pricing-mk { width: 74px; }
.rfq-pricing-table th { white-space: nowrap; }
.rfq-pricing-lots { margin-bottom: 14px; }
.rfq-pricing-table td.rfq-in-lot { font-style: italic; text-align: center; }
/* r308: their lot price, and the lines it covers as it is typed. */
.rfq-pricing-lotprice input { width: 160px; }
.rfq-pricing-lotprice .settings-sub { max-width: 640px; }
.rfq-pricing-table tr.rfq-in-their-lot td { background: #F4F8FD; }
.rfq-pricing-legacy, .rfq-pricing-mixed { margin: 4px 0 10px; }
/* r308: a line the factory's lot price covers, beside the client's lot. */
.tag-factory-lot { background: #EAF1FB; color: #2A5594; }
.tag-factory-lot.tag-muted { background: #F1F3F6; color: #69727F; }
.rfq-batch-bar { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin: 6px 0 8px; }
.recon-rfq-table .rfq-tick-col { width: 26px; text-align: center; }
.recon-rfq-table .rfq-tick-col input { width: auto; }
.co-rfq-table { margin-top: 10px; }
/* The typeahead is a block inside a flex cell, so it needs a stated width or
   it collapses to nothing -- the same trap the quote's lot strip hit. */
.co-lot-mfg .mfg-ta { flex: 0 0 150px; }
.co-lot-mfg .mfg-ta-input { width: 100%; }
@media (max-width: 1500px) {
  /* r228: ten cells now (the Rep select joined the strip), so five a row --
     name / MFG / Rep / Rep quote / Cost, then Markup / Price / was / delta /
     remove -- rather than the three-line wrap four-per-row produced. */
  .co-lot-row { grid-template-columns: 1.4fr repeat(4, auto); }
}
.co-lot-was { font-size: 11.5px; color: var(--ink-muted); }
/* ROUND 98: quote options that can be pulled into this change order. */
.co-option-row {
  display: grid; grid-template-columns: 1.4fr auto auto auto;
  gap: 12px; align-items: center; padding: 3px 0;
}
.co-option-name { font-weight: 500; }
.co-option-total { font-variant-numeric: tabular-nums; }
.co-option-lines { font-size: 11.5px; color: var(--ink-muted); }
/* The Was cell names the line it changes: type code + part, then qty x price. */
.co-was-line { font-weight: 500; }

/* ROUND 101 [stated 2.5]: the change order's print/export choices. */
.co-export-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 6px 14px; }
.co-export-opt { display: flex; align-items: center; gap: 7px; font-size: 13px; }

/* ROUND 104: per-user module tick grid on the Users panel. */
.acct-modules { margin: -4px 0 12px; padding: 10px 12px; border: 1px solid var(--line, #d8dbe0); border-radius: 6px; background: #FAFBFC; }
.acct-modules-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(190px, 1fr)); gap: 6px 14px; margin-bottom: 8px; }
.acct-module-tick { display: flex; align-items: center; gap: 7px; font-size: 13px; cursor: pointer; }
.acct-modules-foot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.acct-modules-foot .settings-sub { margin-right: auto; }

/* ROUND 104: the awarded team member tick. Green = won, per the colour rules. */
.team-award-btn { background: none; border: none; cursor: pointer; font-size: 16px; color: #9aa0a8; padding: 0 4px; line-height: 1; }
.team-award-btn.is-awarded { color: #1E7B34; }
.team-awarded-tag { color: #1E7B34; font-size: 11px; font-weight: 600; margin-left: 6px; text-transform: uppercase; letter-spacing: .4px; }

/* ROUND 104: RFI editor -- response card, no-answer ticks, save state, project picker. */
.rfi-response-card { border: 1px solid var(--line, #d8dbe0); border-radius: 6px; padding: 12px; margin-bottom: 24px; background: #FAFBFC; }
.rfi-response-doc-row { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-top: 6px; }
.rfi-response-doc-row .settings-sub { margin-right: auto; }
.rfi-upload-label { cursor: pointer; }
.rfi-no-answer { display: inline-flex; align-items: center; gap: 6px; font-size: 12px; color: #8a4b00; margin-top: 4px; cursor: pointer; }
.btn-unsaved { box-shadow: 0 0 0 2px #C97A00 inset; }
.project-pick-dialog { width: min(560px, 92vw); }
#project-pick-input { width: 100%; padding: 9px 12px; font-size: 15px; border: 1px solid var(--line, #d8dbe0); border-radius: 6px; margin-bottom: 8px; }
.project-pick-list { max-height: 320px; overflow: auto; display: flex; flex-direction: column; gap: 2px; }
.project-pick-row { display: flex; gap: 12px; align-items: baseline; text-align: left; padding: 8px 10px; border: none; background: none; cursor: pointer; border-radius: 5px; font-size: 14px; }
.project-pick-row:hover { background: #EEF1F7; }
/* r315: a list longer than PICK_LIST_MAX says how many it did not draw. */
.pick-more { padding: 6px 10px; margin: 0; font-style: italic; }
/* r315: the cents the invoices charged above taxable x rate, under the tax due. */
.tax-excess-note { display: block; font-size: 11.5px; color: var(--ink-muted); margin-top: 4px; font-weight: normal; }
/* r315: said under a money box left holding text that is not a figure. Fixed,
   so it moves nothing on the page; gone after a few seconds. */
.money-unread-note { position: fixed; z-index: 3000; max-width: 330px; padding: 6px 10px; border-radius: 6px;
  background: var(--danger-soft); color: var(--danger); border: 1px solid var(--danger);
  font-size: 12.5px; line-height: 1.35; box-shadow: 0 2px 8px rgba(0,0,0,.12); pointer-events: none; }
.money-unread-note[hidden] { display: none; }
input.not-a-figure { outline: 2px solid var(--danger); outline-offset: -1px; }
/* r315: Reconcile, Re-open and Print above one statement's period. */
.bank-period-actions { display: flex; gap: 8px; justify-content: flex-end; margin: 6px 0 10px; }
#bank-period-view { margin-bottom: 18px; }
/* r254: the multi-select twin of the picker. A ticked row stays obvious while
   the list is filtered underneath it. */
.project-pick-row.is-picked { background: #E7EEFF; font-weight: 600; }
.project-pick-num { font-weight: 600; color: #1F3FB5; min-width: 74px; }

/* ROUND 104: the service Request-for-Service block. */
.svc-project-row { display: flex; gap: 6px; }
.svc-project-row input { flex: 1; }
.svc-req-row { display: grid; grid-template-columns: 150px 1fr 80px 120px 32px; gap: 6px; align-items: center; }
.svc-request-actions { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0 18px; }
.svc-visit-body { flex: 1; }

/* ROUND 104 [stated 5]: the Design & Service filing tag -- house blue, since
   red means money at risk and green means Won. */
.tag-info { background: #EEF2FB; color: #1F3FB5; }

.settings-sub-head { font-size: 12px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-muted); margin: 16px 0 6px; }

/* ROUND 104 [stated 9]: the QuickBooks connect walkthrough. */
.settings-steps { margin: 6px 0 10px 20px; padding: 0; font-size: 13px; line-height: 1.7; }

/* ROUND 105 [stated 5]: the quotes module's three sections. Each is a card
   with its own header, count, and filter -- a redesign of the view, not a
   grouped list. */
.section-wrap { display: flex; flex-direction: column; gap: 18px; }
.qsection { background: var(--surface, #fff); border: 1px solid #E4E6EA; border-radius: 8px; overflow: hidden; }
.qsection-head { display: flex; align-items: center; gap: 12px; padding: 11px 14px; border-bottom: 1px solid #E4E6EA; background: #FAFBFC; }
.qsection-head h2 { margin: 0; font-size: 14px; letter-spacing: 0.03em; text-transform: uppercase; color: #1F3FB5; }
.qsection-count { font-size: 12px; color: #777; }
.qsection-filter { margin-left: auto; }
.qsection-filter input { width: 210px; padding: 5px 9px; border: 1px solid #C9CDD3; border-radius: 4px; font-size: 13px; }
.qsection-body table { width: 100%; border-collapse: collapse; }
.qsection-empty { padding: 14px; color: #888; font-size: 13px; }

/* ROUND 105 [stated 14]: the favourite-manufacturer picker. */
.fav-list { max-height: 340px; overflow-y: auto; border: 1px solid #E4E6EA; border-radius: 6px; margin-top: 8px; }
.fav-row { display: flex; align-items: center; gap: 10px; padding: 6px 12px; border-bottom: 1px solid #F0F1F4; font-size: 13px; }
.fav-row:last-child { border-bottom: 0; }
.fav-row.is-fav { background: #F7F9FF; }
.fav-star { margin-left: auto; color: #1F3FB5; font-size: 12px; }

/* ROUND 105 [stated 4]: phones one column, emails stacked in another. */
.cust-contact-split { display: grid; grid-template-columns: 1fr 1fr; gap: 0 28px; margin-bottom: 14px; }
.cust-contact-col { display: flex; flex-direction: column; gap: 10px; }
@media (max-width: 860px) { .cust-contact-split { grid-template-columns: 1fr; } }

/* ROUND 105 [stated 12]: several types per company, ticked. */
.cust-types { display: flex; flex-wrap: wrap; gap: 6px 14px; padding: 6px 0; }
.cust-types label { display: flex; align-items: center; gap: 5px; font-size: 13px; }

/* ROUND 105 [stated 9]: the per-manufacturer freight section. */
.qe-freight-section { margin-top: 18px; }
.qe-freight-list { border: 1px solid #E4E6EA; border-radius: 6px; overflow: hidden; }
.qe-freight-row { display: grid; grid-template-columns: 1.6fr 1.4fr 1fr auto; gap: 12px; align-items: center; padding: 8px 12px; border-bottom: 1px solid #F0F1F4; font-size: 13px; }
.qe-freight-row:last-child { border-bottom: 0; }
.qe-freight-row.is-overridden { background: #FFF8E8; }
.qe-freight-terms { color: #777; }
.qe-freight-empty { padding: 12px; color: #888; font-size: 13px; }

/* ROUND 105 [stated 6]: the status bar at the top of a quote -- the gate to
   everything downstream, so it reads as a control, not a field. */
.qe-status-bar { display: flex; align-items: center; gap: 12px; margin: 0 0 16px; padding: 10px 14px;
  background: #EEF2FB; border: 1.5px solid #1F3FB5; border-radius: 6px; }
.qe-status-bar label { font-weight: 600; font-size: 13px; text-transform: uppercase; letter-spacing: 0.04em; color: #1F3FB5; }
.qe-status-bar select { font-size: 15px; font-weight: 600; padding: 6px 10px; border: 1px solid #A9B7E8; border-radius: 4px; background: #fff; }
.qe-status-hint { font-size: 12px; color: #4A5568; }

/* ROUND 105 [stated 2]: the brand image slots. */
.brand-slot { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.brand-preview { max-height: 46px; max-width: 150px; border: 1px solid #E4E6EA; border-radius: 4px; background: #fff; padding: 3px; }
.brand-preview-sm { max-height: 32px; max-width: 32px; }

/* ROUND 105 BUG [stated 3, his find]: "once you click projects then clicking
   on any other module only shows the projects module but with the clicked on
   modules title."
   CAUSE: an author rule with `display: flex` BEATS the browser's built-in
   [hidden]{display:none}. #section-wrap (the quotes screen's three sections)
   therefore never actually hid when you navigated away -- every later module
   set its own title and painted its rows into the table underneath, while the
   quotes sections stayed on screen on top of it.
   Every one of this round's new flex/grid containers that is ever hidden gets
   its own [hidden] rule. The two-line fix; the same trap the .panel rule
   above was written for. */
.section-wrap[hidden] { display: none !important; }
.svc-project-row[hidden] { display: none !important; }
.cust-contact-split[hidden] { display: none !important; }
.brand-slot[hidden] { display: none !important; }
.qe-status-bar[hidden] { display: none !important; }


/* ROUND 105 -- the rest of the [hidden] landmines, found by tests/check-hidden.js
   the moment it was written. Each of these sets `display`, which beats the
   browser's [hidden]{display:none}; each is hidden somewhere in the app. They
   had not surfaced yet only because of the order screens happen to open in.
   The sweep now fails if any container gains a display rule without one. */
.co-freight-stack[hidden] { display: none !important; }
.co-lots-list[hidden] { display: none !important; }
.cust-contacts-list[hidden] { display: none !important; }
.order-collapse[hidden] { display: none !important; }
.order-freight-rows[hidden] { display: none !important; }
.order-shipto-fields[hidden] { display: none !important; }
.order-signed-block[hidden] { display: none !important; }
.proj-tax-fields[hidden] { display: none !important; }
.qe-options-list[hidden] { display: none !important; }
.qe-po-list[hidden] { display: none !important; }
/* r227: two more, both asked for by name by the [hidden] sweep.
   `#sub-transmittal-section` gained a `display` rule this round (the
   narrowed transmittal line), and `.settings-tag-list` has always been a
   flex container -- a flex container that something hides still shows,
   because `display: flex` beats the `hidden` attribute. The sweep is right
   about both. */
#sub-transmittal-section[hidden] { display: none !important; }
.settings-tag-list[hidden] { display: none !important; }
.sale-marker[hidden] { display: none !important; }
.settings-tabs[hidden] { display: none !important; }
.sign-link-box[hidden] { display: none !important; }
#qe-tax-exempt-note[hidden] { display: none !important; }

/* ============ ROUND 105 -- CRM LAYOUT [stated 1.2, 1.3, 1.5, 1.7] ============ */

/* [1.2] Phones and emails were stretching the full column width. A phone
   number is 14 characters; an email rarely more than 40. */
#cust-phone, #cust-phone-additional { max-width: 190px; }
#cust-email, #cust-email-quotes, #cust-email-billing, #cust-email-orders { max-width: 320px; }

/* [1.3] Terms is a number of days -- it needs four characters, not a third of
   the screen. Notes is where the actual writing happens. */
#cust-terms-days { max-width: 110px; }
#cust-credit-limit { max-width: 160px; }
#cust-tax-rate { max-width: 130px; }
.cust-notes-top textarea { width: 100%; min-height: 96px; }

/* [1.7] "The shipping address boxes go right to the edge of the page, there
   should be space to the right before that."
   The grid's two 1fr columns were being sized by their CONTENT, not by the
   box -- a grid item's default min-width is auto, so the widest field pushed
   the column out and the whole editor overflowed the window. minmax(0, 1fr)
   is what actually holds it in; the padding then gives the gutter. */
.cust-address-grid {
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  padding-right: 28px;
  max-width: 100%;
}
.cust-address-block { min-width: 0; }
.cust-address-block input { max-width: 100%; box-sizing: border-box; }
.cust-address-block .qe-fields-row-3 {
  display: grid;
  grid-template-columns: minmax(0, 2fr) minmax(0, 1fr) minmax(0, 1fr);
  gap: 10px;
}

/* [1.5] "Put lines separating the different sections - the layout is
   annoying." Every section heading in the customer editor now sits on its own
   ruled line, so the eye can find where one thing ends and the next starts. */
#customer-editor .qe-lines-head {
  border-top: 1px solid #E4E6EA;
  margin-top: 22px;
  padding-top: 14px;
}
#customer-editor .qe-lines-head:first-of-type { border-top: 0; margin-top: 0; }
#customer-editor .cust-notes-top { padding-bottom: 18px; border-bottom: 1px solid #E4E6EA; }

/* ROUND 105 [stated 1.10]: the main-type star. */
.cust-type-item { display: inline-flex; align-items: center; gap: 3px; margin-right: 14px; }
.cust-type-star { background: none; border: 0; cursor: pointer; font-size: 15px; line-height: 1; color: #C8CCD4; padding: 0 2px; }
.cust-type-star.is-main { color: #E0A419; }
.cust-type-star:disabled { opacity: 0.3; cursor: default; }
.tag-main { background: #FFF4DA; border-color: #E7C777; }

/* ROUND 105 [stated 2]: the quotes section toggle. */
.qsection-toggle { display: flex; gap: 6px; margin-bottom: 2px; }
.qtoggle { padding: 6px 14px; border: 1px solid #C9CDD3; background: #fff; border-radius: 999px;
  font-size: 13px; cursor: pointer; color: #4A5568; }
.qtoggle:hover { border-color: #1F3FB5; }
.qtoggle.is-on { background: #1F3FB5; border-color: #1F3FB5; color: #fff; font-weight: 600; }

/* ROUND 106 [stated 9]: the make-default row under the export ticks. */
.export-defaults-row { display: flex; align-items: center; gap: 8px; margin-top: 10px;
  padding-top: 10px; border-top: 1px solid #E4E6EA; flex-wrap: wrap; }

/* ROUND 107 [stated 4]: the attachment options inside the email dialog. */
.email-attach-opts { margin: 10px 0; border: 1px solid #E4E6EA; border-radius: 6px; padding: 8px 10px; }
.email-attach-opts summary { cursor: pointer; font-size: 13px; font-weight: 600; }
.email-attach-opts .export-options-grid { margin-top: 8px; }


/* ROUND 108: the order's lot list is a flex container, so hiding it needs
   saying explicitly -- caught by the hidden-element sweep. */
.qe-lots-list[hidden] { display: none !important; }

/* [his 3] the tax default sits under the field it resets, as a button. */
.order-tax-default-btn { width: auto !important; display: inline-block; margin-top: 6px; }

/* Both new buttons set their own display, so hiding them needs saying
   explicitly -- caught by the hidden-element sweep. */
.order-refill-btn[hidden], .order-tax-default-btn[hidden] { display: none !important; }

.export-options-grid[hidden] { display: none !important; }

/* ROUND 108 [his screenshot]: Client PO sits in the space to the right of
   "Ship To Site?", compact -- not stretched across the screen. */
.order-dropship-row { display: flex; align-items: center; gap: 40px; flex-wrap: wrap; }
.order-clientpo-field { flex: 0 0 auto; }
.order-clientpo-field input { width: 220px; }
.order-clientpo-field label { display: block; }
.order-dropship-row[hidden], .order-clientpo-field[hidden] { display: none !important; }

/* [his 1] the activity trail on a document. */
.order-activity { margin: 14px 0; }
.order-activity-list { display: flex; flex-direction: column; gap: 2px; }
.order-activity-list[hidden] { display: none !important; }
.activity-row { display: grid; grid-template-columns: 150px 200px 1fr 1fr; gap: 10px;
  padding: 4px 8px; border-bottom: 1px solid var(--line, #e6e8eb); font-size: 13px; }
.activity-when { color: var(--ink-muted); }
.activity-what { font-weight: 600; }

/* [his instruction] every save button confirms it saved. */
.saved-chip { display: inline-block; margin-left: 8px; padding: 2px 8px;
  border-radius: 10px; background: #e8f5ec; color: #14743d;
  font-size: 12px; font-weight: 700; white-space: nowrap; vertical-align: middle; }
.saved-chip[hidden] { display: none !important; }

/* [his 1] the re-fill control is a button, not a full-width bar. */
.order-refill-btn { width: auto !important; display: inline-block !important;
  flex: 0 0 auto; align-self: flex-start; }

/* [his 1] the option's own total, as a quote shows one. */
.qe-option-total { display: flex; justify-content: flex-end; gap: 14px;
  padding: 6px 10px; border-top: 1px solid var(--line, #e6e8eb); font-size: 13px; }
.qe-option-total[hidden] { display: none !important; }

/* [his] the address suggestion list. */
.addr-ac-box { position: absolute; z-index: 4000; background: #fff;
  border: 1px solid var(--line, #d5d8dd); border-radius: 6px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.12); max-height: 240px; overflow-y: auto; }
.addr-ac-box[hidden] { display: none !important; }
.addr-ac-item { padding: 7px 10px; font-size: 13px; cursor: pointer; }
.addr-ac-item:hover { background: #eef2f7; }

.addr-ac-note { padding: 8px 10px; font-size: 12px; color: #8a3a3a; background: #fdf3f3; }

/* [his] change orders listed on the order they belong to. */
.order-cos { margin: 14px 0; }
.order-co-list { display: flex; flex-direction: column; gap: 2px; }
.order-co-list[hidden] { display: none !important; }
.order-co-row { display: grid; grid-template-columns: 90px 150px 1fr 110px 130px;
  gap: 10px; align-items: center; padding: 6px 8px; cursor: pointer;
  border-bottom: 1px solid var(--line, #e6e8eb); font-size: 13px; }
.order-co-row:hover { background: #f3f5f8; }
.order-co-num { font-weight: 700; }
.order-co-amount { text-align: right; font-variant-numeric: tabular-nums; }
.order-co-counts { color: var(--ink-muted); font-size: 12px; }

/* [his] a change order's value on the order, and whether it counts yet. */
.qe-po-amount { margin-left: auto; font-variant-numeric: tabular-nums; font-weight: 600; }
.qe-po-counts { color: var(--ink-muted); font-size: 12px; min-width: 118px; text-align: right; }

/* [his 2] change orders listed under the order they belong to. */
.co-child-row { background: #fafbfc; cursor: pointer; font-size: 13px; }
.co-child-row:hover { background: #eef2f7; }
.co-child-indent { padding-left: 26px !important; color: var(--ink-muted); white-space: nowrap; }
.co-child-reason { margin-right: 10px; }
.co-child-amount { text-align: right; font-variant-numeric: tabular-nums; }

/* [his] the detail buttons sit under the order's customer / project line. */
.order-detail-links { display: flex; gap: 8px; align-items: center; margin-top: 4px; flex-wrap: wrap; }
.co-detail-buttons { display: flex; gap: 8px; margin: 8px 0; }

/* [his, r110] the ID box inside a customer-type tag */
.settings-tag .type-id-input {
  width: 58px; margin-left: 8px; padding: 1px 6px; font-size: 12px;
  border: 1px solid var(--border, #d5d9e0); border-radius: 3px; background: #fff;
}

/* r231 [his item 4] -- the PO purpose list in Settings. The name is the input
   because the rename IS the edit; a separate pencil would be a second control
   for the same act. */
.settings-tag .purpose-name-input {
  width: 118px; padding: 1px 6px; font-size: 12.5px; font-weight: 600;
  border: 1px solid transparent; border-radius: 3px; background: transparent;
  color: inherit;
}
.settings-tag .purpose-name-input:hover { border-color: var(--border, #d5d9e0); background: #fff; }
.settings-tag .purpose-name-input:focus { outline: none; border-color: var(--accent); background: #fff; }
.settings-tag-retired { opacity: 0.6; }
.settings-tag-note { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em; }

/* r231 [his item 4] -- WHY THE PO WAS RAISED, IN THE LIST.
   "Why we ordered it should be visually seen." Each purpose gets its own
   colour so a column of them reads at a glance rather than being read word by
   word. Unknown/added purposes fall back to the neutral chip -- a new purpose
   he adds in Settings must still look like a chip, not like broken markup. */
.po-purpose-tag {
  display: inline-block;
  font-size: 11px;
  font-weight: 600;
  padding: 2px 9px;
  border-radius: 999px;
  white-space: nowrap;
  background: #EEECE4;
  color: #5C5849;
  border: 1px solid rgba(0, 0, 0, 0.06);
}
.po-purpose-tag.po-purpose-project     { background: #E7ECFA; color: #1F3FB5; }
.po-purpose-tag.po-purpose-order       { background: #E3F0E7; color: #24793E; }
.po-purpose-tag.po-purpose-stock       { background: #FBF0DC; color: #8A5A17; }
.po-purpose-tag.po-purpose-sample      { background: #F0E9F6; color: #6B3F97; }
.po-purpose-tag.po-purpose-replacement { background: #F6E4DE; color: #B84A2F; }

/* [his r119] System Logs tables inside Settings. */
.settings-log-wrap { max-height: 420px; overflow: auto; border: 1px solid var(--border, #d5d9e0); border-radius: 6px; margin-top: 8px; }
.settings-log { width: 100%; border-collapse: collapse; font-size: 12.5px; }
.settings-log th { position: sticky; top: 0; background: #f6f7f9; text-align: left; font-weight: 700;
  padding: 6px 8px; border-bottom: 1px solid var(--border, #d5d9e0); white-space: nowrap; }
.settings-log td { padding: 5px 8px; border-bottom: 1px solid #eef0f3; vertical-align: top; }
.settings-log tr:last-child td { border-bottom: 0; }
.settings-log td { white-space: nowrap; max-width: 320px; overflow: hidden; text-overflow: ellipsis; }
.settings-log .log-err { display: inline-block; max-width: 200px; overflow: hidden; text-overflow: ellipsis;
  vertical-align: bottom; white-space: nowrap; }

/* r288 [his words] "allllll this space on the right of the logs but have to
   scroll to see them-- fix that". The System Logs cards were held to the 760px
   every Settings card has, and every cell was cut off at 320px with nowrap --
   so the one column worth reading, "What changed", ran off the right edge of a
   card with half the screen empty beside it. These two cards take the width
   they are given, and the long columns wrap instead of hiding. Scoped to the
   two log cards: .settings-log is also the service-charge table (r255). */
.settings-section.settings-section-wide { max-width: none; }
.settings-log-wrap.settings-log-full { max-height: calc(100vh - 240px); min-height: 260px; overflow-x: hidden; }
.settings-log-full .settings-log td { white-space: normal; max-width: none; overflow: visible;
  text-overflow: clip; overflow-wrap: break-word; }
/* The short columns (when, who, what, where) never break; only the last,
   long one may break inside a word -- "anywhere" on every column let the
   table squeeze "program" into "progra / m" (found on the rendered page). */
.settings-log-full .settings-log td:nth-child(-n+4) { white-space: nowrap; }
.settings-log-full .settings-log td:last-child { overflow-wrap: anywhere; }
/* r291: the EMAIL log's second and third columns are ADDRESSES, and an email
   that went to four people is one long unbreakable line -- which pushed the
   whole table sideways inside a box that hides its overflow, so the Subject
   and Status columns simply could not be read (r288's H2, which found it).
   Addresses wrap; the When and Document columns beside them still do not. */
#syslog-email-list .settings-log td:nth-child(2),
#syslog-email-list .settings-log td:nth-child(3) { white-space: normal; overflow-wrap: anywhere; }
.settings-log-full .settings-log .log-err { display: inline; max-width: none; white-space: normal; }

/* [his r125] the PO screen reads in sections rather than one wall of fields. */
.po-section { font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--ink-muted, #6B7078); margin: 18px 0 2px; padding-top: 12px;
  border-top: 1px solid var(--border, #e2e5ea); }
#po-editor .po-section:first-of-type { border-top: 0; padding-top: 0; margin-top: 6px; }

/* [his r126] the three PO note boxes side by side -- narrower, taller. */
.po-notes-row { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 14px; }
.po-notes-row textarea { min-height: 96px; resize: vertical; }

/* [his r126] the lot rows that pulled over from the order. */
.po-lots-wrap { margin: 14px 0 6px; }
.po-lot-row { display: grid; grid-template-columns: 200px 1fr 120px; gap: 12px; align-items: baseline;
  padding: 7px 10px; border: 1px solid var(--border, #e2e5ea); border-radius: 6px; margin-bottom: 6px;
  background: #fbfbfd; }
.po-lot-row .po-lot-name { font-weight: 700; }
.po-lot-row .po-lot-members { color: var(--ink-muted, #6B7078); font-size: 12.5px; }
.po-lot-row .po-lot-cost { text-align: right; font-weight: 700; }

/* [his r127] the PO's short fields sit together instead of one per row. */
.po-narrow-row { display: grid; grid-template-columns: repeat(4, minmax(150px, 260px)); gap: 14px; }
.po-detail-row { margin: 10px 0 4px; display: flex; gap: 10px; }
.qe-cust-block { padding: 4px 0; }
.qe-cust-block + .qe-cust-block { border-top: 1px solid var(--border, #e2e5ea); margin-top: 8px; padding-top: 8px; }

/* [his r128] the vendor invoice's reference strip and inline payment row. */
.vinv-ref-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); gap: 10px 18px;
  padding: 10px 12px; border: 1px solid var(--border, #e2e5ea); border-radius: 8px; background: #fbfbfd; margin-bottom: 6px; }
.vinv-ref { display: flex; flex-direction: column; gap: 1px; }
.vinv-ref-lbl { font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em; color: var(--ink-muted, #6B7078); }
.vinv-pay-form { align-items: end; margin: 8px 0 10px; padding: 10px 12px; border: 1px solid var(--border, #e2e5ea);
  border-radius: 8px; background: #fbfbfd; }
.vinv-pay-actions { display: flex; gap: 8px; align-items: end; }

/* [his r128] the vendor invoice's own field rows: five across so the due date
   doesn't wrap onto a line of its own, and the payment buttons sit side by
   side rather than stacking. */
#vinv-editor .po-narrow-row { grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); max-width: 1200px; }
#vinv-editor .po-narrow-row .field { max-width: 260px; }
.vinv-pay-actions { flex-direction: row; }
.vinv-pay-form .vinv-pay-actions .btn { white-space: nowrap; }

/* A grid row still paints when `hidden` is set -- display beats the attribute.
   The vendor invoice's payment row depends on this. */
.qe-fields[hidden], .po-narrow-row[hidden], .po-notes-row[hidden], .vinv-pay-form[hidden] { display: none !important; }
.po-rep-chip { font-size: 12.5px; color: var(--ink-muted, #6B7078); margin-right: 6px; align-self: center; }

/* [his r131] a document that isn't yours reads as a document, not a form. */
.is-readonly input:disabled, .is-readonly select:disabled, .is-readonly textarea:disabled {
  background: #fbfbfd; color: #3c4148; -webkit-text-fill-color: #3c4148; opacity: 1; cursor: default;
}
.owner-banner { margin-bottom: 12px; }

/* [scale, r132] the paging line under a list. */
.list-page-footer { display: flex; gap: 12px; align-items: center; justify-content: flex-end;
  padding: 10px 4px 2px; }
.list-page-footer[hidden] { display: none !important; }

/* [his r131] the sent-copies panel on a document. */
.sent-copies { margin: 18px 0 6px; }
.sent-copies[hidden] { display: none !important; }

/* [his r136] an unprotected vault is a real problem, not a status note. */
.secrets-bad { color: #A32020; display: block; line-height: 1.5; }

/* [his r138] every list header sorts and drags. */
th[data-col-index] { cursor: grab; }
th.th-dragging { opacity: .5; }
th.th-drop { box-shadow: inset 3px 0 0 #1F3FB5; }

/* [his r142] the Mfg column on every lot row.
   ROUND 208 [his 3]: it holds the typeahead now, not a <select>. A block
   child inside a flex row collapses to nothing without an explicit basis,
   so the width is stated here rather than inherited from the old control. */
.qe-lot-mfg { flex: 0 0 170px; min-width: 130px; }
.qe-lot-row .qe-lot-mfg .mfg-ta-input { flex: none; width: 100%; }

/* ==========================================================================
   ROUND 206 -- PRICING TIERS + COUNTER SALES
   ========================================================================== */

/* Settings > Pricing tiers ------------------------------------------------ */
.tier-table-wrap { overflow-x: auto; margin-bottom: 12px; }
.tier-table { width: 100%; border-collapse: collapse; font-size: 13px; }
.tier-table th {
  text-align: left; font-weight: 600; font-size: 11px; letter-spacing: .04em;
  text-transform: uppercase; color: #5a6472; padding: 6px 8px;
  border-bottom: 1px solid #dfe3e8; vertical-align: bottom;
}
.tier-table .tier-th-sub { text-transform: none; letter-spacing: 0; font-weight: 400; color: #8c94a0; }
.tier-table td { padding: 4px 8px; border-bottom: 1px solid #eef1f4; }
.tier-table th.tier-num, .tier-table td.tier-num { text-align: right; }
.tier-table th.tier-mid, .tier-table td.tier-mid { text-align: center; }
.tier-table input.tier-in { width: 100%; padding: 5px 7px; }
.tier-table input.tier-pct { width: 82px; text-align: right; }
.tier-table input.tier-code { width: 70px; }
/* A retired tier stays readable -- its history still points at it. */
.tier-table tr.tier-off td { opacity: .5; }
.tier-table tr.tier-off input[type="checkbox"] { opacity: 1; }
#tier-example {
  border-top: 1px dashed #dfe3e8; padding-top: 8px; margin-top: 4px; line-height: 1.9;
}

/* The walk-in tick disables the account fields rather than just labelling
   them -- a greyed box says "this does not apply" better than a sentence. */
#cust-pricing-row input:disabled, #cust-pricing-row select:disabled,
#cust-terms-days:disabled, #cust-credit-limit:disabled, #cust-account-number:disabled {
  background: #f2f4f7; color: #8c94a0; cursor: not-allowed;
}

/* Counter sale screen ----------------------------------------------------- */
.counter-cust-row { display: flex; align-items: center; gap: 10px; }
.counter-cust-name { font-weight: 600; font-size: 15px; }

/* ---------- r254 [his 1]: a standard search, not a popup ---------- */
.counter-search {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  padding: 10px 12px; background: #f7f9fc; border: 1px solid #e2e7ee;
  border-radius: 6px 6px 0 0; margin-bottom: 0;
}
#counter-search-box { flex: 1 1 340px; min-width: 220px; font-size: 15px; padding: 8px 10px; }
#counter-search-qty { width: 80px; text-align: right; padding: 8px 10px; }
#counter-search-note { flex-basis: 100%; font-variant-numeric: tabular-nums; }

/* The results. A LIST, not a dialog -- it never covers the sale behind it and
   nothing has to be dismissed. Bounded and scrollable so a broad term cannot
   push the line grid off the screen. */
.counter-search-results {
  border: 1px solid #e2e7ee; border-top: 0; border-radius: 0 0 6px 6px;
  background: #fff; max-height: 340px; overflow-y: auto; margin-bottom: 12px;
}
.counter-result {
  display: block; width: 100%; text-align: left; background: none; cursor: pointer;
  border: 0; border-bottom: 1px solid #eef1f5; padding: 8px 12px; font: inherit;
}
.counter-result:last-child { border-bottom: 0; }
.counter-result:hover, .counter-result:focus { background: #eef4ff; outline: none; }
.cr-main { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap; }
.cr-part { font-weight: 700; font-size: 14px; }
.cr-make { color: #5a6472; font-size: 12.5px; }
.cr-cat {
  font-size: 11px; background: #eef1f5; color: #5a6472; border-radius: 10px; padding: 1px 8px;
}
.cr-desc { display: block; font-size: 12.5px; color: #3b434f; margin: 1px 0 3px; }
.cr-serial {
  display: inline-block; font-size: 11.5px; color: #235c33; background: #e3f0e6;
  border-radius: 3px; padding: 1px 6px; margin-bottom: 3px;
}
.cr-nums { display: flex; gap: 16px; flex-wrap: wrap; font-size: 12.5px; font-variant-numeric: tabular-nums; }
.cr-num em { font-style: normal; color: #8a929e; font-size: 10.5px; text-transform: uppercase;
             letter-spacing: 0.04em; margin-right: 5px; }
.cr-pays { font-weight: 700; color: #1F3FB5; }
.cr-stock { display: flex; gap: 6px; flex-wrap: wrap; align-items: center; margin-top: 3px; font-size: 12px; }
.cr-onhand { font-weight: 600; }
.cr-onhand.has { color: #235c33; }
.cr-onhand.none { color: #a6adb8; font-weight: 400; font-style: italic; }
.counter-result-empty { padding: 10px 12px; font-size: 12.5px; color: #5a6472; margin: 0; }

/* His worked example lives in this card: list, typical sell, cost, what THIS
   customer pays, and which building has it. */
.counter-lookup-card {
  border: 1px solid #d7e0ee; border-left: 3px solid #1F3FB5;
  background: #fff; border-radius: 5px; padding: 10px 12px; margin-bottom: 12px;
}
.counter-card-head { margin-bottom: 6px; display: flex; gap: 10px; flex-wrap: wrap; align-items: baseline; }
.counter-card-nums { display: flex; gap: 18px; flex-wrap: wrap; font-size: 13px; margin-bottom: 6px; }
.counter-card-nums strong { font-variant-numeric: tabular-nums; }
.counter-card-price { padding-left: 14px; border-left: 1px solid #e2e7ee; }
.counter-card-price strong { color: #1F3FB5; font-size: 15px; }
.counter-card-price em { font-style: normal; margin-left: 6px; font-size: 12px; }
.counter-card-stock { font-size: 12px; display: flex; gap: 6px; flex-wrap: wrap; align-items: center; }
.counter-stock-chip {
  background: #eef1f5; border-radius: 10px; padding: 1px 9px; color: #5a6472;
}
.counter-stock-chip.has { background: #e3f0e6; color: #235c33; }
/* r241: the aisle inside the stock chip -- "MAIN: 12 A-2-3-B4". */
.chip-loc { font-style: normal; font-size: 11px; opacity: 0.85; margin-left: 3px; }

/* ---------- r241: scan to line ---------- */
.counter-scan { display: flex; gap: 10px; align-items: center; margin: 6px 0 4px; flex-wrap: wrap; }
.counter-scan label {
  font-size: 10px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--ink-muted); font-weight: 600;
}
.counter-scan input { flex: 1 1 320px; max-width: 460px; }
/* The row that a scan just added, so it is obvious what landed while the
   focus stays in the scan box. Fades rather than sticks -- a permanent mark
   would accumulate down a pallet's worth of scanning. */
@keyframes just-scanned { from { background: #e3f0e6; } to { background: transparent; } }
tr.row-just-scanned td { animation: just-scanned 1.2s ease-out; }
/* The two read-only counter cells r241 adds. Quiet, because neither is
   something to type into. */
.qe-loc-cell, .qe-serial-cell {
  font-size: 11.5px; font-variant-numeric: tabular-nums; color: #4a5462;
  display: inline-block; padding: 2px 4px;
}
.qe-loc-none { color: #a6adb8; font-style: italic; }
/* r241: job or stock, on the PO list and the PO screen. */
.po-for-chip {
  font-size: 11px; border-radius: 10px; padding: 1px 9px; white-space: nowrap;
  background: #eef1f5; color: #5a6472;
}
.po-for-chip.po-for-stock { background: #e3f0e6; color: #235c33; }
.po-for-chip.po-for-job { background: #e6ecfb; color: #1F3FB5; }

/* r239: `.counter-table` and its `.cline` cells are gone. The counter grid is
   a `.qe-table` now, so it inherits the dense editor styling, the sticky
   header and -- the point of the round -- `th { position: relative }`, which
   is what a resize handle needs to exist at all. Leaving these rules behind
   would be four selectors matching nothing, which is how a stylesheet stops
   being readable. */
.counter-totals { margin-bottom: 6px; }

.counter-sign-row { display: flex; gap: 20px; flex-wrap: wrap; align-items: flex-start; margin-bottom: 10px; }
.counter-sign-side { flex: 1 1 280px; min-width: 240px; }
.counter-sig-img { max-height: 90px; display: block; margin-bottom: 12px; }
/* A `display` rule BEATS the hidden attribute -- the recurring trap, and the
   check-hidden sweep caught this one the moment it was written. */
.counter-sig-img[hidden] { display: none !important; }
.counter-qr { margin-bottom: 8px; }
.counter-qr img { display: block; }
.counter-link-input { width: 100%; font-size: 12px; padding: 6px 8px; }

.counter-blockers { margin: 10px 0; }
.counter-blockers ul { margin: 0; padding-left: 18px; }
.counter-blockers li { color: #5a6472; font-size: 13px; }
/* Red is reserved for money at risk and real blockers -- an advisory note
   stays grey, or the red stops meaning anything. */
.counter-blockers li.blocking { color: #a3271f; font-weight: 600; }

.counter-complete-row { display: flex; align-items: center; gap: 14px; flex-wrap: wrap; margin: 14px 0 20px; }
/* !! r238 -- A DISPLAY RULE BEATS THE `hidden` ATTRIBUTE, AND check-hidden SAID
   SO ON THE FIRST RUN. Nothing had ever hidden one of these rows before r238
   split the button into three; the moment two of them were hidden by default
   they would have been on screen anyway, and the screen would have offered
   Sign, Complete and Open the invoice all at once. Fixed at the class like
   .qe-lines-actions (r206), .qe-fields-row (r233) and .qe-totals (r235) -- the
   fifth member of this family, and the reason that guard exists. */
.counter-complete-row[hidden] { display: none !important; }
.btn-lg { font-size: 15px; padding: 10px 20px; }
.counter-done-box {
  border: 1px solid #bcd9c4; background: #eef7f0; color: #235c33;
  border-radius: 5px; padding: 10px 12px; margin-bottom: 20px;
}

/* ==========================================================================
   ROUND 207 -- PULL FROM STOCK
   ========================================================================== */
.reserve-table { width: 100%; font-size: 12.5px; }
.reserve-table th.num, .reserve-table td.num { text-align: right; }
.reserve-table input.reserve-qty {
  width: 74px; text-align: right; padding: 4px 6px;
  font-variant-numeric: tabular-nums;
}
.reserve-table select { padding: 4px 6px; max-width: 130px; }
.reserve-table td.reserve-free { white-space: nowrap; }
/* A line that cannot be pulled is SHOWN with its reason rather than dropped --
   "why isn't that part here?" is a worse question than a greyed row. */
.reserve-table tr.reserve-blocked td { opacity: 0.6; font-style: italic; }

.pull-list-head {
  margin: 16px 0 6px; font-size: 11px; letter-spacing: 0.07em;
  text-transform: uppercase; color: var(--ink-muted);
  border-bottom: 1px solid var(--border); padding-bottom: 4px;
}
/* A `display` rule BEATS the hidden attribute. `.qe-lines-actions` is flex and
   r207 is the first round to hide one, so it needs the guard -- caught by the
   check-hidden sweep the moment it was written, which is what that sweep is
   for. */
.qe-lines-actions[hidden] { display: none !important; }

/* r233 -- THE SAME TRAP, TWO CLASSES FURTHER ON.
   `.qe-fields-row` and `.settings-grid` are `display: grid`. Until this round
   nothing had ever hidden one, so the guard was never needed; the design job's
   "log a visit" row is the first field row in the app that appears and
   disappears, and check-hidden failed on it the moment it was written. That is
   the sweep doing its job -- the same way it caught .qe-lines-actions in r207.
   Fixed at the CLASS rather than on the one element, because the next field
   row somebody hides would hit it again. */
.qe-fields-row[hidden] { display: none !important; }
.settings-grid[hidden] { display: none !important; }

/* r235 -- THE SAME TRAP, ON THE TOTALS. `.qe-totals` is `display: flex` and
   nothing had ever hidden one until the design schedule, which hides every
   money region on the screen. Caught by the browser probe, not by
   check-hidden: that sweep only knows the elements the markup declares
   hideable, and this footer had no `hidden` attribute of its own to declare
   it. The guard is at the class, like .qe-lines-actions in r207. */
.qe-totals[hidden] { display: none !important; }
.qe-lots-section[hidden] { display: none !important; }
.qe-freight-section[hidden] { display: none !important; }

/* ==========================================================================
   ROUND 208
   ========================================================================== */

/* [his 4] What the Project field has already done to this quote, said on the
   form instead of having to be asked. */
.qe-project-effect {
  margin: 6px 0 0;
  font-size: 12px;
  line-height: 1.55;
  color: var(--ink-muted);
}
.qe-project-effect[hidden] { display: none !important; }
.qe-project-effect strong { color: var(--ink); font-weight: 600; }
.qe-project-effect .btn { margin-left: 4px; vertical-align: baseline; }


/* ROUND 208 [his 5 + his 7] -- record tabs ---------------------------------
   "Put this into a new tab at the top of the page. When clicking on it it acts
   like a tab of quotes or orders or whatever module it's on. Then inside that
   tab there would be another tab to click back to the quote or project or
   order, etc." */
.record-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 2px;
  margin: 0 0 18px;
  border-bottom: 1px solid var(--line);
}
.record-tabs[hidden] { display: none !important; }
.record-tab {
  appearance: none;
  border: 1px solid transparent;
  border-bottom: none;
  background: none;
  padding: 8px 14px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  color: var(--ink-muted);
  cursor: pointer;
  border-radius: 6px 6px 0 0;
  margin-bottom: -1px;
  white-space: nowrap;
}
.record-tab:hover { color: var(--ink); background: var(--accent-soft); }
.record-tab.is-on {
  color: var(--ink);
  background: #fff;
  border-color: var(--line);
  border-bottom: 1px solid #fff;
}
.record-tab-count {
  display: inline-block;
  min-width: 18px;
  padding: 1px 5px;
  margin-left: 5px;
  border-radius: 9px;
  background: var(--accent-soft);
  color: #8A5A17;
  font-size: 11px;
  font-weight: 700;
  text-align: center;
  vertical-align: 1px;
}
.record-tab.is-on .record-tab-count { background: #E7ECFA; color: #1F3FB5; }
/* r231: check-hidden.js now counts this chip as hideable (renderWrTabs reaches
   it with querySelector('.record-tab-count')), and `display: inline-block` on
   a class beats the browser's `[hidden] { display: none }`. The r206 rule --
   a display rule beats the hidden attribute -- so it gets its own. */
.record-tab-count[hidden] { display: none; }

/* r231: a count that means "still outstanding" rather than "how many". Used by
   the warranty claim's What-failed tab for items whose photos/video have not
   been confirmed -- the r64/r69 rule has to stay visible from every tab. */
.record-tab-count.record-tab-count-bad,
.record-tab.is-on .record-tab-count.record-tab-count-bad {
  background: var(--danger-soft);
  color: var(--danger);
}

/* ================= r231 [his items 1 + 2] -- FIELD ROWS THAT STOP =========
   [stated] "I HATE rows being the full length of the page."

   `auto-fit` stretches whatever columns exist to fill the row, so three fields
   on a wide screen became three 500px-wide boxes for a date and a phone
   number. `auto-fill` with a MAXIMUM keeps a field field-sized and packs the
   row from the left, leaving the rest of the row empty -- which is what a form
   is supposed to look like.

   Deliberately NOT a max-width on the panel: the tables on these screens (the
   warranty items grid, the request lines) still use the whole width, because
   a table wants it. It is the FIELDS that were the problem. */
.wr-grid, .svc-grid {
  grid-template-columns: repeat(auto-fill, minmax(220px, 300px));
  gap: 10px 18px;
  margin-bottom: 4px;
}
.wr-grid .field.qe-notes-field, .svc-grid .field.qe-notes-field { grid-column: 1 / -1; }

/* r231 -- the group headings inside these two records.
   "Make this better and easier to fill out and go through" is not only about
   width: with the rows finally packing four across, the little bold lines that
   separate WHO REPORTED IT from DATES were landing hard against the row above
   and read as stray text rather than as a heading. Given room and a hairline
   they do the job the tabs cannot -- a tab separates three stages, these
   separate the four or five questions inside one of them. */
#wr-editor .record-tab-panel > .qe-lines-head,
#svc-editor .record-tab-panel > .qe-lines-head {
  margin: 22px 0 10px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
#wr-editor .record-tab-panel > .qe-lines-head:first-child,
#svc-editor .record-tab-panel > .qe-lines-head:first-child {
  margin-top: 2px; padding-top: 0; border-top: none;
}
#wr-editor .record-tab-panel > .qe-lines-head h2,
#svc-editor .record-tab-panel > .qe-lines-head h2 {
  font-family: var(--font-body);
  font-size: 11.5px;
  font-weight: 700;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--ink-muted);
}
.record-tab-panel[hidden] { display: none !important; }

/* The CRM activity lists reuse the PO-row shape, plus a right-hand column for
   the status and the figure. */
.cust-act-row { cursor: pointer; }
.cust-act-row .cust-act-right {
  margin-left: auto;
  display: flex;
  align-items: center;
  gap: 8px;
  font-variant-numeric: tabular-nums;
}
/* Red is only ever money at risk. An open balance is; a total is not. */
.cust-act-row .money-risk { color: #A32020; font-weight: 700; }

/* ==========================================================================
   ROUND 210
   ========================================================================== */

/* [his 5] The vendor-payment editor: what the payment settled, and the two
   things he can do with it. This panel used to render nothing at all. */
/* r221: .panel-facts is the same component on the customer-payment panel --
   "what this payment settled" is the same idea on the other side of the
   ledger, so it is the same block rather than a second one free to drift. */
.vp-facts, .panel-facts { border: 1px solid var(--line); border-radius: 6px; padding: 10px 12px; background: #fff; }
.vp-facts .meta-row, .panel-facts .meta-row { display: flex; justify-content: space-between; gap: 12px; padding: 3px 0; font-size: 12.5px; }
.vp-facts .meta-label, .panel-facts .meta-label { color: var(--ink-muted); }
.vp-facts .meta-value, .panel-facts .meta-value { font-variant-numeric: tabular-nums; text-align: right; }
/* Red is only ever money at risk. An unpaid balance qualifies. */
.vp-facts .money-risk { color: #A32020; font-weight: 700; }
.vp-receipt-actions { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 10px; }
.vp-receipt-actions .settings-sub { margin: 0; flex-basis: 100%; }
.vp-receipt-actions .btn[disabled] { opacity: .5; cursor: not-allowed; }

/* [his 2] The PO's print-or-email dialog. */
.po-export-choice { display: flex; flex-direction: column; gap: 6px; }
.po-export-preview { margin-top: 14px; }
#po-export-note { margin-top: 10px; }

/* ---------- ROUND 212 ---------- */

/* [his 4] the invoice line grid gained Note, Cost and Sell columns. The note
   is free text and takes what is left; the money columns stay narrow so the
   figures line up under each other. */
.qe-table .col-note { min-width: 120px; }

/* [his 3] a line priced by its LOT: its own sell and extension are blank,
   because the bundle price below is the figure. Set back so the eye reads the
   block as belonging to the lot row that closes it. */
.qe-table tr.inv-in-lot td { background: #FAF8F3; }
.qe-table tr.inv-lot-total td {
  font-weight: 600; border-top: 1px solid var(--border);
  background: #F3EFE7;
}
.qe-table tr.inv-lot-total td:first-child { padding-left: 14px; }

/* [his 7] a tick box that cannot do anything says so, under its own label,
   rather than printing nothing and leaving him to guess which part broke.
   The row goes full width and stacks when it carries a warning -- otherwise
   the two-column grid squeezed the label to four wrapped words beside it. */
.export-opt.has-warn { grid-column: 1 / -1; align-items: flex-start; }
.export-opt.has-warn > span { display: block; }
.export-opt .opt-warn {
  display: block; margin-top: 2px; font-size: 11px; font-style: normal;
  color: var(--danger);
}
/* The Preview-only toggle carries a second line explaining what it changes,
   the same shape the PO export dialog uses. */
.export-opt span em {
  display: block; font-size: 11px; font-style: normal; color: var(--ink-muted);
}
/* A class that sets `display` outranks the browser's own [hidden]{display:none}
   on specificity, so hiding one of these with `el.hidden = true` would do
   nothing. check-hidden.js sweeps for exactly this and caught both rules
   above the moment they were written. */
.export-opt .opt-warn[hidden],
.export-opt span em[hidden] { display: none; }

/* ==========================================================================
   r222 -- THE SUBMITTAL-TO-ORDER RECONCILIATION LOOP.

   [stated] "it should be flagged on the order and submittal (green for
   correct, red for not correct)."

   Green, red -- and amber, which his sentence does not name because the state
   it describes did not exist yet. A line waiting on a factory to answer an
   RFQ is neither correct nor incorrect, and a week of red while the email sits
   in somebody's inbox tells nobody anything.

   COLOUR IS NEVER THE ONLY CARRIER. Every dot has a title, every state is
   spelled out in words in the banner beneath the grid, and the gate lists its
   blockers as text. Roughly one man in twelve cannot reliably separate the red
   from the green, and this is a screen that decides whether a purchase order
   goes out.
   ========================================================================== */

.recon-dot {
  display: inline-block;
  width: 9px; height: 9px;
  padding: 0; margin-left: 5px;
  border-radius: 50%;
  border: 1px solid rgba(0, 0, 0, .25);
  vertical-align: middle;
  cursor: help;
}
.recon-green { background: #2e7d32; }
.recon-amber { background: #ed9c28; }
.recon-red   { background: #c62828; }
.recon-none  { background: #c9ccd1; }

/* The row tint on the submittal grid. Deliberately faint: the approval-status
   tint already owns this row (sub-status-green / -red / -orange) and two
   saturated colour systems on one line make both unreadable. This is a wash at
   the left edge, so the two facts -- what the architect said, and whether the
   order agrees -- can both be seen at once. */
.sub-row.sub-recon-red   { box-shadow: inset 3px 0 0 #c62828; }
.sub-row.sub-recon-amber { box-shadow: inset 3px 0 0 #ed9c28; }
.sub-row.sub-recon-green { box-shadow: inset 3px 0 0 #2e7d32; }

/* !! THE HIDDEN SWEEP CAUGHT THIS ONE THE MOMENT IT WAS WRITTEN.
   .order-locked-banner is a flex row; the reconciliation banner stacks a head
   and a list, so it needs `display: block`. But a bare `display` on a class
   OUTRANKS the [hidden] attribute, so the banner could never be hidden again
   -- an empty red bar permanently across the top of every order screen. The
   guard goes with the override, always. */
.recon-banner { display: block; }
.recon-banner[hidden] { display: none !important; }
.recon-banner-green { border-left: 4px solid #2e7d32; }
.recon-banner-amber { border-left: 4px solid #ed9c28; }
.recon-banner-red   { border-left: 4px solid #c62828; }
.recon-banner-head {
  display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap;
}
.recon-banner-head em { font-style: normal; opacity: .8; }

.recon-list { margin: 8px 0 0; padding: 0; list-style: none; }
.recon-list li {
  display: flex; gap: 8px; align-items: baseline;
  padding: 3px 0 3px 10px;
  border-left: 3px solid transparent;
  font-size: 12px;
}
.recon-list li strong { min-width: 46px; }
.recon-list li span { opacity: .85; }
.recon-list-red   { border-left-color: #c62828; }
.recon-list-amber { border-left-color: #ed9c28; }
.recon-list-green { border-left-color: #2e7d32; }

/* The release gate [his step 7]. It always says WHY -- a disabled button with
   nothing beside it is a support ticket, and a list of the three Types in the
   way is something the user can act on in the next minute. */
.recon-gate {
  margin: 10px 0;
  padding: 10px 12px;
  border-radius: 6px;
  border: 1px solid #d9dce1;
  background: #fbfbfc;
}
.recon-gate-open { border-color: #2e7d32; background: #f3f9f3; }
.recon-gate-shut { border-color: #c62828; background: #fdf5f5; }
.recon-gate-head {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
}
.recon-gate-head span { opacity: .85; flex: 1 1 auto; }

/* The factory-pricing panel on the order screen [his steps 3 + 4]. */
.recon-rfq-panel { margin: 10px 0; }
.recon-rfq-panel h3 { margin: 0 0 6px; font-size: 13px; }
.recon-rfq-table { width: 100%; border-collapse: collapse; font-size: 12px; }
.recon-rfq-table th, .recon-rfq-table td {
  padding: 4px 6px; border-bottom: 1px solid #e6e8ec; text-align: left;
}
.recon-rfq-table input { width: 100%; box-sizing: border-box; }
.recon-rfq-actions { white-space: nowrap; }
.recon-rfq-actions .btn-link { margin-right: 8px; }
.recon-danger { color: #c62828; }

/* [his step 6] the spec cells, editable at last. They were <td
   class="sub-item-readonly"> plain text, so the only way to move a submittal
   line towards an amended order was a whole new revision. */
.sub-spec-cell input, .sub-spec-cell select {
  width: 100%; box-sizing: border-box;
  border: 1px solid transparent;
  background: transparent;
  padding: 2px 3px;
  font: inherit;
}
.sub-spec-cell input:hover, .sub-spec-cell select:hover { border-color: #d9dce1; }
.sub-spec-cell input:focus, .sub-spec-cell select:focus {
  border-color: #7a8699; background: #fff; outline: none;
}
.sub-drafted-btn {
  font-size: 11px; padding: 2px 6px; margin-left: 4px;
  border: 1px solid #c62828; border-radius: 3px;
  background: #fff; color: #c62828; cursor: pointer;
}
.sub-drafted-btn.is-drafted {
  border-color: #2e7d32; color: #2e7d32; cursor: default;
}

/* r224: the @media (prefers-color-scheme: dark) block that lived here was
   deleted. It flipped four surfaces to near-black without ever redefining
   --ink (#1C2127), so on a machine set to dark mode the "Not ready to
   transmit" banner and the focused Part # cell rendered near-black text on
   a near-black background. LumenCore is light-only everywhere else; a
   seven-line partial theme is what produced both symptoms. Do not
   reintroduce a prefers-color-scheme block without a full palette --
   tests/css-balance-check.js fails the build if one comes back without a
   matching --ink override. */

/* ==========================================================================
   ROUND 224 -- THE CHANGE LOG ("Changed") ON THE ORDER SCREEN.
   ========================================================================== */
.order-change-log { display: flex; flex-direction: column; gap: 6px; }
/* An explicit display: wins over [hidden]'s display: none, so a panel that
   sets one has to say so again. tests/check-hidden.js exists because a screen
   that cannot be hidden is a screen that stays on top of the next one. */
.order-change-log[hidden] { display: none; }
.ocl-row {
  display: flex; align-items: flex-start; gap: 10px;
  padding: 8px 10px;
  border: 1px solid var(--border); border-radius: 5px;
  background: #fff;
}
.ocl-row.ocl-money { border-left: 3px solid #B8860B; }
.ocl-row.ocl-manual { border-left: 3px solid var(--brand-blue, #3A5A98); background: #FCFDFF; }
.ocl-row.is-flagged { border-color: var(--danger); background: #FFFBFA; }
.ocl-tick { padding-top: 2px; }
.ocl-body { flex: 1; min-width: 0; }
.ocl-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; font-size: 13px; }
.ocl-date {
  font-variant-numeric: tabular-nums; color: var(--ink-muted);
  font-size: 12px; white-space: nowrap;
}
.ocl-co, .ocl-kind-money, .ocl-internal {
  font-size: 11px; padding: 1px 7px; border-radius: 10px; white-space: nowrap;
}
.ocl-co { background: #EEF1F7; color: var(--ink); font-weight: 600; }
.ocl-kind-money { background: #FBF3DF; color: #7A5A00; }
.ocl-internal { background: #F1F3F7; color: var(--ink-muted); }
.ocl-text { color: var(--ink); }
.ocl-flag {
  margin-top: 4px; font-size: 12px; color: var(--danger); font-weight: 600;
}
.ocl-note {
  margin-top: 5px; width: 100%; font-size: 12px;
  padding: 3px 6px; border: 1px solid var(--border); border-radius: 4px;
}
.ocl-del {
  border: none; background: none; cursor: pointer;
  color: var(--ink-muted); font-size: 17px; line-height: 1; padding: 0 3px;
}
.ocl-del:hover { color: var(--danger); }
.ocl-del-spacer { display: inline-block; width: 17px; }

/* [stated] "when there is a money change you can also put a thick red border
   around the change order create button. so it stands out more prodominitly."
   Thick, as asked, and it clears itself when a change order is approved. */
#order-add-co.needs-co {
  border: 3px solid var(--danger);
  color: var(--danger);
  font-weight: 700;
}
.order-money-unaccounted {
  margin: 0 0 8px;
  font-size: 13px;
  font-weight: 600;
  color: var(--danger);
}

/* ==========================================================================
   ROUND 224 -- THE CORRECTED LOOP: the controls that carry it from the
   submittal, through the change order, and back to the next revision.
   ========================================================================== */

/* The reconcile banner does something now -- it used to be text with a bold
   order number and nothing clickable on the screen. */
.recon-order-link {
  color: var(--brand-blue, #3A5A98);
  font-weight: 700;
  text-decoration: underline;
}
.recon-order-link:hover { color: var(--ink); }
.recon-banner-said {
  display: block; margin-top: 4px;
  font-size: 12.5px; color: var(--ink-muted);
}
.recon-banner-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-top: 10px;
}
.recon-waived {
  font-size: 12.5px; color: var(--ink-muted);
  display: inline-flex; align-items: center; gap: 8px;
}

/* The change order's two ends of the loop: the RFQ out to the factory, and
   the revision back to the architect. */
.co-loop-strip {
  margin: 10px 0;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #F7F9FC;
}
.co-loop-strip.co-loop-rfq { border-left: 3px solid #B8860B; background: #FDFBF4; }
.co-loop-strip.co-loop-open { border-left: 3px solid #2e7d32; background: #F3F9F4; }
.co-loop-strip.co-loop-shut { border-left: 3px solid var(--border); background: #F7F8FA; }
.co-loop-head strong { display: block; font-size: 13.5px; color: var(--ink); }
.co-loop-head span { display: block; margin-top: 3px; font-size: 12.5px; color: var(--ink-muted); }
.co-loop-actions { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 9px; }
.co-loop-count {
  margin-left: 6px; font-size: 11px; color: var(--ink-muted);
  font-variant-numeric: tabular-nums;
}

/* r224 [his 8]: the invoice's own totals row, under the line items, as on
   quotes and orders. It shows what the LINES add up to -- not an echo of the
   stored subtotal -- and says plainly when the two disagree, because a totals
   row that repeats the stored figure hides the typo it exists to catch. */
.inv-lines-total td {
  border-top: 2px solid var(--ink);
  padding-top: 7px;
  font-weight: 600;
  background: #FAFBFC;
}
.inv-lines-total.is-off td { background: var(--danger-soft); }
.inv-lines-total-label { font-size: 12px; font-weight: 600; color: var(--ink); }
/* r286: the sentence names every earlier invoice on a balance, and a long one
   must wrap inside the six columns it spans -- unwrapped it widened the grid
   and pushed Qty, Cost, Sell and Ext. off the right of the screen. */
.inv-lines-total td.inv-lines-total-label { white-space: normal; }
/* The grid is `width: max-content`, so a wrapping cell still asks for its whole
   sentence on one line. A block that contributes no width of its own (width 0,
   then as wide as the cell) wraps inside whatever the columns already are. */
.inv-lines-total-text { width: 0; min-width: 100%; white-space: normal; }
.inv-lines-agree { font-weight: 400; color: var(--ink-muted); }
.inv-lines-off { font-weight: 700; color: var(--danger); }

/* ==========================================================================
   r233 -- THE DESIGN JOB SCREEN.
   ==========================================================================
   Four small rules and nothing else. The frame, the tabs, the tables and the
   fields all come from what is already here -- r231 made the module inset a
   single MODULE FRAME rule and `.quote-editor` picks it up, so this screen is
   inset like every other one without repeating the numbers.

   !! PADDING AND COLOUR ONLY, NEVER `display`.
   The r206 trap, restated by r231: a `display` rule on a selector that also
   matches a hideable element beats the [hidden] attribute, and check-hidden
   fails the build for it. Nothing below sets display on anything hideable. */

/* A field that wants the whole row of the grid -- the notes box and the two
   tick-plus-explanation blocks, where the explanation is the point and a
   300px column would wrap it into a column of single words. */
.settings-grid > .field-wide,
.qe-fields-row > .field-wide { grid-column: 1 / -1; max-width: none; }

/* The fee, as the server computed it. Deliberately a panel and not a table
   row: this is the number he checks against the contract, and it should not
   have to be found among the inputs that produced it. */
.design-fee-summary {
  margin: 14px 0 4px;
  padding: 14px 16px;
  border: 1px solid var(--line, #E2DDD1);
  border-radius: 8px;
  background: #FBFAF6;
}
.design-fee-line { display: flex; justify-content: space-between; gap: 24px; padding: 3px 0; font-size: 13px; }
.design-fee-line .design-fee-label { color: var(--ink-muted, #6B6558); }
.design-fee-line .design-fee-value { font-variant-numeric: tabular-nums; }
.design-fee-total {
  margin-top: 8px; padding-top: 8px;
  border-top: 1px solid var(--line, #E2DDD1);
  font-weight: 600; font-size: 15px;
}
/* The flat fee's effect on the sq-ft answer, said out loud rather than left
   as two numbers that disagree. */
.design-fee-note { margin-top: 8px; font-size: 11.5px; color: var(--ink-muted, #6B6558); line-height: 1.45; }
/* The summary is the SERVER'S answer. The moment anything above it is typed
   the figure on screen is a stale one, and it says so -- rather than either
   freezing (which reads as broken) or recalculating in the browser, which
   would be the second implementation of the fee this round exists to avoid.
   `visibility`, not `display`: this note sits inside a panel that is never
   hidden by the [hidden] attribute, but keeping to visibility here means the
   rule cannot ever beat [hidden] on anything (the r206 trap). */
.design-fee-stale-note { visibility: hidden; }
.design-fee-summary.is-stale { border-color: #E0A75E; background: #FFFBF3; }
.design-fee-summary.is-stale .design-fee-stale-note { visibility: visible; color: #8A5200; }

/* The visit allowance line. Turns amber only when it is actually over -- a
   counter that is always coloured stops being read. */
.design-usage { margin: 4px 0 12px; font-size: 12.5px; color: var(--ink-muted, #6B6558); }
.design-usage.is-over { color: #8A5A00; font-weight: 600; }

/* ==========================================================================
   r234 -- THE AGREEMENT TAB.
   ==========================================================================
   Padding and colour only, never `display` -- the r206 trap, and check-hidden
   fails the build for it. */
.design-state {
  padding: 10px 13px; border-radius: 6px; font-size: 12.5px; line-height: 1.5;
  border: 1px solid var(--border); background: var(--surface-2); margin-bottom: 4px;
}
.design-state-now  { background: #FFF1DC; border-color: #E0A75E; color: #8A5200; }
.design-state-done { background: #E3F3E8; border-color: #9FD2B0; color: #1F6B39; }

.design-billing {
  max-width: 520px; border: 1px solid var(--border); border-radius: 6px;
  padding: 6px 12px; background: #FBFAF6;
}
.design-bill-row {
  display: flex; justify-content: space-between; gap: 20px;
  padding: 5px 0; font-size: 13px; border-bottom: 1px solid #EFEBE0;
}
.design-bill-row:last-child { border-bottom: 0; }
.design-bill-row > span:last-child { font-variant-numeric: tabular-nums; }
/* r276 [stated] "...with its current revision and issued for at the top."
   The heading of the counter block, not another figure row: it is what the
   rows below are counting, so it reads heavier and its right-hand value is
   words rather than a number (tabular figures would space a status oddly). */
.design-bill-head {
  font-weight: 800; font-size: 13.5px;
  border-bottom: 1px solid #DDD6C4; padding-bottom: 7px; margin-bottom: 2px;
}
.design-bill-head > span:last-child { font-variant-numeric: normal; font-weight: 600; color: #6B6357; }
/* r276 -- which revision a site visit's notes are filed under, under the note
   itself rather than in a column of its own: the table is already five columns
   wide and this is a property of the NOTE, not of the visit. */
.visit-rev-tag { margin-top: 6px; }
/* r265 [his ask]: the orders a design job turned into. A little more room than
   a figure row, because each one carries a status pill and a way in. */
.design-order-row { align-items: center; padding: 7px 0; }
.design-order-row > span:last-child { display: flex; align-items: center; gap: 8px; }

/* The wording, as stored text. Scrolls inside itself so the record stays
   navigable -- a twelve-section contract inlined at full height would bury the
   invoice panel above it. */
.design-agreement-preview {
  max-height: 460px; overflow-y: auto;
  border: 1px solid var(--border); border-radius: 6px;
  padding: 14px 16px; background: #FBFAF6;
  white-space: pre-wrap; font-size: 12px; line-height: 1.55;
}

/* The merge fields, as clickable chips. A field already used reads differently
   from one still available, so the gap in a half-written contract is visible
   at a glance rather than by reading the whole thing. */
.design-token-list { display: flex; flex-wrap: wrap; gap: 5px; margin: 4px 0 8px; }
.design-token {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 11px; padding: 2px 7px; border-radius: 4px; cursor: pointer;
  border: 1px solid var(--border); background: #fff; color: var(--ink-muted);
}
.design-token.is-used { background: #E3F3E8; border-color: #9FD2B0; color: #1F6B39; }

/* ==========================================================================
   r235 -- THE DESIGN SCHEDULE.
   ==========================================================================
   Padding and colour only. Nothing here sets `display` on a hideable element:
   the money regions are hidden with the [hidden] attribute from app.js, and a
   display rule on them would beat it (the r206 trap). */
.qe-design-banner {
  margin: 0 0 12px; padding: 10px 14px; border-radius: 6px; font-size: 12.5px; line-height: 1.55;
  background: #EEF2FB; border: 1px solid #B9C7E8; color: #24406F;
}
/* r250 item A: the distribution side of the fork. Amber, not blue -- the blue
   banner says "this is the specification", and this one says "the
   specification is somewhere else". Two banners that looked the same would
   be one more thing to read carefully on a screen whose whole problem is
   that two screens look alike. */
.qe-design-banner.is-distribution {
  background: #FDF6E3; border-color: #E3D3A0; color: #6B551B;
}
.qe-design-banner.is-distribution .qe-design-jump { margin-left: 6px; vertical-align: baseline; }

/* r250 item G: the per-job general-notes override, collapsed by default. It is
   a thing you occasionally change, not a thing you read every time you open
   the Issues tab, so it opens on demand and does not push the counters down. */
.design-notes-block {
  margin: 14px 0 4px; padding: 10px 14px; border: 1px solid var(--border);
  border-radius: 6px; background: #FBFAF6;
}
.design-notes-block > summary {
  cursor: pointer; font-weight: 600; font-size: 13px; list-style: revert;
}
.design-notes-block > summary .tag { margin-left: 8px; font-weight: 400; }
.design-notes-block textarea { width: 100%; margin-top: 6px; font-size: 12.5px; line-height: 1.5; }
.design-notes-block[hidden] { display: none !important; }
/* [hidden] must beat the class's own display context -- the r206/r231/r235
   family. Nothing sets display on this element today, and this is the guard
   that stops the fourth time. */
.qe-design-banner[hidden] { display: none !important; }

/* The fixture image cell. Small, because the column is -- it is a thumbnail
   for a spec book page, not a viewer. */
.qe-img-cell {
  display: flex; align-items: center; justify-content: center;
  position: relative; height: 34px; border-radius: 4px; cursor: pointer;
  border: 1px dashed var(--border); background: #FBFAF6; overflow: hidden;
}
.qe-img-cell.has-img { border-style: solid; background: #fff; }
.qe-img-cell.is-dropping { border-color: var(--primary-light, #4A6BC8); background: #EEF2FB; }
.qe-img-cell img { max-width: 100%; max-height: 100%; object-fit: contain; display: block; }
.qe-img-empty { font-size: 10px; color: var(--ink-muted); letter-spacing: 0.04em; text-transform: uppercase; }
.qe-img-cell.is-unsaved { border-style: dotted; cursor: default; font-size: 10px; color: var(--ink-muted); }
.qe-img-clear {
  position: absolute; top: 1px; right: 1px; width: 15px; height: 15px; line-height: 13px;
  padding: 0; border-radius: 3px; border: 1px solid var(--border);
  background: rgba(255,255,255,0.9); color: var(--ink-muted); font-size: 12px; cursor: pointer;
}
.qe-img-clear:hover { color: var(--danger); border-color: #E5B8B8; }

/* ==========================================================================
   r236 -- ISSUES AND THE TWO COUNTERS.
   ==========================================================================
   Padding and colour only -- nothing here sets `display` on a hideable
   element. */
.design-counters { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 6px; }
.design-counter {
  flex: 1 1 200px; min-width: 190px;
  border: 1px solid var(--border); border-radius: 6px; padding: 11px 14px; background: #FBFAF6;
}
/* Amber only when it is actually over. A counter that is always coloured stops
   being read -- the same rule as the site-visit line. */
.design-counter.is-over { border-color: #E0A75E; background: #FFF9F0; }
.design-counter-n { display: block; font-size: 24px; font-weight: 700; line-height: 1.1; font-variant-numeric: tabular-nums; }
.design-counter-n .of { font-size: 15px; font-weight: 500; color: var(--ink-muted); }
.design-counter-k { display: block; margin-top: 2px; font-size: 12px; font-weight: 600; }
.design-counter-sub { display: block; margin-top: 3px; font-size: 11px; color: var(--ink-muted); line-height: 1.4; }

.design-issues-list { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
.design-issue {
  border: 1px solid var(--border); border-left: 3px solid var(--border);
  border-radius: 5px; padding: 9px 12px; background: #fff;
}
/* The final package is the one the credit window is measured from, so it reads
   differently from the rest of the list. */
.design-issue.is-final { border-left-color: #1F6B39; background: #F6FBF7; }
.design-issue-head { display: flex; align-items: center; gap: 9px; flex-wrap: wrap; font-size: 12.5px; }
.design-issue-n { font-weight: 700; }
.design-issue-date { color: var(--ink-muted); }
.design-issue-actions { margin-left: auto; display: flex; gap: 4px; }
.design-issue-what { margin-top: 5px; font-size: 13px; }
.design-issue-meta { margin-top: 3px; font-size: 11.5px; color: var(--ink-muted); line-height: 1.45; }
.design-issue-files { margin-top: 7px; display: flex; flex-wrap: wrap; gap: 7px; }
.design-issue-file {
  display: inline-flex; align-items: center; gap: 6px; position: relative;
  border: 1px solid var(--border); border-radius: 4px; padding: 3px 8px; font-size: 11.5px; background: #FBFAF6;
}

/* r237 -- the credit-window banner reuses the lien-flag furniture, but AMBER:
   a closing window is a decision to make, not money already at risk. Red is
   used sparingly in this app and spending it here would devalue it where it
   means something. */
#home-design-windows { border-color: #E0A75E; background: #FFF6E9; }
#home-design-windows .lien-flag-head { color: #8A5200; }
#home-design-windows .lien-flag { border-color: #E8D3A8; }
#home-design-windows .lien-flag-money { color: #8A5200; }
#home-design-windows .lien-flag-main { cursor: pointer; }

.design-credit-says {
  margin: 10px 0 14px; padding: 11px 14px; border-radius: 6px;
  background: #FBFAF6; border: 1px solid var(--border);
  font-size: 12.5px; line-height: 1.55;
}

/* ==========================================================================
   r242 -- PACKING AND LABELS on the delivery.
   ========================================================================== */
/* One row per package: what it is, what is in it, and the two numbers that go
   on the label. Built on .qe-po-row's furniture rather than a new box shape,
   so it reads as the same kind of list as the vendor POs and the team roster
   already on these screens. */
.pkg-row {
  display: flex;
  align-items: center;
  gap: 12px;
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 6px;
  padding: 8px 12px;
  margin-bottom: 6px;
}
.pkg-no { font-weight: 700; font-size: 14px; min-width: 62px; }
.pkg-no .pkg-of { font-weight: 400; font-size: 11px; color: var(--ink-muted); }
/* The Type is the biggest thing on the printed label, so it is the biggest
   thing in this row too -- the screen and the paper should agree about what
   the important word is. */
.pkg-type {
  font-weight: 800; font-size: 17px; letter-spacing: -0.01em;
  min-width: 66px; white-space: nowrap;
}
.pkg-type.is-mixed { font-size: 12px; letter-spacing: .06em; color: var(--ink-muted); }
.pkg-contents { flex: 1; min-width: 0; font-size: 12px; color: var(--ink-muted); }
.pkg-f { display: flex; flex-direction: column; gap: 2px; }
.pkg-f > span { font-size: 10px; letter-spacing: .05em; text-transform: uppercase; color: var(--ink-muted); }
.pkg-f input { width: 92px; }
.pkg-f-wide input { width: 170px; }
.pkg-pieces { font-weight: 600; font-size: 12.5px; white-space: nowrap; min-width: 52px; text-align: right; }

/* ==========================================================================
   r243 -- CARD PAYMENTS on the invoice.
   ========================================================================== */
/* A card attempt row reads like a payment row because that is what it nearly
   is -- but the STATUS is the thing being read, so it carries the colour and
   the money does not. A decline shown in the same grey as an approval is a log
   nobody can scan. */
.card-tx { display: flex; align-items: center; gap: 12px; }
.card-tx-kind { font-weight: 600; font-size: 12px; min-width: 54px; }
/* r287 [stated 09/16/2026] "who opened it, when, and IP". The time leads the
   row, and who / the address sit under the detail in the muted colour. The row
   wraps rather than pushing the Refund button off the right edge. */
.card-tx { flex-wrap: wrap; }
.card-tx-when { font-size: 12px; color: var(--ink-muted); white-space: nowrap; min-width: 118px;
  font-variant-numeric: tabular-nums; }
.card-tx-who { flex-basis: 100%; padding-left: 130px; font-size: 12px; color: var(--ink-muted); }
.card-tx-who:empty { display: none; }
.inv-opens-ip { font-variant-numeric: tabular-nums; }
#inv-charge-card[hidden], #inv-add-payment[hidden] { display: none; }
.card-tx-status {
  font-size: 11px; letter-spacing: .05em; text-transform: uppercase; font-weight: 700;
  border-radius: 3px; padding: 2px 7px; white-space: nowrap;
}
.card-tx-approved .card-tx-status { background: #E6F3EA; color: #1E6B37; }
.card-tx-refunded .card-tx-status { background: #EAF0FB; color: #1F3FB5; }
.card-tx-declined .card-tx-status,
.card-tx-failed .card-tx-status { background: #FBE9E7; color: #9A2A1F; }
.card-tx-pending .card-tx-status,
.card-tx-sent_to_terminal .card-tx-status { background: #FDF3D8; color: #8A5200; }
/* !! r283 -- `flex-direction: row` IS NOT THE DEFAULT HERE, AND THAT IS WHY
   THESE BUTTONS HAVE BEEN STACKED. This element carries `.field` as well, and
   `.field` sets `flex-direction: column`. `display: flex` below overrides
   `.field`'s display and says nothing about the direction, so the column
   survived and "Take the payment" sat on top of "Cancel" on the card form from
   the day it was written. Found by rendering the panel and looking at it. */
.card-form-actions { display: flex; flex-direction: row; align-items: flex-end; gap: 8px; }

/* r283: the refund panel's service-fee question. The tick and the sentence
   under it are one field, so the sentence cannot end up beside a different
   control when the row wraps -- and the field spans the row because the
   question is a sentence, not a box. */
#inv-refund-form .qe-field-wide { grid-column: 1 / -1; }
/* The actions sit on their own row under the question, not squeezed into a
   column beside it. */
#inv-refund-form .card-form-actions { grid-column: 1 / -1; justify-content: flex-start; }
#inv-refund-form .tickline { display: flex; align-items: center; gap: 8px;
  font-weight: 600; cursor: pointer; }
#inv-refund-form .tickline input { width: auto; margin: 0; }
#inv-refund-form #refund-fee-note { margin: 4px 0 0; max-width: 62ch; }

/* ==========================================================================
   r244 [his item 1] -- THE BLOCKED-DIALOGS BAR.
   ========================================================================== */
/* Amber, not red: nothing is broken and no money is at risk -- the app has
   quietly switched to its own confirmations. Red is spent sparingly in this
   app and spending it here would devalue it where it means something. */
.dialog-blocked-bar {
  position: fixed; left: 0; right: 0; top: 0; z-index: 60;
  display: flex; align-items: center; justify-content: center; gap: 14px;
  padding: 9px 16px; font-size: 12.5px; line-height: 1.4;
  background: #FFF6E9; border-bottom: 1px solid #E0A75E; color: #8A5200;
}
.dialog-blocked-bar[hidden] { display: none; }

/* r244 [his item 2]: the year tick and the "not applicable" cells beside it.
   A per-project number carries no year by design (TR-001 means the first
   transmittal on THIS job), so its cell says so rather than showing a tick
   that would do nothing. */
.numbering-row input.nr-year { width: auto; justify-self: center; }
.numbering-row .nr-na { font-size: 11px; text-align: center; }

/* ==========================================================================
   r244 [his item 13] -- A BUTTON IS THE WIDTH OF ITS WORDS. MAKE IT A RULE.
   ==========================================================================
   [stated] "'Log the visit' and 'cancel' should NOT expand the whole width of
   the screen. How many times do I have to tell you the same thing not to do?
   Make this a rule and never do it again unless necessary!!"

   The cause, so it is not guessed at next time: `.field` is a flex COLUMN, and
   a flex container stretches its children across the cross axis by default --
   so any button dropped into a field became as wide as the field, and a
   `.field-wide` field is the whole screen. It was never anybody typing
   `width: 100%`.

   Two rules. The first is the safety net: a bare button in a field shrinks to
   its text whatever else happens. The second is the shape forms should use --
   a row of actions, side by side, left-aligned.

   tests/check-buttons.js measures every button on every screen in a real
   browser and fails if one in a form is wider than its words need. That is the
   part that makes it a rule rather than a promise. */
.field > .btn,
.field > button { align-self: flex-start; width: auto; }

.form-actions {
  display: flex;
  flex-direction: row;
  align-items: center;
  gap: 8px;
  flex-wrap: wrap;
}
.form-actions > .btn,
.form-actions > button { align-self: center; width: auto; flex: 0 0 auto; }
.form-actions-note { font-size: 11.5px; color: var(--ink-muted); }

/* r244 [his item 6] -- a note nobody outside this office ever sees is labelled
   as one, in the label itself, where it cannot be missed. */
.internal-tag {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border-radius: 3px;
  background: #FFF6E9;
  border: 1px solid #E0A75E;
  color: #8A5200;
  font-size: 9.5px;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

/* r244 [his item 13] -- a site visit note is a paragraph, and the row opens. */
.visit-row { cursor: pointer; }
.visit-row:hover { background: #FBF9F4; }
.visit-notes-cell { white-space: normal; line-height: 1.45; max-width: 520px; }
.visit-notes-cell > div { margin: 0 0 3px; }
.visit-bullets { margin: 2px 0 4px; padding-left: 18px; }
.visit-bullets li { margin: 0 0 2px; }
/* r244 [his item 7]: the blended rate, called out because it is the number the
   contract's "fee rate" sentence is actually about. */
.design-fee-line.design-fee-effective { font-weight: 600; }

/* r244 [his item 11] -- our own signature pad, before the agreement goes out. */
.cs-pad-wrap { position: relative; }
#cs-pad {
  width: 100%; height: 150px; border: 1px solid var(--border); border-radius: 8px;
  background: #fff; touch-action: none; display: block; cursor: crosshair;
}
.cs-clear { position: absolute; top: 6px; right: 6px; }

/* ===========================================================================
   r245 [his item 17] -- THE SPEC BOOK'S PROGRESS BAR.
   ===========================================================================
   [stated] "When creating a spec book as this could take a while there should
   be a status progress bar on the screen to show percent complete or
   something."

   It names the STEP as well as the percentage, because a bar that sits at 60%
   for twenty seconds while a 200-page cut sheet is embedded looks stuck, and
   "Cut sheet DMF-DRD2.pdf" is the difference between waiting and giving up. */
.sb-progress { margin-top: 14px; max-width: 520px; }
.sb-progress[hidden] { display: none; }
.sb-bar {
  height: 8px; background: #ECEAE2; border-radius: 999px; overflow: hidden;
}
.sb-bar > i {
  display: block; height: 100%; width: 0%; background: var(--accent, #1F3FB5);
  border-radius: 999px; transition: width 200ms linear;
}
.sb-line {
  display: flex; gap: 10px; align-items: baseline;
  margin-top: 6px; font-size: 12px; color: var(--ink-muted);
}
.sb-line #sb-pct { font-weight: 700; color: var(--ink); font-variant-numeric: tabular-nums; }

.sb-result { margin-top: 12px; max-width: 640px; font-size: 12.5px; }
.sb-result[hidden] { display: none; }
.sb-result.is-ok { color: var(--ink); }
.sb-result.is-bad { color: #A3372A; }
.sb-result .sb-warn {
  margin-top: 8px; padding: 8px 10px; background: #FBF0DC;
  border-left: 3px solid #C8A32E; line-height: 1.5;
}

/* ==========================================================================
   r250b -- ITEM C's PICKER, ITEM D's IMAGE SOURCE, ITEM E's PREVIEW ROW.
   ========================================================================== */

/* --- item C: the spec-chip cell and its picker -------------------------- */
.qe-chip-cell { display: flex; align-items: center; gap: 4px; width: 100%; }
.qe-chip-cell > input { flex: 1 1 auto; min-width: 0; }
.qe-chip-btn {
  flex: 0 0 auto; width: 22px; height: 22px; line-height: 1;
  border: 1px solid var(--border, #D8D4C8); border-radius: 4px;
  background: #fff; color: #1B2A41; font-weight: 700; cursor: pointer; padding: 0;
}
.qe-chip-btn:hover { background: #EEF1F7; }
.qe-chip-picker {
  position: absolute; z-index: 900; width: 320px;
  background: #fff; border: 1px solid #C8C4B8; border-radius: 8px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.18); padding: 10px;
}
.qe-chip-picker-row { display: flex; gap: 6px; align-items: center; }
.qe-chip-picker-row select { flex: 1 1 auto; min-width: 0; }
.qe-chip-picker-row input { flex: 1 1 auto; min-width: 0; }
.qe-chip-picker-note { margin: 8px 0 0 0; font-size: 11.5px; color: #5B6068; line-height: 1.45; }

/* --- item D: a product's picture standing in for a line's ---------------- */
/* It must not look like a picture this job owns. Dashed, dimmed a little, and
   labelled -- three signals rather than one, because the cell is 84px and a
   single subtle one is a signal nobody sees. */
.qe-img-cell.from-product { border-style: dashed; }
.qe-img-cell.from-product img { opacity: 0.88; }
.qe-img-badge {
  position: absolute; bottom: 0; left: 0; right: 0;
  font-size: 9px; line-height: 1.4; text-align: center;
  background: rgba(27,42,65,0.78); color: #fff; letter-spacing: 0.04em;
  text-transform: uppercase; pointer-events: none;
}

/* --- item E: the View PDF row under each terms block -------------------- */
.terms-preview-row {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 6px 0 14px 0;
}
.terms-preview-note { margin: 0; }
.terms-preview-dead { display: block; }
.settings-warn {
  display: block; padding: 9px 11px; border-radius: 6px;
  background: #FFF4E2; border: 1px solid #E0BE7C; color: #6B4A08;
  font-size: 12px; line-height: 1.5;
}
.settings-unlisted { margin-top: 12px; }

/* r250b item F: the transmittal line on an issue row. */
.design-issue-tr {
  display: flex; align-items: center; gap: 8px; flex-wrap: wrap;
  margin-top: 8px; padding-top: 8px; border-top: 1px dashed var(--border, #D8D4C8);
  font-size: 12px;
}
.design-issue-tr-n { font-weight: 700; letter-spacing: 0.02em; }

/* ===========================================================================
   r251c -- RETURNS.
   =========================================================================== */

/* The non-returnable tick, on any line grid it is switched on for. Centred in
   its own cell so a column of ticks reads down the page at a glance, which is
   the whole point of asking for a tick rather than a flag. */
.qe-tick-cell {
  display: flex; align-items: center; justify-content: center;
  height: 26px; cursor: pointer;
}
.qe-tick-cell input { width: 15px; height: 15px; cursor: pointer; }

/* The lines that MAY come back. Buttons, not a text box: a return has to trace
   to something that was actually sold. */
.ret-add-list { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px 0; }
.ret-add-btn { text-align: left; line-height: 1.35; }
.ret-add-meta {
  display: block; font-size: 11px; color: var(--muted, #6C6A63); font-weight: 400;
}

.ret-table th.col-qty { width: 82px; }
.ret-table td.num { text-align: right; }
.ret-table td.num input { text-align: right; }
.ret-undecided { color: var(--muted, #6C6A63); }
/* Pieces that arrived and have been neither accepted nor denied. Amber rather
   than red: it is not wrong, it is unfinished -- somebody still has to count. */
.ret-undecided.is-open { color: #8A5B00; font-weight: 700; }

.ret-row-flag > td { background: #FFFBF2; }
.ret-flag {
  display: inline-block; margin-left: 8px; padding: 1px 6px; border-radius: 3px;
  background: #F6E2C0; border: 1px solid #E0BE7C; color: #6B4A08;
  font-size: 10px; font-weight: 700; letter-spacing: 0.04em;
}

.ret-settlement { max-width: 560px; }
.ret-settle { width: 100%; border-collapse: collapse; }
.ret-settle td { padding: 5px 0; font-size: 13px; }
.ret-settle td.value { text-align: right; font-variant-numeric: tabular-nums; }
.ret-settle tr.grand td {
  border-top: 2px solid var(--ink, #1B2A41); font-weight: 700; padding-top: 8px;
}

/* What is standing in the way, in words, BEFORE the button is pressed. The
   button is disabled while any of these stand, so the screen and the server
   cannot disagree about whether a credit may be raised. */
.ret-blockers {
  margin: 12px 0 0 0; padding: 10px 12px 10px 28px; border-radius: 6px;
  background: #FDF0EE; border: 1px solid #E3B4AC; color: #7A2E20;
  font-size: 12px; line-height: 1.55;
}
.ret-advice {
  margin: 12px 0 0 0; padding: 10px 12px; border-radius: 6px;
  background: #EEF4FB; border: 1px solid #A9C4E0; color: #274866;
  font-size: 12px; line-height: 1.55;
}
/* r256 -- WHY the restocking fee is what it is. Quiet by default, because it
   is context and not a warning; it turns amber once the credit note exists,
   because at that point the number is frozen and the boxes above it are inert. */
.ret-fee-why {
  margin: 8px 0 0 0; font-size: 11.5px; line-height: 1.5; color: #5A6472;
}
.ret-fee-why.is-settled {
  padding: 8px 10px; border-radius: 6px;
  background: #FDF6E3; border: 1px solid #E3CE93; color: #6B551B;
}
.ret-refund {
  display: block; margin-top: 14px; font-size: 12px; line-height: 1.5; cursor: pointer;
}
.ret-refund input { margin-right: 6px; }
/* r310 [money audit D9] -- a line sold inside a lot (or an option priced as a
   whole) has no price of its own: the box where he types the credit for each
   piece, and the few words saying why it is his to type. */
.ret-typed-credit { width: 104px; min-width: 104px; text-align: right; }
.ret-typed-row td { padding-top: 0; border-top: none; }
.ret-typed-why { display: block; font-size: 11.5px; line-height: 1.45; color: #5A6472; text-align: right; }

/* r310 [money audit D8] -- an order billed MORE than it is now worth (a change
   order took money off after it was billed). Money of the client's we are
   holding, with the credit note offered: information and an action, not a
   warning, so the quiet blue the return panel's advice uses. */
.order-over-billed {
  margin: 6px 0 10px 0; padding: 10px 12px; border-radius: 6px;
  background: #EEF4FB; border: 1px solid #A9C4E0; color: #274866;
  font-size: 12.5px; line-height: 1.55;
}
.order-over-billed .order-over-billed-say { margin: 0 0 8px 0; }
.order-over-billed .settings-sub { margin-left: 8px; }

/* ==========================================================================
   r252 -- ACCOUNT REGISTRATION, THE TERMS PROGRAM AND THE CREDIT APPLICATION
   ========================================================================== */

/* The pending count beside "Account Applications". A number he can see from
   any screen, because an application nobody looks at is an account nobody
   opens -- and it is HIDDEN at zero rather than showing a 0, which reads as
   something to clear. */
.nav-badge {
  margin-left: auto; min-width: 20px; padding: 1px 6px; border-radius: 9px;
  background: #B3341F; color: #fff; font-size: 11px; font-weight: 700;
  text-align: center; line-height: 17px;
}

/* The one-application screen and the credit tab: plain stacked blocks rather
   than the grid the money editors use, because almost everything on them is
   read-only evidence. */
.qe-fields-stack { padding: 0 0 40px; }
.appl-block { margin: 22px 0 0; }
.appl-block h2 {
  font-size: 12px; letter-spacing: .07em; text-transform: uppercase;
  color: var(--muted, #5B6068); margin: 0 0 8px;
  padding-bottom: 5px; border-bottom: 1px solid var(--border, #D5D9E0);
}
.appl-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 26px; }
.appl-row {
  display: flex; justify-content: space-between; gap: 14px;
  padding: 6px 0; border-bottom: 1px solid #eceef1; font-size: 13.5px;
}
.appl-row .k { color: var(--muted, #5B6068); }
.appl-row .v { text-align: right; word-break: break-word; }
.appl-note {
  border: 1px solid #E0A75E; background: #FFF1DC; color: #8A5200;
  border-radius: 6px; padding: 10px 13px; font-size: 13px; line-height: 1.5;
  margin: 14px 0;
}
.appl-note.plain { border-color: #cfd6e0; background: #f4f7fb; color: #34404f; }
.appl-note.good { border-color: #bcd7c3; background: #f2f9f4; color: #24512f; }
.appl-actions { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.credit-refs { width: 100%; border-collapse: collapse; font-size: 13px; margin-top: 8px; }
.credit-refs th, .credit-refs td {
  border: 1px solid var(--border, #D5D9E0); padding: 5px 7px; text-align: left;
}
.credit-refs th { background: #f4f5f7; font-weight: 600; font-size: 12px; }
.credit-refs input, .credit-refs select {
  width: 100%; border: 0; padding: 3px 2px; font: inherit; background: transparent;
}
/* The terms versions list in Settings. A published version is evidence and
   cannot be edited, so it reads differently from a draft. */
/* ==========================================================================
   r294 -- TWO DOCUMENTS, TWO PANELS, AND COLUMNS THAT LINE UP.
   ==========================================================================
   [stated 09/20/2026, with the screenshot] "this needs to be separated
   better. +new version is in the design section and you have alllll that
   room on the right but cramming verbiage together."

   The middle complaint is the one that mattered: "+ New version" adds a
   SALES version and it sat directly above the "Design Terms and Conditions"
   heading, so it read as the design list's button. Each document is its own
   bordered panel now and its button lives inside its own border, under its
   own list -- it cannot drift into the next section because the next section
   is a different box. */
.terms-docblock { border: 1px solid var(--border, #d9dce1); border-radius: 10px;
  padding: 16px 18px 14px; margin-bottom: 18px; background: #fcfcfd; }
.terms-docblock:last-of-type { margin-bottom: 4px; }
.terms-docblock-head { display: flex; align-items: baseline; gap: 12px;
  flex-wrap: wrap; margin-bottom: 2px; }
.terms-docblock-head h2 { margin: 0; }
.terms-docblock-tag { font-size: 11px; letter-spacing: .05em; text-transform: uppercase;
  color: var(--muted, #5B6068); border: 1px solid var(--border, #d9dce1);
  border-radius: 999px; padding: 1px 9px; background: #fff; }
.terms-docblock-actions { margin-top: 12px; padding-top: 10px;
  border-top: 1px dashed var(--border, #d9dce1); }

.terms-row {
  display: flex; align-items: center; gap: 14px; padding: 10px 0;
  border-bottom: 1px solid #eceef1; font-size: 13.5px; flex-wrap: wrap;
}
.terms-row:last-child { border-bottom: 0; }
/* Fixed minimums so the columns line up DOWN the list rather than each cell
   being as wide as its own text happened to be. With the card taking the
   width it is given, there is room for them to breathe. */
.terms-row .terms-ver { font-weight: 700; min-width: 70px; }
.terms-row .terms-when { min-width: 200px; color: var(--muted, #5B6068); font-size: 12.5px; }
.terms-row .terms-hash { min-width: 168px; }
/* The notes cell takes the slack, so the buttons sit a readable distance
   from the words they act on instead of against the far edge of a 1500px
   card. Its own contents wrap rather than stretching. */
.terms-row .terms-row-notes { flex: 1 1 220px; display: flex; align-items: center;
  gap: 10px; flex-wrap: wrap; min-width: 0; }
.terms-row .terms-row-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
.terms-row .terms-state { font-size: 12px; min-width: 92px; }
.terms-row .terms-state.draft { color: #8A5B00; }
.terms-row .terms-state.live { color: #24512f; font-weight: 600; }
.terms-row .terms-state.old { color: var(--muted, #5B6068); }
.terms-hash { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 11px; color: var(--muted, #5B6068); }

/* ==========================================================================
   r293 -- THE TERMS VERSION SAYS WHAT IT IS, AND THE DOCUMENT IS SHOWN.
   ==========================================================================
   [stated 09/19/2026] "uploading a pdf for Terms uploads as plain text and
   not the actual document... Make it happen so we can upload it and it keeps
   all formatting or it uploads a pdf document and it uses the actual document
   provided."  */
.terms-row .terms-kind { font-size: 11.5px; font-weight: 600; color: #24512f;
  background: #eaf4ed; border: 1px solid #cfe3d6; border-radius: 999px; padding: 1px 9px; }
.terms-row .terms-kind.is-rendered { color: var(--muted, #5B6068); background: transparent;
  border-color: var(--border, #d9dce1); font-weight: 500; }

.terms-doc-preview { border: 1px solid var(--border, #d9dce1); border-radius: 8px;
  background: #f6f8f6; padding: 10px 12px 4px; margin-bottom: 10px; }
.terms-doc-preview .tdp-head { display: flex; align-items: baseline; gap: 10px;
  flex-wrap: wrap; margin-bottom: 8px; }
.terms-doc-preview .tdp-frame { display: block; width: 100%; height: 62vh; min-height: 420px;
  border: 1px solid var(--border, #d9dce1); border-radius: 6px; background: #fff; }
.terms-doc-preview .tdp-note { margin: 8px 2px 6px; }
/* The reading, when there is a document above it: still there, plainly
   secondary. It is what search reads and it is never what a customer sees. */
.terms-wording-preview.is-secondary { max-height: 180px; opacity: .78;
  border-style: dashed; }
/* Green counterpart to the existing .cell-warn / .cell-danger pair. A signed
   guaranty is not a warning and not a danger, and printing it in the same
   grey as an empty cell hides the one fact that unblocks an approval. */
.cell-good { color: #24512f; font-weight: 600; }

/* ==========================================================================
   r259 -- THE FORMATTED-WORDING CHIPS AND THE TERMS WORDING PANEL.
   ==========================================================================
   The editor brings its own stylesheet (richeditor.js); what is here is the
   Settings screen around it -- the state chip beside each block, and the
   read-only reading of a terms body that replaced its textarea. */
.rte-chip { font-size:10.5px; font-weight:700; letter-spacing:.05em; text-transform:uppercase;
  padding:2px 8px; border-radius:999px; white-space:nowrap; }
.rte-chip.is-html { background:#e7f0ff; color:#1f3a93; }
.rte-chip.is-text { background:#eef0f2; color:#5a626b; }
.rich-edit-row { display:flex; align-items:center; gap:9px; flex-wrap:wrap; }
.rte-unsaved { font-size:11.5px; }
/* A formatted block's textarea holds MARKUP. It stays visible -- seeing what
   is stored is worth something -- but it is not hand-editable, because an
   unbalanced tag typed into a contract is exactly what the editor exists to
   stop. */
textarea.is-rich-locked { background:#f5f6f8; color:#5a626b; cursor:default; }
.terms-wording { border:1px solid var(--border,#e2e4e8); border-radius:8px; overflow:hidden; }
.terms-wording-head { display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  padding:9px 12px; background:var(--panel,#fafbfc); border-bottom:1px solid var(--border,#e2e4e8); }
.terms-wording-preview { max-height:420px; overflow:auto; padding:22px 26px; background:#fff;
  font-family:'Source Serif 4','Source Serif Pro',Georgia,serif; font-size:12.4px; line-height:1.62; }
.terms-wording-preview.is-empty::before { content:'Nothing written yet.'; color:var(--muted,#6d747d);
  font-family:inherit; font-style:italic; }
/* r263 -- WHAT WAS READ OUT OF AN UPLOADED FILE, ABOVE THE READING ITSELF.
   It sits between the buttons and the wording deliberately: he has to pass it
   on the way to the words, which is the whole point of showing him a reading
   before anything is saved. */
.terms-upload-report { padding:11px 14px; background:#f3f7ff; border-bottom:1px solid #d8e3f7; }
.terms-upload-report .tur-head { display:flex; align-items:baseline; gap:10px; flex-wrap:wrap; }
.terms-upload-report .tur-lead { margin:5px 0 0; }
.terms-upload-report .tur-warnings { margin:8px 0 0; padding-left:1.15em; }
.terms-upload-report .tur-warnings li { font-size:11.8px; color:#7a4a14; margin:0 0 3px; }
.terms-source-link { font-size:11.5px; color:#1f3a93; text-decoration:underline; max-width:190px;
  overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.terms-wording-preview h2, .terms-wording-preview h3 { font-size:12.4px; font-weight:700;
  letter-spacing:.02em; text-transform:uppercase; margin:1.5em 0 .5em; color:#1f3a93; }
.terms-wording-preview p { margin:0 0 .7em; }
.terms-wording-preview pre { white-space:pre-wrap; font:inherit; margin:0 0 .7em; }
.terms-wording-preview ul, .terms-wording-preview ol { margin:0 0 .7em; padding-left:1.4em; }
.terms-wording-preview table { border-collapse:collapse; margin:0 0 .8em; }
.terms-wording-preview td, .terms-wording-preview th { border:1px solid #cfd4da; padding:4px 8px; }

/* ==========================================================================
   r261 [his item 1(a)] -- THE LINES A PROBLEM OR CHARGE-BACK IS ABOUT.
   ==========================================================================
   [stated 09/09/2026] "A problem or charge-back still requires a line item or
    multiple line items to be chosen so we know what that is correlated to."

   Grouped by ORDER, because a record raised on a job can name lines from
   several -- and the order number is the thing he would say out loud, so it
   leads each group rather than sitting in a column somewhere. Wraps rather
   than truncating: this is the sentence the record exists for. */
.qe-po-cited { flex: 2 1 260px; display: flex; flex-wrap: wrap; gap: 4px 10px; }
.qe-cited-group { display: inline-flex; flex-wrap: wrap; align-items: baseline; gap: 4px 6px; }
.qe-cited-group > strong { font-size: 11.5px; letter-spacing: .02em; color: var(--ink-muted); }
.qe-cited {
  font-size: 11.5px; line-height: 1.5;
  background: #F3F1EA; border: 1px solid var(--border); border-radius: 999px;
  padding: 1px 8px; white-space: nowrap;
}
.qe-cited em { font-style: normal; color: var(--ink-muted); }
/* In the raise dialog the same markup is the only thing in the box, so it
   needs room to breathe rather than sitting on one line of a row. */
#ri-lines .qe-cited-group,
#prob-cited .qe-cited-group { margin-right: 14px; }
#ri-lines, #prob-cited { display: flex; flex-wrap: wrap; gap: 8px 14px; }

/* ==========================================================================
   r267 -- WHAT THE SAVE JUST DID, SAID OUT LOUD.
   ==========================================================================
   `stock_warnings` has come back from every purchase-order save since r217 --
   received goods taken back off the shelf, a vendor holding a different
   version of the PO, and (r265) a goods-in-transit entry a locked period
   refused to remove, which overstates accounts payable across a year end with
   the trial balance still balancing. Nothing had ever displayed one.

   Amber rather than red on purpose: none of these refuses anything. They are
   consequences of a save that went through, and the reader is being told so he
   can act, not stopped. Hidden until there is something to say, and the
   `[hidden]` companion is there because `[hidden]` is specificity zero and
   loses to any `display` rule on a class (r265). */
.save-warnings {
  display: block; margin: 14px 0 4px 0; padding: 11px 14px;
  border: 1px solid #E0A75E; background: #FFF1DC; color: #8A5200;
  border-radius: 6px; font-size: 13px; line-height: 1.55;
}
.save-warnings[hidden] { display: none !important; }
.save-warnings strong { display: block; margin-bottom: 5px; font-size: 12px; letter-spacing: .02em; }
.save-warnings ul { margin: 0; padding-left: 18px; }
.save-warnings li + li { margin-top: 5px; }

/* ==========================================================================
   r271 [stated 09/13/2026] -- THE DESIGN WORKFLOW, MADE TO READ.
   ==========================================================================
   "not have to question what button to hit, how to get to the next stage" */

/* The What's-next step carries its BUTTON now (DESIGN_STEP_BUTTONS). */
.next-step.has-action { display: inline-flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.next-step .next-step-btn { margin-left: auto; white-space: nowrap; }

/* The design job's header used to wrap its own number in half at 1500px
   ("DSN26-" / "0001") because seven long-labelled buttons shared the row.
   The number never breaks; the buttons wrap onto a second line instead. */
#design-editor .qe-topbar { flex-wrap: wrap; row-gap: 8px; }
#design-editor .qe-heading { flex: 1 1 260px; min-width: 200px; }
#design-editor #design-number { white-space: nowrap; }
#design-editor .qe-actions { flex-wrap: wrap; row-gap: 6px; }

/* The three numbered steps on the Schedule tab. */
.design-step-n {
  display: inline-block; width: 22px; height: 22px; line-height: 22px; text-align: center;
  border-radius: 50%; background: var(--primary, #1F3A93); color: #fff; font-size: 12px;
  font-weight: 700; margin-right: 8px; vertical-align: 2px; font-family: var(--font-body, inherit);
}

/* The cut-sheet cell on a design schedule row (his item 5). */
.qe-cut-cell.has-sheet { border-style: solid; background: #EEF7EE; border-color: #9FD2B0; }
.qe-cut-link { font-size: 11px; color: #1F6B39; font-weight: 600; text-decoration: none; }
.qe-cut-link:hover { text-decoration: underline; }

/* r272 -- an inline field sitting in a row of buttons (the deposit amount
   beside "+ Deposit invoice", and the counter override's reason). It reads as
   part of the action, which is the point: the value the button needs is next
   to the button, not behind a prompt. */
.inline-field {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-size: 11px;
  letter-spacing: 0.06em;
  color: var(--muted, #6b7280);
  text-transform: uppercase;
}
.inline-field input {
  text-transform: none;
  letter-spacing: normal;
  font-size: 13px;
  padding: 4px 7px;
}
/* The reason a step is not ready, said on the screen under the strip rather
   than in a modal. */
.order-next-note { font-style: italic; }
/* r272 -- the two signature facts, above the button that records them. */
.sign-name-row { display: flex; gap: 18px; flex-wrap: wrap; align-items: center; margin: 8px 0 6px; }
/* r272 -- the counter override row: the control the blocker message has been
   naming since r258, finally on the screen. */
.counter-override-row {
  display: flex; gap: 12px; align-items: center; flex-wrap: wrap;
  margin: 8px 0 10px; padding: 10px 12px;
  border: 1px solid #E0C48A; background: #FDF7EA; border-radius: 6px;
}
/* r272 -- askFields: the same dialog shape as pickFromList, with fields. */
.ask-fields .field { margin-bottom: 10px; }
.ask-fields .field > label { display: block; margin-bottom: 3px; font-size: 11px;
  letter-spacing: 0.06em; text-transform: uppercase; color: var(--muted, #6b7280); }
.ask-fields input { width: 100%; }
.ask-fields-note { color: #A32020; }

/* !! r272 -- AND BOTH OF THE ABOVE CAN ACTUALLY BE HIDDEN.
   `[hidden]` is specificity ZERO (r265), so a class that sets `display`
   beats it and an element the code has hidden goes on showing. Found by
   `check-hidden`, which has swept every hideable element in this program
   since it was written and caught both of r272's new ones on the first
   battery run -- which is what a sweep is for. */
.next-step[hidden] { display: none; }
.counter-override-row[hidden] { display: none; }

/* r272 -- the address picker in the email composer (his 09/13/2026 item). */
.email-pick-row .email-pick-controls { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
/* r291 [his item 1]: each row now ends with where the address came from
   ("... - ap@dime.example . from Project team"), and at 280px the provenance
   was cut off at the closed select -- which is the half of the row that makes
   it safe to pick from in a hurry. It takes the whole line; the three Add
   buttons wrap under it. */
.email-pick-row select { flex: 1 1 100%; min-width: 220px; max-width: 100%; }
.email-pick-row[hidden] { display: none; }

/* ==========================================================================
   r278 -- THE UPDATE SCREEN'S PROGRESS, ITS REFUSAL AND ITS PAPER LIST.
   ==========================================================================
   [stated 09/14/2026] "I update through the Settings screen, not deploy.sh,
   and I want to see the pre-flight actually refuse a bad build on that screen
   before you tell me it works."

   The bar counts phases FINISHED out of phases DECLARED -- tools/progress.js's
   rule, kept on the screen as well as in the terminal. Nothing here is driven
   by a timer, so it cannot sit at 90% while something hangs. */
.upd-bar { height: 8px; border-radius: 4px; background: #E6E8EC; overflow: hidden; margin: 14px 0 8px; }
.upd-bar-fill { height: 100%; width: 0; background: var(--brand, #1F3FB5); transition: width .25s ease; }
.upd-bar-fill.is-bad { background: #A32020; }
/* r308: the running step's own count, as a thin bar under its words. */
.upd-subbar { height: 5px; max-width: 460px; border-radius: 3px; background: #E6E8EC; overflow: hidden; margin: 2px 0 8px; }
.upd-subbar-fill { height: 100%; width: 0; background: var(--brand, #1F3FB5); opacity: .75; transition: width .25s ease; }
/* !! r281 -- THE UPLOAD, WHEN THE BROWSER WILL NOT SAY HOW FAR IT HAS GOT.
   A percentage nobody measured is a lie, and tools/progress.js's rule is that
   this program never draws one. But a bar sitting still at zero is what he
   reported as "no progress bar", so the honest third answer is a bar that says
   WORKING rather than a number: moving stripes, no width claim. The moment the
   first byte count arrives the class comes off and it becomes a real
   measurement. */
.upd-bar-fill.is-working { width: 100%; background-image: linear-gradient(115deg,
  rgba(255,255,255,.00) 0 12px, rgba(255,255,255,.35) 12px 24px);
  background-size: 28px 100%; animation: upd-stripe 1s linear infinite; }
@keyframes upd-stripe { from { background-position: 0 0; } to { background-position: 28px 0; } }
@media (prefers-reduced-motion: reduce) { .upd-bar-fill.is-working { animation: none; } }
.upd-log { max-height: 230px; overflow: auto; background: #12161C; color: #D8DEE9;
  padding: 10px 12px; border-radius: 6px; font-size: 11.5px; line-height: 1.5;
  white-space: pre-wrap; margin: 8px 0 0; }
.upd-finding { border-left: 3px solid #A32020; padding: 6px 0 6px 10px; margin: 10px 0; }
.upd-finding .upd-what { font-weight: 700; }
.upd-finding .upd-detail { font-size: 12.5px; }
.upd-finding .upd-eg { font-size: 12px; font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--muted, #5B6068); }
/* [hidden] is specificity ZERO (r265), and every one of these is an element the
   code hides. check-hidden sweeps for exactly this. */
#upd-progress[hidden], #upd-refusal[hidden], #upd-paper[hidden], #upd-log-wrap[hidden],
#upd-fail[hidden], #upd-result[hidden] { display: none; }
/* r288: the refusal, under the steps, where he is looking. */
.upd-fail { color: #A32020; font-weight: 700; font-size: 13px; margin: 6px 0 0;
  padding: 8px 10px; background: #FBE9E9; border-left: 3px solid #A32020; border-radius: 4px; }
.upd-step { white-space: nowrap; font-size: 12px; }
.upd-step-done { color: #1F7A3D; }
.upd-step-running { font-weight: 700; }
.upd-step-failed { color: #A32020; font-weight: 700; }
.upd-step-pending { color: var(--muted, #8b9099); }
.upd-notes { margin-top: 10px; }
.upd-notes > summary { cursor: pointer; font-size: 12px; color: var(--muted, #5B6068); }

/* ==========================================================================
   r285 [his words, 09/16/2026] -- THE INVOICE TOTALS, LINED UP UNDER THE LINES.
   "tax and shipping and all of that should be lined better to input them if
   needed and a running total on the bottom". One column of label and figure,
   right-aligned under the grid like the paper's own totals block, and the
   total ruled off at the foot.
   ========================================================================== */
.inv-totals { margin: 10px 0 18px auto; max-width: 440px; display: flex; flex-direction: column; gap: 6px; }
.inv-totals-row { display: flex; align-items: center; justify-content: space-between; gap: 14px; }
/* r291: an explicit `display` beats the browser's own [hidden]{display:none},
   so a row hidden in JS kept showing. The r291 walk's picture caught a
   "Why is it exempt? Required" box sitting on an invoice nobody was asked
   about, while the code that hid it read as having worked. */
.inv-totals-row[hidden] { display: none; }
.inv-totals-k { font-size: 13px; color: var(--ink, #1C2127); display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.inv-totals-hint { font-size: 11.5px; color: var(--ink-muted); }
.inv-totals-v { width: 160px; text-align: right; font-variant-numeric: tabular-nums; }
.inv-totals-v[readonly] { background: #F3F1EC; color: var(--ink); cursor: default; }
.inv-totals-rate { display: inline-flex; align-items: center; gap: 4px; }
.inv-totals-rate input { width: 78px; text-align: right; }
.inv-totals-total { border-top: 2px solid var(--ink, #1C2127); padding-top: 8px; margin-top: 2px; }
.inv-totals-total .inv-totals-k { font-weight: 700; font-size: 14px; }
.inv-totals-total .inv-totals-v { font-weight: 700; }
.inv-totals > .settings-sub { margin: 2px 0 0; text-align: right; }

/* r285 -- the card-fee tick on the invoice, and the payment window's form. */
.inv-card-fee { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin: 4px 0 10px; }
.inv-card-fee[hidden] { display: none; }
.inv-card-fee .tickline { display: inline-flex; align-items: center; gap: 6px; font-size: 13px; }
.inv-card-fee .settings-sub { margin: 0; }

/* ==========================================================================
   r287 -- CANCELLING A DESIGN AGREEMENT, AND THE RECORD OF IT.
   ========================================================================== */
.design-cancel-banner { margin-top: 10px; padding: 12px 14px; border-radius: 6px; font-size: 13px;
  border: 1px solid #E3B8B3; background: #FBF1EF; }
.design-cancel-banner[hidden], .design-cancel-panel[hidden] { display: none; }
.design-cancel-banner.is-history { border-color: var(--border); background: var(--surface); }
.design-cancel-banner .dcb-head { font-weight: 700; color: #8A1A1A; letter-spacing: .02em; }
.design-cancel-banner.is-history .dcb-head { color: var(--ink); }
.design-cancel-banner .dcb-row { margin-top: 8px; padding-top: 8px; border-top: 1px solid var(--border); }
.design-cancel-banner .dcb-row:first-of-type { border-top: 0; padding-top: 0; }
.design-cancel-banner .dcb-line { color: var(--ink-muted); margin-top: 2px; }
.design-cancel-banner .dcb-warn { color: #8A5200; margin-top: 4px; }
.design-cancel-panel { margin-top: 10px; padding: 14px 16px; border: 1px solid #C9534A; border-radius: 6px;
  background: #fff; font-size: 13px; }
.design-cancel-panel h3 { margin: 0 0 6px; font-size: 15px; }
.design-cancel-panel .dcp-lead { color: var(--ink-muted); margin: 0 0 10px; }
.dcp-inv { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; padding: 8px 0;
  border-top: 1px solid var(--border); }
.dcp-inv .dcp-num { font-weight: 700; min-width: 120px; }
.dcp-inv .dcp-fig { color: var(--ink-muted); font-variant-numeric: tabular-nums; }
.dcp-inv .dcp-choices { display: flex; gap: 14px; margin-left: auto; }
.dcp-inv .dcp-choices label { display: inline-flex; align-items: center; gap: 5px; cursor: pointer; white-space: nowrap; }
.dcp-inv .dcp-notes { flex-basis: 100%; color: #8A5200; font-size: 12px; }
.dcp-other { color: var(--ink-muted); padding: 4px 0; }
.dcp-reason { width: 100%; min-height: 54px; margin-top: 8px; }
.dcp-actions { display: flex; gap: 8px; margin-top: 10px; flex-wrap: wrap; }
.dcp-note { margin-top: 8px; color: #9A2A1F; }
.dcp-note:empty { display: none; }

/* ==========================================================================
   r289 -- NAMED SALES-TAX RATES.
   ========================================================================== */
.tax-default-row { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px; margin: 10px 0 14px; }
.tax-default-label { font-weight: 700; font-size: 13px; }
.tax-default-row select { min-width: 280px; max-width: 100%; }
.tax-rate-table td { vertical-align: middle; }
.tax-rate-table td:nth-child(4) { max-width: 320px; overflow-wrap: anywhere; }
.tax-row-actions { white-space: nowrap; text-align: right; }
.tax-default-tag { background: #E4EFE6; color: #24793E; }
.tax-rate-msg { margin: 8px 0; padding: 8px 10px; border-radius: 6px; font-size: 13px; }
.tax-rate-msg[hidden] { display: none; }
.tax-rate-msg.is-good { background: #E4EFE6; color: #1F5E31; }
.tax-rate-msg.is-bad { background: var(--danger-soft, #F6E4DE); color: var(--danger, #B84A2F); font-weight: 600; }
.tax-show-retired { margin: 4px 0 12px; }
.tax-add-head { margin: 16px 0 6px; font-size: 15px; }
.tax-rate-form { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 8px 12px; }
.tax-rate-form .field { margin: 0; }
.tax-rate-form #tax-new-name { min-width: 240px; }
.tax-rate-form #tax-new-rate { width: 90px; }
.tax-rate-form #tax-new-code { width: 110px; }
.tax-rate-form #tax-new-reason { min-width: 280px; }
.tax-new-reason-field[hidden] { display: none; }
.tax-suggest, .tax-stale { margin-top: 18px; padding: 12px 14px; border: 1px solid var(--border, #E3E0D8); border-radius: 6px; }
.tax-suggest[hidden], .tax-stale[hidden] { display: none; }
.tax-suggest h3, .tax-stale h3 { margin: 0 0 6px; font-size: 14px; }
.tax-stale ul { margin: 6px 0 0 18px; padding: 0; font-size: 13px; }
.tax-stale li { margin: 3px 0; }
/* The flag on an open quote whose rate Settings has moved (his answer 3). */
.tax-flag { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 14px; margin: 4px 0 12px;
  padding: 9px 12px; border: 1px solid #E0B25C; background: #FBF1DC; border-radius: 6px; color: #6B4700; font-size: 13px; }
.tax-flag[hidden] { display: none; }
.tax-flag .btn[hidden] { display: none; }
#qe-tax-rate-id, #order-tax-rate-id, #cust-tax-rate-id, #proj-tax-rate-id, #qe-cust-tax-rate-id { width: 100%; }
.proj-tax-rate-field { max-width: 460px; }
.inv-tax-select { max-width: 200px; }
.tax-rate-table th:first-child, .tax-rate-table td:first-child { min-width: 220px; }
.tax-rate-table td:nth-child(3) { white-space: nowrap; }

/* r289: the RFQ's own notes on the Print / Email window. */
.rfq-export-notes-field { margin-top: 12px; }
.rfq-export-notes-field textarea { width: 100%; box-sizing: border-box; font-family: inherit; resize: vertical; }

/* ==========================================================================
   r292 -- THE ORDER'S NEXT LINE, AND THE FULFILMENT TAB.
   ==========================================================================
   [stated 09/18/2026] "We have so many steps from all different windows and
   tabs that we have to click and remember to do and that's how steps get
   missed and not done correctly."

   One line, under the tab bar and outside the panels, so whichever tab is open
   the next thing to do is on the screen. It is deliberately quiet: this is not
   a banner competing with the credit warning above it, it is the sentence you
   read before you decide which tab to open. */
.order-next-line {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 12px; margin: 0 0 12px;
  border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: 6px; background: var(--surface-2, #FAFAFB);
}
/* `display: flex` beats `[hidden]`, so a hidden flex box stays on the screen.
   The sweep in tests/check-hidden.js catches it; this is the answer. */
.order-next-line[hidden] { display: none; }
.order-next-line .onl-cap {
  font-size: 10px; font-weight: 700; letter-spacing: 0.09em; text-transform: uppercase;
  color: var(--ink-muted);
}
.order-next-line .onl-say { flex: 1 1 260px; }
.order-next-line .onl-then { color: var(--ink-muted); font-size: 12px; }
/* Held goods are the one state this line raises its voice for: it is money
   owed against goods standing on a shelf, and it is the state a person is most
   likely to work around by accident. */
.order-next-line.stage-held { border-left-color: #C0392B; }
.order-next-line.stage-ready { border-left-color: #E08A1E; }
.order-next-line.stage-done { border-left-color: #2E7D4F; }

.fulfilment-panel { margin-bottom: 18px; }
.fulfilment-panel .ff-head { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; }
.fulfilment-panel .ff-dest { color: var(--ink-muted); font-size: 12px; }
.fulfilment-panel .ff-held {
  border: 1px solid #E8C4BE; background: #FDF3F1; border-radius: 6px;
  padding: 10px 12px; margin-bottom: 14px;
}
.fulfilment-panel .ff-held p { margin: 5px 0 0; }
/* !! THE WAY FORWARD IS NOT SMALL PRINT (r291). A refusal with no way out is a
   dead end he cannot get past, so the sentence that says what to do about it
   is set at the same size as the one that says no. */
.fulfilment-panel .ff-way { font-weight: 600; }
.fulfilment-panel .ff-counts {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: 10px; margin-bottom: 14px;
}
.fulfilment-panel .ff-counts > div {
  border: 1px solid var(--line); border-radius: 6px; padding: 9px 11px;
}
.fulfilment-panel .ff-n { display: block; font-size: 19px; font-weight: 700; line-height: 1.2; }
.fulfilment-panel .ff-cap {
  display: block; font-size: 10.5px; text-transform: uppercase; letter-spacing: 0.05em;
  color: var(--ink-muted); margin-top: 2px;
}
.fulfilment-panel .ff-papers { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }

/* The stage pills. `status-ready` already exists (r291); these are its
   neighbours, and they follow the same rule he settled at r76 -- done is
   green, the next thing to do is orange, blocked is red. */
.status-pill.status-held { background: #FBE6E2; color: #8C2F22; }
.status-pill.status-done { background: #E3F3E8; color: #26603F; }
.status-pill.status-on_order { background: #ECEEF1; color: #4A5058; }
.status-pill.status-partly_out { background: #FFF1DC; color: #7A5314; }
.status-pill.status-unsigned,
.status-pill.status-awaiting_signature { background: #FDF3DC; color: #7A5314; }

/* ==========================================================================
   r292 -- THE DRIVER'S SCREEN. PHONE-SHAPED, BECAUSE IT IS USED ON A PHONE.
   ==========================================================================
   [stated 09/19/2026] "they sign in withtheir credentials and it just shows
   what deliveries and where they are going and important inforation on it."

   Everything here is sized for a thumb at a loading dock: the tap targets are
   44px or more, the address is the biggest thing on the stop, and nothing needs
   a second hand. It is the only screen in this program laid out phone-first --
   the rest of the office screens are r293's job, and his words about those are
   in the backlog ("I can't sign out of my mobile app from lumencore cause i
   cant see 3/4 of the page!"). */
.driver-screen { max-width: 620px; margin: 0 auto; padding: 14px 14px 60px; }
.driver-screen .dr-top { display: flex; align-items: center; gap: 10px; margin-bottom: 14px; }
.driver-screen .dr-top h1 { flex: 1; font-size: 20px; margin: 0; }

.dr-list { display: flex; flex-direction: column; gap: 10px; }
.dr-list[hidden] { display: none; }
.dr-stop {
  display: block; width: 100%; text-align: left; cursor: pointer;
  border: 1px solid var(--line); border-radius: 8px; background: var(--surface, #fff);
  padding: 13px 14px; font: inherit; color: inherit;
}
.dr-stop:hover { border-color: var(--accent); }
.dr-stop.is-done { opacity: 0.62; }
.dr-stop-who { font-weight: 700; font-size: 16px; }
.dr-stop-where { margin-top: 2px; }
.dr-stop-meta { display: flex; flex-wrap: wrap; gap: 10px; margin-top: 6px; font-size: 12px; color: var(--ink-muted); }
.dr-stop-call { margin-top: 6px; font-size: 12.5px; font-weight: 600; }
.dr-tag {
  border: 1px solid var(--line); border-radius: 10px; padding: 1px 7px;
  text-transform: uppercase; letter-spacing: 0.05em; font-size: 10px;
}
.dr-tag-done { background: #E3F3E8; color: #26603F; border-color: #BEDFC9; }
.dr-empty { color: var(--ink-muted); padding: 18px 2px; }

.dr-run-head .dr-addr { font-size: 17px; line-height: 1.45; margin-bottom: 8px; }
.dr-run-head .dr-addr .dr-attn { font-weight: 700; }
.dr-call { margin: 10px 0; font-size: 15px; }
.dr-call strong { display: block; font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-muted); }
.dr-call a { font-weight: 700; }
.dr-meta { display: flex; flex-wrap: wrap; gap: 10px; margin: 10px 0; font-size: 12.5px; color: var(--ink-muted); }
.dr-notes { border-left: 3px solid var(--line); padding-left: 10px; margin: 10px 0; }
.dr-paper { font-size: 12.5px; color: var(--ink-muted); }
.dr-load h2, .dr-sign h2 { font-size: 13px; text-transform: uppercase; letter-spacing: 0.06em; color: var(--ink-muted); margin: 18px 0 6px; }
.dr-pkgs { list-style: none; margin: 0; padding: 0; }
.dr-pkgs li { border-bottom: 1px solid var(--line); padding: 9px 2px; }
.dr-pkg-n { float: right; font-variant-numeric: tabular-nums; }

.dr-pad-wrap { position: relative; margin: 8px 0 10px; }
/* The pad is a box you sign in, so it looks like one: a ruled area with room
   for a real signature, not a text input pretending to be one. */
#driver-sigpad {
  width: 100%; border: 1px dashed var(--line); border-radius: 8px;
  background: #fff; touch-action: none;
}
.dr-photos { display: flex; gap: 8px; flex-wrap: wrap; margin: 10px 0; }
.dr-photos .btn { min-height: 44px; display: inline-flex; align-items: center; }
.dr-files { display: flex; gap: 8px; flex-wrap: wrap; }
.dr-file { font-size: 12px; border: 1px solid var(--line); border-radius: 10px; padding: 3px 9px; }
.dr-msg { margin: 10px 0; font-weight: 600; }
.dr-msg.is-bad { color: #A3291B; }
.dr-done { width: 100%; min-height: 50px; font-size: 16px; margin-top: 6px; }
.dr-notify { border-top: 1px solid var(--line); margin-top: 18px; padding-top: 14px; }
.dr-notify .btn { width: 100%; min-height: 46px; }
.dr-signed-note { margin: 0 0 10px; font-weight: 600; }

/* r292 [his item 12]: the files that travel with an RFI -- what went out with
   the question and what came back with the answer. */
.rfi-files-block { margin-top: 20px; }
.rfi-file-list { display: flex; flex-direction: column; gap: 6px; }
.rfi-file-row {
  display: flex; align-items: center; gap: 10px;
  border: 1px solid var(--line); border-radius: 6px; padding: 7px 10px;
}
.rfi-file-row a { flex: 0 0 auto; font-weight: 600; }
/* r296 [his items 3 and 5]: the name is an input because he asked to RENAME,
   and the question is a select because a number that does not exist must not
   be typeable. The name takes the room the link used to, since it is the thing
   on the row somebody reads. */
.rfi-file-row .rfi-file-no { font-weight: 700; color: var(--muted, #6b7280); flex: 0 0 auto; }
.rfi-file-row .rfi-file-name { flex: 1 1 auto; min-width: 140px; }
.rfi-file-row .rfi-file-q { flex: 0 0 auto; max-width: 200px; }
/* r304: the × that takes a file off a record, IN its row. It used to borrow
   .qe-img-clear, which is absolutely positioned for the quote's picture cell
   -- every row's × piled up in the window's top-right corner and the one on
   top was the last file's. A plain button in the flow cannot leave its row. */
.row-file-del {
  flex: 0 0 auto; width: 22px; height: 22px; line-height: 18px; padding: 0;
  border-radius: 4px; border: 1px solid var(--border); background: #fff;
  color: var(--ink-muted); font-size: 15px; cursor: pointer;
}
.row-file-del:hover { color: var(--danger); border-color: #E5B8B8; }
/* r296 [stated]: "a space next to the answer that we enter who answered that
   question". Beside it, not under it -- it is a property of the answer. */
.rfi-answer-line-row { display: flex; gap: 8px; align-items: flex-start; }
.rfi-answer-line-row .rfi-answer-field { flex: 1 1 auto; }
.rfi-answer-line-row .rfi-answered-by { flex: 0 0 210px; align-self: flex-start; }

/* ==========================================================================
   r292 -- A DRIVER'S PHONE IS NOT AN OFFICE SCREEN.
   ==========================================================================
   [stated, still open at r292] "I can't sign out of my mobile app from
   lumencore cause i cant see 3/4 of the page! This needs to be fixed so I can
   view on my phone."

   The office screens' phone layout is its own job (r293). This is the one
   account that is ONLY ever on a phone, so it gets the whole width now: no
   sidebar (they have one screen), no project search (it reads documents a
   driver is refused), and the sign-out where a thumb can reach it. */
body.is-driver .sidebar,
body.is-driver .sidebar-toggle,
body.is-driver .job-search-bar,
body.is-driver .job-search-results { display: none !important; }
body.is-driver .topbar { padding: 8px 14px; }
body.is-driver .content-col { width: 100%; }
body.is-driver .driver-screen { padding-top: 6px; }
/* The two uploads are buttons a wet thumb has to hit, so they look and size
   like buttons rather than like labels that happen to be clickable. */
.dr-photos .btn {
  border: 1px solid var(--line); border-radius: 8px; padding: 10px 14px;
  background: var(--surface, #fff); font-weight: 600; cursor: pointer;
}
.dr-photos .btn:hover { border-color: var(--accent); }

/* ==========================================================================
   r293 -- SIGNED-IN DEVICES. [stated 09/19/2026] "in admin settings there
   should be a way to log out of all logged in sessions forcing anyone logged
   into have to log back in again."
   ========================================================================== */
.sessions-list { list-style: none; margin: 8px 0 10px; padding: 0;
  max-height: 220px; overflow: auto; }
.sessions-row { display: flex; gap: 10px; align-items: baseline; flex-wrap: wrap;
  padding: 5px 0; border-bottom: 1px solid var(--border, #e6e8eb); font-size: 12.5px; }
.sessions-row:last-child { border-bottom: 0; }
.sessions-who { font-weight: 600; min-width: 140px; }
.sessions-agent { flex: 1 1 260px; min-width: 0; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }

/* r293: a settings box that grows with what is in it. The JS sets the height;
   this stops the browser's own resize handle fighting it and keeps the box
   readable at the width of the field it sits in. */
textarea.grow-with-content { resize: vertical; min-height: 4.2em; line-height: 1.5;
  overflow-y: hidden; }

/* ==========================================================================
   !! r293 -- THE PHONE. HIS WORDS, TWICE.
   ==========================================================================

   [stated 09/19/2026] "I can't sign out of my mobile app from lumencore cause
   i cant see 3/4 of the page! This needs to be fixed so I can view on my
   phone."

   HE WAS RIGHT ABOUT THE SIGN-OUT BUTTON IN THE MOST LITERAL WAY POSSIBLE.
   The rule directly above this one -- which has been in the file since the
   first responsive pass -- contains `.sidebar-foot { display: none; }`, and
   `.sidebar-foot` is the box the Sign out button lives in. On any screen
   narrower than 720px the button was not hard to find, small, or off the
   edge. It was `display: none`. Nothing in the program could have told him
   that, because nothing had ever opened it at 390px and LOOKED (r286).

   AND THE REST OF HIS SENTENCE WAS RIGHT TOO. Measured at 390x844, the 76px
   icon rail above takes a fifth of the width to show a column of section
   HEADINGS -- "SALES", "PURCHASING", "ACCOUNTING" -- clipped mid-word,
   because those are not `.nav-label`s and were never hidden. So a phone got
   a useless rail, no labels to tap, no sign-out, and 240px of content.

   WHAT THIS DOES, and it is deliberately not a rebuild (the r292 note: "Do
   not rebuild them"):

     * the sidebar becomes a SLIDE-OVER DRAWER with its real labels back and
       the sign-out button back, opened by the hamburger that is already
       there;
     * the content column gets the whole width, at a 16px gutter;
     * anything that was a fixed grid becomes one column;
     * the record tabs and wide tables scroll sideways under a finger instead
       of being cut off;
     * tap targets go to 44px, which is the number r292 used for the driver's
       screen -- one number for the whole program, not one per screen.

   560px, not 720px: a 600-700px tablet in portrait has room for the rail and
   is better off with it. A phone does not. */
@media (max-width: 560px) {
  /* ---- the drawer ---------------------------------------------------- */
  .sidebar {
    position: fixed; top: 0; left: 0; bottom: 0;
    width: min(82vw, 290px);
    z-index: 70;
    padding: 56px 12px 20px;
    transform: translateX(-102%);
    transition: transform .18s ease-out;
    box-shadow: 0 0 40px rgba(0, 0, 0, .35);
  }
  body.nav-open .sidebar { transform: none; }
  /* !! THE THREE THINGS THE 720px RULE HID, PUT BACK. A drawer with no
     labels and no sign-out is the same screen he complained about, wearing
     an animation. */
  .brand-word, .nav-label { display: inline; }
  .sidebar-foot { display: block; margin-top: 18px; }
  .nav-item { justify-content: flex-start; min-height: 44px; }
  /* r310: the group headings the left panel gained this round are buttons a
     thumb opens and closes -- the same 44px as the links under them (r292's
     one number). They were 29px, and encounter 13's tap-target check said so. */
  .nav-group-header { min-height: 44px; }
  .sidebar .brand { padding-left: 4px; }
  .sidebar-signout { min-height: 44px; padding: 0 16px; }
  .sidebar-user { flex-wrap: wrap; gap: 10px; }

  /* The scrim. It is a real element (added by app.js) rather than a
     ::after on body, because it has to be tappable to close the drawer and
     a pseudo-element on body sits under everything that follows. */
  .nav-scrim {
    position: fixed; inset: 0; z-index: 65;
    background: rgba(20, 24, 30, .45);
    opacity: 0; pointer-events: none; transition: opacity .18s ease-out;
  }
  body.nav-open .nav-scrim { opacity: 1; pointer-events: auto; }

  /* The hamburger is the only way back to the nav, so it is always on top of
     the drawer and always a finger's width. */
  .sidebar-toggle {
    width: 44px; height: 44px; font-size: 18px; z-index: 80;
    background: var(--brand-blue); border-color: var(--brand-blue);
  }
  /* A collapsed sidebar is a DESKTOP preference and must not decide anything
     here -- remembered from a laptop it would have left the drawer
     permanently shut with no way to open it. */
  body.sidebar-collapsed .sidebar { width: min(82vw, 290px); padding: 56px 12px 20px; overflow-y: auto; }

  /* ---- the content gets the width ------------------------------------ */
  :root { --module-gutter: 16px; --module-gutter-top: 14px; --module-gutter-bottom: 20px; }
  .content-col { width: 100%; min-width: 0; }
  .app > .content-col { margin-left: 0; }
  /* The hamburger is fixed over the top-left corner, so the first thing on
     the page needs to start below it. */
  .job-search-bar { padding-left: 56px; }
  body.is-driver .job-search-bar { padding-left: 0; }

  .topbar { flex-direction: column; align-items: stretch; gap: 10px; }
  .topbar h1 { font-size: 21px; }
  .topbar-actions { flex-wrap: wrap; }
  #search { width: 100%; }

  /* ---- one column, everywhere it was two or more --------------------- */
  .settings-grid, .qe-cust-fields, .proj-two-col, .proj-tax-fields,
  .cust-contact-split, .home-grid, .kpi-row, .acct-grid, .qe-head-grid,
  .two-col, .three-col { grid-template-columns: 1fr !important; }

  /* ---- wide things scroll under a finger ----------------------------- */
  .table-wrap, .qe-table-wrap, .data-table-wrap, .settings-log-wrap {
    overflow-x: auto; -webkit-overflow-scrolling: touch; }
  /* !! r294 -- THE TAB BAR HAD NO HEIGHT AT ALL ON A PHONE, AND THE BANNER
     UNDER IT ATE TWO-THIRDS OF EVERY TAB.

     MEASURED at 390x844 on the order screen: `#order-record-tabs` came back
     0px tall while its buttons were 44px, so the buttons overflowed out of a
     container that reserved nothing -- and `#order-locked-banner`, which
     follows it in the flow, was drawn over their lower two-thirds.
     `document.elementFromPoint` at the centre of the Money tab returned the
     BANNER, and Playwright refused the click with "order-locked-banner
     intercepts pointer events". A person's thumb would have been refused in
     exactly the same way, silently.

     WHY IT WAS ZERO. r293 made the bar scroll sideways under a finger
     (`overflow-x: auto`) and gave its buttons the 44px tap target. The 44px
     arrives as `min-height` on a stretched flex ITEM, which does not
     contribute to the height of a scroll-container flex PARENT -- so the
     parent sized to nothing while the children stood 44px tall inside it.
     At 1400px the bar has no `overflow-x` and measures 32px, which is why
     nothing had ever shown it.

     The fix is to say what the bar's height IS rather than let it be
     inferred: it holds a row of 44px targets, so it is at least that tall,
     and they sit in the middle of it.

     Found by encounter 13, which is the first thing that ever tried to open
     a job on a phone and read the money on it. */
  .record-tabs, .qe-nav, .order-tabs, .tabs {
    overflow-x: auto; flex-wrap: nowrap; -webkit-overflow-scrolling: touch;
    scrollbar-width: none;
    align-items: center; min-height: 46px; }
  .record-tabs::-webkit-scrollbar, .qe-nav::-webkit-scrollbar,
  .order-tabs::-webkit-scrollbar, .tabs::-webkit-scrollbar { display: none; }
  .record-tabs > *, .qe-nav > *, .order-tabs > *, .tabs > * { flex: 0 0 auto; }

  /* ---- 44px, the same number the driver's screen uses ---------------- */
  .btn, .nav-item, .record-tabs > button, .order-tabs > button,
  .qe-nav-tab, select, input[type="text"], input[type="number"],
  input[type="date"], input[type="password"], input[type="email"] { min-height: 44px; }
  .btn-sm { min-height: 38px; }
  .row-del-btn { min-width: 38px; min-height: 38px; }

  /* !! r294 -- TWO REAL BUTTONS WERE STILL TOO SMALL TO HIT, AND THE 44px
     RULE ABOVE COULD NOT SEE THEM BECAUSE NEITHER CARRIES `.btn`.

     Measured at 390x844 by encounter 13, which walks a day's work on a
     phone: the alternates AI button is 17x26 and the reconciliation dot is
     9x9. Both are real `<button>`s a person is meant to press -- the dot
     carries `data-recon-type` and opens what it is about.

     The dot is the interesting one. Growing the ELEMENT to 44px would draw
     a 44px coloured square where a 9px dot belongs, so what grows is its
     HIT AREA: the dot stays 9px and an invisible 44px target is centred on
     it. That is the standard answer and it is the only one that keeps both
     halves of what he asked for -- a screen that reads right and a screen
     a thumb can use. */
  .qe-alt-ai { min-height: 44px; min-width: 44px; }
  /* r293's banner clamp arrow. It is the control that opens a warning about
     money, so it is not a control to make somebody aim at. */
  .banner-more-btn { min-height: 44px; min-width: 44px; }
  .recon-dot { position: relative; }
  .recon-dot::after {
    content: ''; position: absolute; left: 50%; top: 50%;
    width: 44px; height: 44px; transform: translate(-50%, -50%);
  }

  /* A modal on a phone is the screen, not a card floating in the middle of
     it -- a 24px inset on 390px leaves a dialog nobody can read. */
  .export-modal { padding: 0; align-items: flex-end; }
  .export-dialog { max-width: none; max-height: 92vh; border-radius: 12px 12px 0 0; }
}

/* ==========================================================================
   r293 -- THE ORDER SCREEN, AFTER HIS 09/19 NOTE.
   ==========================================================================
   "these should be a few words and if we click an arrow on it it can drop
   down and we can read the whole thing. I don't need all of that shown all
   the time."  */

/* ============================================================================
   r298 -- THE GAPS BETWEEN THE STACKED BLOCKS, MEASURED.
   ============================================================================
   [stated 09/21/2026] "i dont want this shit taking up 3/4 o the screen like i
   told you 3 rounds ago!"

   MEASURED at 1500x1000 on an order that is over its limit, on hold and
   disagreeing with its submittal: the first editable field sat 755px down a
   1000px screen. 76%. Four of those blocks stack above it, and the GAPS
   between them came to roughly 110px on their own -- one of them 64px --
   because each block carries its own bottom margin and the next its own top.
   Collapsing four stacked warnings into one rhythm costs nothing and it is
   about a tenth of the screen back. */
#order-locked-banner, #order-credit-banner, #order-reconcile-banner,
#order-rfq-panel, #order-next-strip, #order-next-line { margin-top: 10px; margin-bottom: 0; }
#order-record-tabs { margin-bottom: 0; }
#order-locked-banner + *, #order-credit-banner + *, #order-reconcile-banner + *,
#order-rfq-panel + *, #order-next-strip + *, #order-next-line + *,
#order-record-tabs + * { margin-top: 10px; }

/* The clamp. The text is all there; only its height is decided here, so
   find-on-page and screen readers still reach every word. */
.order-locked-banner, .credit-banner { position: relative; }
.order-locked-banner.is-clamped:not(.is-open),
.credit-banner.is-clamped:not(.is-open) {
  max-height: 2.9em; overflow: hidden;
}
.order-locked-banner .banner-more,
.credit-banner .banner-more,
#order-next-strip .banner-more,
#order-rfq-panel .banner-more { display: none; }
.order-locked-banner.is-open .banner-more,
.credit-banner.is-open .banner-more,
#order-next-strip.is-open .banner-more,
#order-rfq-panel.is-open .banner-more { display: block; margin-top: 6px; }
.banner-lede { font-weight: 500; }
.banner-more-btn {
  border: 1px solid currentColor; background: transparent; color: inherit;
  border-radius: 5px; cursor: pointer; line-height: 1;
  width: 26px; height: 26px; flex: 0 0 auto; margin-left: auto; opacity: .75;
}
.banner-more-btn:hover { opacity: 1; }
/* ============================================================================
   r298 -- WHERE THE ARROW SITS ON A BANNER THAT IS NOT A FLEX ROW.
   ============================================================================
   The signed banner is a flex row and `margin-left: auto` puts its chevron on
   the right of the one line. The credit and reconcile banners are BLOCKS with a
   heading, so the same button landed underneath the text -- and, while the
   first attempt was clamping them by height, INSIDE the part being hidden,
   which is a banner cut in half with no way to open it.
   They are already `position: relative`, so the button is pinned to the corner
   where he is looking for it, and it is outside `.banner-more` so it can never
   be what the arrow hides. */
.credit-banner > .banner-more-btn,
.recon-banner > .banner-more-btn,
#order-next-strip > .banner-more-btn,
#order-rfq-panel > .banner-more-btn {
  position: absolute; top: 8px; right: 8px; margin-left: 0;
}
#order-next-strip, #order-rfq-panel { position: relative; }
/* ...and the summary line must not run under it. */
.credit-banner > h3, .credit-banner > p,
.recon-banner > .recon-banner-head { padding-right: 34px; }
/* r298: the second flag on the heading, when an account is over its limit AND
   on hold. Same size, so the heading still reads as one fact followed by
   another rather than a heading with a badge stuck on it. */
.credit-banner > h3 .credit-also { font-weight: 600; }

/* The two acts that used to sit in the banner on every tab. They are on the
   Order tab now -- "It goes in the order with a simple button." */
.order-stop-row { display: flex; gap: 8px; flex-wrap: wrap; margin: 0 0 14px; }
.order-stop-row:empty { display: none; }

/* r293: STARTED, NOT FINISHED. r76 gave the strip green for done, orange for
   do-it-now and red for blocked, and there was no colour for "raised, waiting
   on somebody". Both of the steps he caught were painting that state green.
   Blue, because it is the one thing on this strip that needs nothing from
   him right now. */
.next-step.waiting {
  background: #E8EEF9; border-color: #A8BEE2; color: #26457F; font-weight: 600;
}

/* check-hidden: a display rule on a class beats the hidden attribute, so
   anything this stylesheet shows has to say how it hides. */
.nav-label[hidden] { display: none; }
.banner-more[hidden] { display: none; }

/* r300 [his yes, 09/21/2026, from the before/after render] -- THE TWO "WHAT'S
   NEXT" BLOCKS SHARE ONE BOX. r292's sentence-and-one-button line and the
   older stage-chip strip said overlapping things in two bordered boxes,
   126px together on every order. One box now: the sentence and its button on
   the first row, the chips on the second, the repeated "What's next" heading
   gone. 88px. The stage colour on the left edge (red held, orange ready,
   green done) moves to the box. Both children keep their own painters and
   their own hidden flags; the box hides itself when both are hidden. */
.order-next-box {
  border: 1px solid var(--line); border-left: 3px solid var(--accent);
  border-radius: 6px; background: var(--surface-2, #FAFAFB);
  padding: 8px 12px; margin: 10px 0 12px;
}
.order-next-box:not(:has(> :not([hidden]))) { display: none; }
.order-next-box .order-next-line { border: 0; padding: 0; margin: 0; background: transparent; border-radius: 0; }
.order-next-box .next-strip { border: 0; padding: 0; margin: 0; background: transparent; }
.order-next-box .order-next-line:not([hidden]) + .next-strip:not([hidden]) { margin-top: 7px; }
.order-next-box .next-strip h3 { display: none; }
.order-next-box .next-step { padding: 4px 9px; font-size: 12px; }
.order-next-box:has(> .order-next-line.stage-held) { border-left-color: #C0392B; }
.order-next-box:has(> .order-next-line.stage-ready) { border-left-color: #E08A1E; }
.order-next-box:has(> .order-next-line.stage-done) { border-left-color: #2E7D4F; }

/* r300 -- Install from GitHub, above the upload box on Settings > Update. */
.upd-github { border: 1px solid var(--line); border-radius: 6px; padding: 10px 14px; margin: 8px 0 14px; background: var(--surface-2, #FAFAFB); }
.upd-github-head { margin: 0 0 4px; font-size: 13px; }
.upd-github-setup { margin-top: 8px; }
.upd-github-setup summary { cursor: pointer; font-size: 12.5px; color: var(--ink-muted); }
/* r313: what's in each update -- under Check GitHub, in a chosen file's
   banner, and under "Currently running build". No `display` here: these
   blocks are shown and hidden by the hidden attribute (tests/check-hidden.js). */
.upd-whatsnew { margin: 8px 0 6px; }
.upd-whatsnew-head { font-weight: 700; font-size: 13px; margin: 0 0 2px; }
.upd-whatsnew-when { font-weight: 400; font-size: 12px; color: var(--ink-muted); margin-left: 4px; }
.upd-whatsnew-round { font-size: 12px; font-weight: 600; color: var(--ink-muted); margin: 6px 0 0; }
.upd-whatsnew-list { margin: 2px 0 0 18px; padding: 0; font-size: 13px; line-height: 1.45; }
.upd-whatsnew-list li { margin: 1px 0; }

/* ========================================================================
   r305 -- THE CONTACT BOOK. Contacts, Companies, and a card for each.
   Built from the program's own pieces (the .qtoggle chips of the quotes
   screen, the global table, .tag) so it reads as part of the same program.
   ======================================================================== */
.crm-filters { display: flex; flex-direction: column; gap: 8px; margin: 4px 0 14px; }
.crm-filters[hidden] { display: none; }
#crm-search { max-width: 520px; padding: 9px 12px; border: 1px solid var(--border); border-radius: var(--radius); font-size: 14px; background: var(--surface); }
.crm-chip-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; }
.crm-chip-row > span:last-child { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; }
.crm-chip-label { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--ink); min-width: 70px; }
.crm-chip { padding: 4px 11px; font-size: 12px; }
.crm-chip-n { opacity: .7; font-size: 11px; margin-left: 2px; }
.crm-chip-off { border-style: dashed; }
.crm-count { margin: 0 0 8px; color: var(--ink-muted); font-size: 12px; }
.crm-table td .tag + .tag { margin-left: 3px; }
.crm-type { background: #EEF2FB; color: #1F3FB5; }
.th-sortable.is-sorted { color: var(--brand-blue); }
.crm-note { background: #FFF8E6; border: 1px solid #F0DDA8; color: #6B4E00; border-radius: var(--radius); padding: 9px 12px; margin: 0 0 12px; font-size: 13px; }
.crm-note[hidden] { display: none; }
.crm-note p { margin: 0; }
.crm-note p + p { margin-top: 6px; }
.crm-err { color: var(--danger); font-size: 13px; }
.crm-h2 { font-size: 13px; text-transform: uppercase; letter-spacing: .05em; margin: 18px 0 8px; color: var(--ink); }
.crm-h2:first-child { margin-top: 0; }
.crm-h3 { font-size: 12px; color: var(--ink-muted); margin: 14px 0 6px; text-transform: uppercase; letter-spacing: .04em; }
.crm-card-grid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 18px; align-items: start; }
@media (max-width: 900px) { .crm-card-grid { grid-template-columns: 1fr; } }
.crm-panel { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 16px 18px; }
.crm-row2 { display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr)); gap: 10px; }
.crm-ticks { display: flex; flex-wrap: wrap; gap: 4px 14px; }
.crm-tick { font-size: 13px; }
.crm-link { border: 1px solid var(--border); border-radius: var(--radius); padding: 9px 12px; margin-bottom: 8px; }
.crm-link.is-main { border-color: #C9D3F2; background: #F8FAFF; }
.crm-link.is-past { background: #FBFAF6; }
.crm-link-acts { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 6px; }
.crm-chan { display: flex; gap: 6px; align-items: center; margin-bottom: 6px; }
.crm-chan select { width: 90px; }
.crm-chan input[data-chan-label] { width: 110px; }
.crm-chan input[data-chan-value] { flex: 1; }
.crm-log { list-style: none; padding: 0; margin: 0; font-size: 13px; }
.crm-log li { padding: 5px 0; border-bottom: 1px solid var(--border); }
.crm-log li:last-child { border-bottom: none; }
.crm-log-when { color: var(--ink-muted); font-variant-numeric: tabular-nums; margin-right: 6px; }
.crm-plain { padding-left: 18px; margin: 0; font-size: 13px; }
.crm-plain li { margin: 3px 0; }
.crm-pair { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); padding: 10px 14px; margin-bottom: 8px; }
.crm-pair-names { font-weight: 600; margin-bottom: 2px; }
.crm-pair-acts { display: flex; gap: 8px; align-items: center; margin-top: 8px; flex-wrap: wrap; }
.crm-small { font-size: 12px; }
.crm-suggest { background: #F6F8FE; border: 1px solid #DCE3F7; border-radius: var(--radius); padding: 8px 12px; margin: 8px 0; font-size: 13px; }
.crm-sugg { display: inline-flex; gap: 6px; align-items: center; margin: 3px 10px 3px 0; flex-wrap: wrap; }
.crm-dialog { max-width: 640px; }
/* r313: a formDialog -- one form where a row of browser boxes used to be -- is
   drawn above whatever it was opened from (the cut-sheet editor is 200). */
.export-modal.fd-top { z-index: 250; }
.crm-dialog .fd-note { white-space: pre-line; margin: 0 0 10px; }
.crm-table-tight td { padding: 8px 10px; }
.team-left-tag { color: #9A5B00; font-size: 11px; font-weight: 600; margin-left: 6px; text-transform: uppercase; letter-spacing: .4px; }
/* The type ticks sit inside a .field, whose label rule is the small capitals
   of a field NAME -- a list of choices reads as a list, in ordinary case. */
.crm-ticks label.crm-tick { text-transform: none; letter-spacing: normal; font-weight: 400; font-size: 13px; color: var(--ink); margin: 0; display: inline-flex; align-items: center; gap: 5px; }
.crm-dialog .field label.checkbox-row { text-transform: none; letter-spacing: normal; font-weight: 400; font-size: 14px; color: var(--ink); }
.crm-card select:disabled, .crm-card textarea:disabled, .crm-card input:disabled { background: #F3F1EA; color: var(--ink-muted); }
.settings-tag .type-name-input { border: 1px solid transparent; background: transparent; font: inherit; color: inherit; padding: 1px 3px; width: auto; min-width: 60px; }
.settings-tag .type-name-input:hover, .settings-tag .type-name-input:focus { border-color: var(--border); background: #fff; }
.settings-tag .type-public { font-size: 11px; display: inline-flex; align-items: center; gap: 3px; margin: 0 4px; color: var(--ink-muted); text-transform: none; letter-spacing: normal; }
/* Links inside the contact book read as the program's links, not the
   browser's default blue underline. */
.crm-book a, .crm-card a { color: var(--brand-blue); text-decoration: none; }
.crm-book a:hover, .crm-card a:hover { text-decoration: underline; }

/* r305: a question reworded in place. */
.rfi-question-edit { width: 100%; margin: 4px 0; font: inherit; font-size: 13px; padding: 6px 8px; border: 1px solid var(--border); border-radius: 6px; }
.rfi-question-edit-acts { display: inline-flex; gap: 6px; }

/* r306 [his "clean up settings"]: the settings menu sits down the left in
   plain sections; the chosen group fills the right. On a narrow screen the
   menu goes back above the settings and wraps. */
.settings-layout { display: grid; grid-template-columns: 230px minmax(0, 1fr); gap: 28px; align-items: start; }
.settings-layout > .settings-tabs {
  position: sticky; top: 12px; display: flex; flex-direction: column; flex-wrap: nowrap; gap: 2px;
  border-bottom: 0; margin: 0; padding: 4px 0 12px; max-height: calc(100vh - 24px); overflow-y: auto;
}
.settings-menu-section { display: flex; flex-direction: column; gap: 1px; margin-bottom: 10px; }
/* r310 [his 3] "Make the categories a different color or bold or something."
   The section names were the same grey as a hint, so a heading and the item
   under it read as one list. Now: bold, the brand blue, a rule above each
   section, and the items set in beneath their heading. */
.settings-menu-section + .settings-menu-section { border-top: 1px solid var(--border); padding-top: 8px; }
.settings-menu-heading {
  font-size: 12px; font-weight: 800; letter-spacing: 0.08em; text-transform: uppercase;
  color: var(--brand-blue); padding: 6px 12px 5px;
}
.settings-layout > .settings-tabs .settings-tab {
  text-align: left; border: 0; border-left: 3px solid transparent; border-radius: 0 6px 6px 0;
  padding: 6px 12px 6px 22px; margin: 0; font-size: 14px; background: none; color: var(--ink);
}
.settings-layout > .settings-tabs .settings-tab:hover { background: rgba(33, 64, 154, 0.06); }
.settings-layout > .settings-tabs .settings-tab.is-active {
  border-left-color: var(--brand-blue); background: rgba(33, 64, 154, 0.09); color: var(--brand-blue); font-weight: 600;
}
@media (max-width: 900px) {
  .settings-layout { grid-template-columns: 1fr; gap: 12px; }
  .settings-layout > .settings-tabs { position: static; flex-direction: row; flex-wrap: wrap; max-height: none; }
  .settings-menu-section { flex-direction: row; flex-wrap: wrap; align-items: center; margin: 0 12px 4px 0; }
  .settings-menu-heading { width: 100%; padding: 6px 4px 2px; }
}

/* ===========================================================================
   r310 [his 17] THE LEFT PANEL.
   "this needs to be cleaned up and reorganized properly. The coloring of it
   all it terrible and hard to see and when opening a tab I have no indication
   of what is in it along with so many not in a category. Do this better"
   * A dark navy panel instead of the saturated brand blue: white text on it
     reads at 15:1, and the one accent (amber) is left for "you are here".
   * Every heading the same quiet colour, uppercase, with its chevron.
   * An open group's items are SET IN under their heading, on a guide line, so
     what belongs to the group is visible at a glance and nothing outside it
     looks like it is in it.
   * Home at the top; Documents and Settings in the foot, under a rule.
   =========================================================================== */
.sidebar { background: #111C3A; width: 244px; }
body.sidebar-collapsed .sidebar-toggle { background: #111C3A; border-color: #111C3A; }
.nav { gap: 1px; }
.nav-item { color: #DCE3F4; padding: 7px 12px; border-left: 3px solid transparent; }
.nav-item:hover { background: rgba(255,255,255,0.07); color: #fff; }
.nav-item.active {
  background: rgba(255,255,255,0.13); border-left-color: #E8A33D;   /* the brand glow's amber */
  color: #fff; font-weight: 600;
}
.nav-item-home { margin-bottom: 6px; }
.nav-group { margin-top: 2px; }
.nav-group-header {
  color: #AFBCDD; font-size: 11px; font-weight: 700; letter-spacing: 0.06em; white-space: nowrap;
  padding: 9px 12px 7px 12px; border-left: 3px solid transparent; border-radius: 0 6px 6px 0;
}
.nav-group-header:hover { color: #fff; background: rgba(255,255,255,0.06); }
.nav-group.is-open > .nav-group-header,
.nav-group.has-active > .nav-group-header { color: #fff; }
.nav-group-chevron { font-size: 13px; opacity: 0.75; }
.nav-group-body {
  margin: 0 0 6px 15px; padding-left: 6px;
  border-left: 1px solid rgba(255,255,255,0.16);
}
.nav-group-body .nav-item { padding: 6px 10px 6px 12px; border-radius: 0 6px 6px 0; }
.nav-group-body .nav-label { font-size: 13.5px; }
.nav-foot {
  display: flex; flex-direction: column; gap: 1px;
  margin-top: 10px; padding-top: 8px; border-top: 1px solid rgba(255,255,255,0.14);
}
.sidebar-foot { border-top-color: rgba(255,255,255,0.14); }

/* ===========================================================================
   r310 [his 3] SETTINGS USES THE WIDTH IT HAS.
   "you have alllllll this space to the right and instaed of using it you have
   me scrolling down a mile. use the space given and make it better. same with
   purchasing, same with getting paid, same with late payments.... list goes
   on and on, check them all."
   Every card was capped at 760px and stacked one under the next, so a wide
   screen showed a column of cards with the right half empty. The cap is gone
   and a page's cards sit side by side -- two across on a laptop, three on a
   wide monitor -- in reading order. A card that holds a table (tax rates,
   tiers, the manufacturer list, logs) keeps the full width it needs.
   =========================================================================== */
.settings-section { max-width: none; }
/* Cards wrap in rows and GROW to fill their row, so a card that ends up
   alone in a row takes the whole width rather than leaving a hole beside it
   (a grid left exactly the empty right half he was complaining about). */
/* (Specific enough to beat `.settings-group.is-open .settings-group-body
   { display: block }`, the old accordion rule the first group still wears.) */
.settings-layout .settings-accordion > .settings-group > .settings-group-body {
  display: flex; flex-wrap: wrap;
  gap: 18px 20px;
  align-items: flex-start;
  padding-top: 2px;
}
.settings-group > .settings-group-body > * { flex: 1 1 400px; min-width: 0; box-sizing: border-box; }
.settings-group-body > .settings-section,
.settings-group-body > .settings-section:first-child { margin: 0; }
.settings-group-body > .settings-section:has(table),
.settings-group-body > .settings-section:has(.fav-list),
.settings-group-body > .settings-section:has(.numbering-grid),
.settings-group-body > :not(.settings-section) { flex-basis: 100%; }
/* A long card on one subject sets its fields in two columns once it has the
   width for them: the heading and its introduction run across the top, each
   field keeps its own label and note together. */
.settings-section.settings-flow { columns: 2 380px; column-gap: 28px; }
.settings-section.settings-flow > h2,
.settings-section.settings-flow > h2 + .settings-sub { column-span: all; }
.settings-section.settings-flow > * { break-inside: avoid; }
.settings-group-body > .settings-section-wide { flex-basis: 100%; }

/* r310 [his 3]: the email View window. */
.email-view-dialog { width: min(760px, 94vw); }
.email-view-head { display: grid; grid-template-columns: 80px 1fr; gap: 4px 12px; margin: 6px 0 12px; font-size: 13px; }
.email-view-head dt { color: var(--ink-muted); font-weight: 600; }
.email-view-head dd { margin: 0; }
.email-view-frame { width: 100%; height: 420px; border: 1px solid var(--border); border-radius: 6px; background: #fff; }
.email-tpl-btns { display: inline-flex; gap: 8px; flex-wrap: wrap; }
/* r310 [his 3, his screenshot of Who signs for us]: the company signature pad
   is 620px of drawing surface in a field capped at 340px -- it spilled out of
   its box and the Clear button sat on the signature. The pad's field takes the
   card's width and the pad scales to fit it (initSignaturePad maps the pointer
   through the drawn size, so a scaled pad still draws where the pen is). */
.settings-section > .field:has(.cs-pad-wrap) { max-width: none; }
.settings-section .cs-pad-wrap { position: relative; max-width: 620px; }
.settings-section .cs-pad-wrap canvas {
  display: block; width: 100%; height: auto; border: 1px solid var(--border); border-radius: 6px; background: #fff;
}
.settings-section .cs-pad-wrap .cs-clear { position: absolute; top: 6px; right: 6px; }
/* r310 [his 3]: the wording blocks on Terms sit two across, each with its
   Edit wording and View PDF buttons on one line under its box. */
#settings-terms-blocks { display: flex; flex-wrap: wrap; gap: 18px 26px; align-items: flex-start; }
#settings-terms-blocks > .terms-tab-block { flex: 1 1 420px; min-width: 0; }
.terms-tab-block + .terms-preview-row, .terms-tab-block .terms-preview-row { display: inline-flex; margin-right: 8px; }
/* r310 [his 3]: the alert ticks, in groups, side by side where there is room. */
.alert-kinds { display: grid; grid-template-columns: repeat(auto-fill, minmax(260px, 1fr)); gap: 12px 24px; margin-top: 8px; }
.alert-group { display: flex; flex-direction: column; gap: 4px; }
.alert-group-head { margin: 0 0 2px; font-size: 12px; font-weight: 700; letter-spacing: 0.05em; text-transform: uppercase; color: var(--brand-blue); }
/* r310 [his 3]: a card now shares its row, so the fixed-column field grids
   inside it (2fr 1fr 1fr and the like) wrap to the card's width instead of
   running off its right edge and clipping the boxes. */
.settings-group-body .settings-section .qe-fields-row-2,
.settings-group-body .settings-section .qe-fields-row-3,
.settings-group-body .settings-section .qe-fields-row-4 { grid-template-columns: repeat(auto-fit, minmax(170px, 1fr)); }
.settings-group-body .settings-section .qe-fields > .field,
.settings-group-body .settings-section .settings-grid > .field { min-width: 0; }
.settings-group-body .settings-section input, .settings-group-body .settings-section select { max-width: 100%; box-sizing: border-box; }
.settings-group-body .settings-section .settings-add-row { flex-wrap: wrap; max-width: none; }
.settings-group-body .settings-section .settings-add-row input { flex: 1 1 160px; min-width: 0; }

/* ==========================================================================
   r310 [stated 09/25/2026] "These need to show up in the appropriate module
   but they can be lined out with 'Design Project' and not editable" -- a
   design fee's own quote and order on the Quotes and Orders lists: the row
   quiet, and a tag naming the design job.
   r314 [his words, 09/28/2026] "remove the cross throughs on the attached
   design job order#s. How the rest of the layout is is good and locking it
   from editing is perfect. don't need to cross the numbers out" -- the number
   is no longer lined out; the quiet row, the tag and the lock stay.
   ========================================================================== */
tr.row-design-fee > td:first-child { text-decoration: none; color: var(--ink-muted); }
tr.row-design-fee td { background: var(--bg); color: var(--ink-muted); }
tr.row-design-fee:hover td { background: #F3F1EA; }
.tag.tag-design-project { background: #EDE7F6; color: #4A2F7A; border: 1px solid #D6C9EC; font-weight: 600; text-decoration: none; }
.owner-banner.design-fee-banner { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 12px;
  background: #F4F0FA; border-color: #D6C9EC; color: #3B2566; }
.owner-banner.design-fee-banner[hidden] { display: none; }

/* r314 -- "Send Follow Up Email", under the red past-due line on an invoice. */
.inv-followup-row { margin: -6px 0 14px; display: flex; gap: 8px; flex-wrap: wrap; }
.inv-followup-row[hidden] { display: none; }
/* r314 -- the late fees on an invoice, lines of it under its goods. */
.inv-late-fees { margin: 10px 0 14px; }
.inv-late-fees[hidden] { display: none; }
.inv-late-fees-head { font-weight: 600; font-size: 13px; margin: 0 0 4px; }
.inv-late-fee-table td { padding: 6px 8px; }
.inv-late-fee-table .lf-desc { width: 100%; }
.inv-late-fee-table .lf-act { text-align: right; white-space: nowrap; width: 1%; }
.lf-line { margin: 0 0 6px; }
.lf-table { margin: 8px 0; }
