/* Brand palette (RJ, 2026-09-13): "I would like to make my app design
   utilize these colors" -- RJ supplied a 5-swatch palette (screenshot):
   Blue Slate #666A86, Slate Grey #788AA3, Muted Teal #92B6B1,
   Celadon #B2C9AB, Pearl Beige #E8DDB5 (cool blue-grey through warm
   cream). Every variable below is one of those five swatches used as-is,
   or a deliberate tint/shade computed from one (mixed with white to
   lighten for a background, or with black to darken for readable text/a
   hover state) -- never a color picked freehand. Exact mix ratios and
   contrast-ratio checks are in the project docs (architecture addendum, phase 42).
   --error-bg/--error-text are the one deliberate exception: kept as the
   original red rather than remapped onto any of the 5 (none of them is a
   red/orange), since staff scanning job/estimate status badges need
   "error/declined/cancelled" to read as unambiguously bad at a glance --
   flagged there for RJ to weigh in on. */
:root {
  --bg: #faf8ef;              /* Pearl Beige tinted ~22% into white */
  --panel-alt-bg: #fcfbf5;    /* Pearl Beige tinted ~13% into white -- subtle alt-row/header tint */
  --border: #d4dae2;          /* Slate Grey tinted ~32% into white */
  --text: #2e303c;            /* Blue Slate darkened ~55% toward black */
  --muted: #627186;           /* Slate Grey darkened ~18% toward black, for AA contrast */
  --accent: #666a86;          /* Blue Slate, exactly as supplied */
  --accent-hover: #16171d;    /* Blue Slate darkened ~78% toward black */
  --success-bg: #e9f0e7;      /* Celadon tinted ~28% into white */
  --success-text: #495b58;    /* Muted Teal darkened ~50% toward black */
  --error-bg: #fdecec;        /* unchanged -- see note above */
  --error-text: #a3221f;      /* unchanged -- see note above */
  --info-bg: #e4e8ed;         /* Slate Grey tinted ~20% into white */
  --info-text: #424557;       /* Blue Slate darkened ~35% toward black */
  --warning-bg: #f2ecd6;      /* Pearl Beige tinted ~55% into white */
  --warning-border: #e8ddb5;  /* Pearl Beige, exactly as supplied */
  --warning-text: #3a372d;    /* Pearl Beige darkened ~75% toward black */
  --neutral-bg: #eceff2;      /* Slate Grey tinted ~14% into white -- "inactive" badges (draft/expired/pending) */
  --neutral-text: #485362;    /* Slate Grey darkened ~40% toward black */
  --hover-bg: #eff1f4;        /* Slate Grey tinted ~12% into white -- row/button hover states */
  --runsheet-dark: #24252f;   /* Blue Slate darkened ~65% toward black -- runsheet cover/grid table headers */
  --runsheet-mid: #474a5e;    /* Blue Slate darkened ~30% toward black -- runsheet endsheet table header */
  /* Phase 44 (RJ, 2026-09-13, brand-palette follow-up): "you didn't really
     use the whole color palette... I don't want my whole screen below my
     header to just be white." Three changes: --panel moved off pure white
     onto a cool tint so cards/panels/inputs aren't stark white; a full set
     of 5 bolder "chip" tints (one per swatch, ~30% into white -- clearly
     colored, not just-barely-off-white) was added so a repeated row of
     cards/tiles/buttons can visibly cycle through all 5 swatches instead
     of leaning on --accent/--info/--success alone; and the topbar (every
     page, the very first thing seen) became a 5-stop gradient running
     across all 5 swatches darkened ~50% toward black so white nav text
     stays readable the whole width -- the most direct, literal answer to
     "use the whole palette" there is. See
     the project docs (architecture addendum, phase 44) for exact ratios, contrast
     checks, and where each is used. */
  --panel: #f5f8f7;           /* Muted Teal tinted ~9% into white -- Phase 44: bumped up from ~5% */
  --chip-1: #d1d2db;          /* Blue Slate tinted ~30% into white */
  --chip-2: #d6dce3;          /* Slate Grey tinted ~30% into white */
  --chip-3: #dee9e8;          /* Muted Teal tinted ~30% into white */
  --chip-4: #e8efe6;          /* Celadon tinted ~30% into white */
  --chip-5: #f8f5e9;          /* Pearl Beige tinted ~30% into white */
  /* RJ, 2026-10-19: Impressio Mentis brand -- the logo's dark green type needs a
     light header, so the 5-swatch gradient is each swatch tinted ~45% into
     white (lightest, Pearl Beige, under the logo on the left), with dark nav
     text and a brand-green rule under the bar. RJ: the first, near-white
     version was a little too light -- this sits between it and the old dark
     bar. Dark text >= 6.6:1 on every stop; logo green 8.1:1, logo grey 4.5:1. */
  --topbar-gradient: linear-gradient(90deg, #f2ecd6, #d5e1d1, #c3d7d4, #b5bfcd, #abadbc);
  --brand-green: #004f44;
  --topbar-text: #1d2b29;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
  background: var(--bg);
  color: var(--text);
}

.topbar {
  background: var(--topbar-gradient);
  color: var(--topbar-text);
  padding: 10px 24px;
  display: flex;
  align-items: center;
  gap: 28px;
  border-bottom: 3px solid var(--brand-green);
  box-shadow: 0 1px 4px rgba(0, 0, 0, 0.06);
}

.topbar a { color: var(--topbar-text); text-decoration: none; font-size: 14px; font-weight: 500; opacity: 0.92; }
.topbar a:hover { opacity: 1; color: var(--brand-green); text-decoration: underline; }
.topbar .brand { font-weight: 700; font-size: 16px; opacity: 1; color: var(--brand-green); }
.topbar .brand-logo { display: inline-flex; align-items: center; line-height: 0; }
.topbar .brand-logo:hover { text-decoration: none; }
.topbar .brand-logo img { height: 36px; width: auto; display: block; }
/* RJ (2026-10-02): "my log out button is creeping off on the right" --
   header items were shrinking (wrapping "RJ · Administrator" onto two lines)
   and then overflowing the bar. Header items never wrap their own text now;
   if the bar still runs out of room, it wraps onto a second row instead of
   pushing Log Out off screen. */
.topbar { flex-wrap: wrap; row-gap: 10px; column-gap: 20px; }
.topbar a, .topbar span, .topbar button, .topbar .nav-dropdown-toggle { white-space: nowrap; }
/* RJ, 2026-10-24: the header shrinks to fit narrower monitors -- text, gaps
   and the logo scale with the window (14px at ~1500px+, down to 12px), and
   base.html's script lowers --nav-scale further if items would still wrap. */
.topbar {
  --nav-scale: 1;
  --nav-fs: calc(clamp(12px, 0.62vw + 5px, 14px) * var(--nav-scale));
  column-gap: calc(clamp(10px, 1.6vw - 4px, 20px) * var(--nav-scale));
  padding-left: clamp(12px, 1.6vw, 24px); padding-right: clamp(12px, 1.6vw, 24px);
}
.topbar > a, .topbar .nav-dropdown-toggle { font-size: var(--nav-fs); }
.topbar .brand-logo img { height: calc(clamp(28px, 2.4vw, 36px) * var(--nav-scale)); }
.topbar a.nav-alerts { font-size: calc(var(--nav-fs) - 1px); }

/* Portals nav dropdown (Phase 48, RJ, 2026-09-14) */
.nav-dropdown { position: relative; display: inline-block; }
.nav-dropdown-toggle {
  background: none; border: none; color: var(--topbar-text); font-size: 14px; font-weight: 500; opacity: 0.92;
  cursor: pointer; padding: 0; font-family: inherit;
}
.nav-dropdown-toggle:hover { opacity: 1; color: var(--brand-green); text-decoration: underline; }
.nav-dropdown-menu {
  display: none; position: absolute; top: 100%; left: 0; margin-top: 12px;
  background: #fcfaf4; border: 1px solid var(--border); border-top: 3px solid var(--brand-green);
  border-radius: 6px; padding: 6px 0; min-width: 170px;
  box-shadow: 0 6px 16px rgba(0,0,0,0.12); z-index: 50;
}
.nav-dropdown-menu.open { display: block; }
.nav-dropdown-menu a { display: block; padding: 8px 16px; white-space: nowrap; font-size: 14px; }
.nav-dropdown-menu a:hover { background: rgba(0,79,68,0.08); text-decoration: none; }
/* Account dropdown (RJ, 2026-10-02): name/role, My Account and Log Out fold
   into one "Name ▾" menu pinned to the right edge, so the header fits on
   one line. Its menu opens leftward so it never runs off screen. */
.nav-account { margin-left: auto; }
.nav-account .nav-dropdown-menu { left: auto; right: 0; }
/* RJ, 2026-10-19: open system alerts, beside the account menu. */
.topbar a.nav-alerts { margin-left: auto; background: #b42318; color: #fff; font-weight: 700; opacity: 1;
  padding: 3px 8px; border-radius: 999px; font-size: 13px; }
.topbar a.nav-alerts:hover { color: #fff; background: #8f1c13; text-decoration: none; }
.topbar a.nav-alerts + .nav-account { margin-left: 0; }
.job-alerts div + div { margin-top: 4px; }
.nav-dropdown-label { display: block; padding: 6px 16px 8px; font-size: 12px; opacity: 0.7;
  border-bottom: 1px solid var(--border); margin-bottom: 4px; white-space: nowrap; }
.nav-dropdown-menu .nav-dropdown-item { display: block; width: 100%; text-align: left; background: none; border: none;
  color: var(--topbar-text); font: inherit; font-size: 14px; padding: 8px 16px; cursor: pointer; white-space: nowrap; }
.nav-dropdown-menu .nav-dropdown-item:hover { background: rgba(0,79,68,0.08); }

.container {
  max-width: 1000px;
  margin: 0 auto;
  padding: 28px;
}
/* RJ, 2026-10-26: wide lists (Shipping, Prepare Shipment, Carton Labels) use the screen's width. */
.container.container-wide { max-width: 1500px; }
.nowrap, td.nowrap-cell > a { white-space: nowrap; }
/* RJ, 2026-10-23: Back, top left under the header banner (static/nav_back.js). */
.nav-back-bar { padding: 8px 24px 0; min-height: 0; }
.nav-back-bar:not(:has(a:not([hidden]))) { display: none; }
.nav-back { display: inline-flex; align-items: center; gap: 4px; padding: 4px 12px; border: 1px solid var(--border, #d4dae2);
            border-radius: 6px; background: var(--card-bg, #fff); color: var(--text, inherit); font-size: 13px;
            font-weight: 600; text-decoration: none; }
.nav-back:hover { border-color: var(--accent, #3a7); text-decoration: none; }
.nav-back[hidden] { display: none; }
.nav-back-bar:has(a:not([hidden])) + .container { padding-top: 14px; }
@media print { .nav-back-bar { display: none; } }
/* RJ, 2026-10-28: "Return to scheduling", fixed on the left side while in Purchasing. */
.btn.po-return-scheduling { position: fixed; left: 16px; top: 50%; transform: translateY(-50%); z-index: 40;
  max-width: 150px; white-space: normal; text-align: center; box-shadow: 0 2px 10px rgba(0,0,0,0.18); }
@media (max-width: 1360px) { .btn.po-return-scheduling { top: auto; bottom: 16px; transform: none; } }
@media print { .btn.po-return-scheduling { display: none; } }

h1 { font-size: 22px; margin: 0 0 6px; }
h2 { font-size: 17px; margin: 0 0 12px; }
.subtitle { color: var(--muted); margin: 0 0 24px; font-size: 14px; }

.card {
  background: var(--panel);
  border: 1px solid var(--border);
  border-top: 4px solid var(--accent);
  border-radius: 10px;
  padding: 20px;
  margin-bottom: 20px;
}

/* Phase 44 (RJ, 2026-09-13): dashboard/PM-Portal stat tiles reuse the same
   status colors as the .badge rules elsewhere (draft/sent/approved/
   declined, and PM Portal's own stale/warn/good) as a colored top edge,
   overriding .card's default accent border above -- so a row of stat
   tiles reads its own status at a glance by color, not just by number,
   and the palette shows up as more than one repeated hue. */
.stat.stat-draft { border-top-color: var(--neutral-text); }
.stat.stat-sent { border-top-color: var(--info-text); }
.stat.stat-approved { border-top-color: var(--success-text); }
.stat.stat-declined { border-top-color: var(--error-text); }
.stat.stat-danger { border-top-color: var(--error-text); }
.stat.stat-warn { border-top-color: var(--warning-text); }
.stat.stat-good { border-top-color: var(--success-text); }

/* Book/addon part list (RJ, 2026-09-05): each part collapses to one
   condensed summary line by default so several parts are visible on screen
   at once -- the full spec/pricing/internal-data content lives inside a
   <details> that expands on click. */
.part-card { margin-bottom: 10px; padding: 14px 20px; }
.part-summary summary { outline: none; }
.part-summary summary::-webkit-details-marker { display: none; }
.part-summary summary::marker { content: ""; }
.part-summary .disclosure-arrow { display: inline-block; color: var(--muted); transition: transform 0.1s ease; width: 0.8em; }
.part-summary[open] .disclosure-arrow { transform: rotate(90deg); }
.part-summary summary { padding: 3px 0; }
.part-summary[open] summary { margin-bottom: 2px; }

.grid { display: grid; gap: 16px; }
.grid-4 { grid-template-columns: repeat(4, 1fr); }
.grid-3 { grid-template-columns: repeat(3, 1fr); }
.grid-2 { grid-template-columns: 1fr 1fr; }

.stat { text-align: center; }
.stat .n { font-size: 28px; font-weight: 700; }
.stat .label { color: var(--muted); font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; }

/* Phase 36 (RJ, 2026-09-13): Project Manager Portal stat-tile color coding
   -- a stale/overdue count should read as trouble, a just-changed count as
   forward progress, at a glance without reading the label first. */
.stat-danger .n { color: var(--error-text); }
.stat-warn .n { color: var(--warning-text); }
.stat-good .n { color: var(--success-text); }
.stat.is-clickable { cursor: pointer; }
.stat.is-clickable:hover { background: var(--bg); }

.pm-flags .badge { margin: 0 4px 4px 0; }
.badge.pm-stale, .badge.pm-overdue { background: var(--error-bg); color: var(--error-text); }
.badge.pm-just-changed { background: var(--success-bg); color: var(--success-text); }
.badge.pm-needs-prepress { background: var(--warning-bg); color: var(--warning-text); }

table { width: 100%; border-collapse: collapse; font-size: 14px; }
th, td { text-align: left; padding: 9px 10px; border-bottom: 1px solid var(--border); }
th { color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase; letter-spacing: 0.03em; }
tr:last-child td { border-bottom: none; }
tr:hover td { background: var(--hover-bg); }

a.row-link { color: var(--text); text-decoration: none; display: block; }

.btn {
  display: inline-block;
  padding: 9px 16px;
  border-radius: 7px;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: white;
  font-size: 14px;
  cursor: pointer;
  text-decoration: none;
}
.btn:hover { background: var(--accent-hover); }
.btn.secondary { background: white; color: var(--accent); }
.btn.secondary:hover { background: var(--hover-bg); }
.btn.danger { background: white; color: var(--error-text); border-color: var(--error-text); }

.badge {
  display: inline-block;
  white-space: nowrap;
  padding: 3px 9px;
  border-radius: 999px;
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.03em;
}
.badge.draft { background: var(--neutral-bg); color: var(--neutral-text); }
.badge.sent { background: var(--info-bg); color: var(--info-text); }
.badge.approved { background: var(--success-bg); color: var(--success-text); }
.badge.declined { background: var(--error-bg); color: var(--error-text); }
.badge.expired, .badge.converted, .badge.closed { background: var(--neutral-bg); color: var(--neutral-text); }

/* Job status + estimate pipeline-status badges (RJ, 2026-09-04). "open" and
   "closed" are shared slugs between the two status systems, so their rules
   cover both (closed is grouped with expired/converted above). */
.badge.open { background: var(--info-bg); color: var(--info-text); }
.badge.awaiting_files, .badge.awaiting_payment, .badge.on_hold, .badge.awaiting_vendor_quotes { background: var(--warning-bg); color: var(--warning-text); }
.badge.proofs_approved, .badge.proofing, .badge.ready_for_press, .badge.scheduled, .badge.ready_for_bindery, .badge.ready_to_ship { background: var(--info-bg); color: var(--info-text); }
.badge.shipped, .badge.billed, .badge.won { background: var(--success-bg); color: var(--success-text); }
.badge.cancelled, .badge.lost { background: var(--error-bg); color: var(--error-text); }

/* Per-operation Open/Complete badges (RJ, 2026-09-05, Operations tab) --
   "open" reuses the job/estimate "open" rule above. */
.badge.complete { background: var(--success-bg); color: var(--success-text); }

/* Purchase order status (RJ, 2026-09-05 Purchasing module) -- "open" reuses
   the shared rule above; "received" is a closed-out/terminal state, same
   success styling as "complete". */
.badge.received { background: var(--success-bg); color: var(--success-text); }

/* Shipping pack status (Phase 16, RJ, 2026-09-07) -- "cancelled" reuses the
   shared error rule above. */
.badge.info_received, .badge.booked { background: var(--info-bg); color: var(--info-text); }
.badge.in_transit { background: var(--warning-bg); color: var(--warning-text); }
.badge.delivered { background: var(--success-bg); color: var(--success-text); }

/* Phase 17 (RJ, 2026-09-07): flags a pack created from a customer's "Do you
   want a BAC?" request, so staff can tell it apart from a regular order
   shipment at a glance. */
.badge.bac { background: var(--warning-bg); color: var(--warning-text); margin-left: 4px; font-size: 10px; }

/* Phase 17b (RJ, 2026-09-07): the BAC question's third answer ("No, but
   send samples") gets its own badge distinct from BAC's -- and a shipment
   hold (first used so a job's other packs wait on BAC approval, later
   reused for a payment hold on a partial shipment) gets a badge too so
   staff spot a blocked pack at a glance. */
.badge.sample { background: var(--info-bg); color: var(--info-text); margin-left: 4px; font-size: 10px; }
.badge.hold { background: var(--error-bg); color: var(--error-text); margin-left: 4px; font-size: 10px; }
/* RJ, 2026-10-26: Prepare Shipment -- a pack via on hold can't be selected. */
#pack_table tr.pack-on-hold td { color: var(--muted, #667); background: var(--error-bg); }
#pack_table .pack-hold-why { font-size: 12px; color: var(--error-text); max-width: 260px; }

/* Phase 21 (RJ, 2026-09-08): flags a Pack Via row that needs 275# (heavy
   duty) cartons -- casebind, fewer than 8 cartons, or the customer prefers
   heavy duty boxes. See models._pack_via_needs_275(). */
.badge.heavy { background: var(--warning-bg); color: var(--warning-text); margin-left: 4px; font-size: 10px; }

/* Live "contains" typeahead dropdown -- PO line-item search (inventory
   items) and the adhoc-line Job # search (RJ, 2026-09-05 Purchasing
   module). Positioned under whichever text input it belongs to. */
.typeahead-results {
  position: absolute; z-index: 20; top: 100%; left: 0; right: 0;
  background: white; border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 4px 10px rgba(0,0,0,0.08); max-height: 260px; overflow-y: auto;
  margin-top: 2px; font-size: 13px;
}
.typeahead-item { padding: 7px 10px; cursor: pointer; }
.typeahead-item:hover { background: var(--hover-bg); }
.typeahead-empty { padding: 7px 10px; color: var(--muted); }
/* RJ, 2026-11-02: Op # picker on PO lines (static/op_combobox.js) -- a
   scrollable, contains-searchable list of chargeable operations. The list
   sits on the page (fixed) so the line table's scroll box never clips it. */
.op-combobox-input { width: 170px; }
.op-combobox-list {
  position: fixed; z-index: 60; margin: 0; padding: 4px 0; list-style: none;
  background: white; border: 1px solid var(--border); border-radius: 6px;
  box-shadow: 0 4px 10px rgba(0,0,0,0.12); max-height: 240px; overflow-y: auto;
  font-size: 13px; max-width: 460px;
}
.op-combobox-option { padding: 6px 10px; cursor: pointer; }
.op-combobox-option.active, .op-combobox-option:hover { background: var(--hover-bg); }
.op-combobox-option[aria-selected="true"] { font-weight: 600; }
.op-combobox-group { padding: 6px 10px 2px; font-size: 11px; font-weight: 700; color: var(--muted);
  text-transform: uppercase; letter-spacing: .04em; }
.op-combobox-empty { padding: 6px 10px; color: var(--muted); }
/* RJ, 2026-11-02: a due date set by the must-ship date (static/must_ship_lock.js). */
input.due-locked { background: var(--neutral-bg); color: var(--neutral-text); cursor: not-allowed; }
.due-lock-note { display: block; margin: 3px 0 0; font-size: 12px; color: var(--muted); }
.po-lines-table input, .po-lines-table select { padding: 6px 8px; font-size: 13px; }
.po-lines-table td { vertical-align: top; }
.ext-cell { font-weight: 600; white-space: nowrap; }

/* Email-vendor compose window (RJ, 2026-09-06 Purchasing follow-up), and
   any other simple modal built the same way: toggle the .open class,
   never the `hidden` attribute (author CSS setting display:flex here would
   otherwise fight the UA's [hidden]{display:none} at equal specificity). */
.modal-overlay {
  display: none; position: fixed; inset: 0; background: rgba(20, 20, 24, 0.45);
  align-items: center; justify-content: center; z-index: 100; padding: 20px;
}
.modal-overlay.open { display: flex; }
.modal-box {
  background: var(--panel); border-radius: 10px; padding: 22px 26px;
  width: 100%; max-width: 480px; max-height: 88vh; overflow-y: auto;
  box-shadow: 0 12px 40px rgba(0,0,0,0.22);
}
.modal-box h2 { margin-top: 0; }

/* Settings landing page: Vendors/Operations/Cost Centers link cards (RJ,
   2026-09-06: "make all three of the page links in the settings to look
   the same as each other and look more appealing"). */
.settings-links { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; margin-bottom: 24px; }
.settings-link-card {
  display: flex; align-items: center; gap: 14px;
  background: var(--panel); border: 1px solid var(--border); border-radius: 10px;
  padding: 18px; text-decoration: none; color: var(--text);
  transition: border-color 0.12s ease, box-shadow 0.12s ease, transform 0.12s ease;
}
.settings-link-card:hover { border-color: var(--accent); box-shadow: 0 3px 14px rgba(0,0,0,0.07); transform: translateY(-1px); }
.settings-link-card .icon {
  font-size: 20px; width: 42px; height: 42px; flex-shrink: 0; border-radius: 8px;
  background: var(--info-bg); display: flex; align-items: center; justify-content: center;
}
/* Phase 44 (RJ, 2026-09-13): with 10+ link cards on this page, giving every
   icon the same --info-bg chip read as flat/monotone -- cycle through all
   5 palette-derived tints instead (Blue Slate, Slate Grey, Muted Teal,
   Celadon, Pearl Beige) so the page reads as more than one color without
   changing any card's meaning (this is purely decorative -- no card's
   function depends on its position in the list). */
.settings-link-card:nth-child(5n+1) .icon { background: var(--chip-2); }
.settings-link-card:nth-child(5n+2) .icon { background: var(--chip-1); }
.settings-link-card:nth-child(5n+3) .icon { background: var(--chip-3); }
.settings-link-card:nth-child(5n+4) .icon { background: var(--chip-4); }
.settings-link-card:nth-child(5n+5) .icon { background: var(--chip-5); }
.settings-link-card .label { min-width: 0; }
.settings-link-card h3 { margin: 0 0 3px; font-size: 15px; }
.settings-link-card p { margin: 0; color: var(--muted); font-size: 12.5px; line-height: 1.4; }
.settings-link-card .arrow { margin-left: auto; color: var(--muted); font-size: 18px; flex-shrink: 0; }

/* Inventory list: rows-per-page + Excel export controls, and draggable
   column headers (RJ, 2026-09-06 follow-up). */
.inv-toolbar { display: flex; align-items: center; gap: 10px; margin-bottom: 12px; flex-wrap: wrap; }
.inv-toolbar .spacer { flex: 1; }
.inventory-table th[draggable="true"] { cursor: grab; user-select: none; }
.inventory-table th.col-drag-over { box-shadow: inset 2px 0 0 var(--accent); }
.pagination { display: flex; align-items: center; gap: 10px; margin-top: 10px; color: var(--muted); font-size: 13px; }

/* Status dropdowns (RJ, 2026-09-04): the job/estimate status controls are
   <select> elements styled like the badges above, so changing status looks
   like picking a new badge rather than filling out a form field. */
.job-header-row {
  display: flex;
  justify-content: space-between;
  align-items: flex-start;
  flex-wrap: wrap;
  gap: 16px;
  margin-bottom: 6px;
}
.job-header-row > div:first-child { flex: 1 1 auto; min-width: 260px; }
.job-status-form { flex: 0 0 auto; }
select.status-select {
  cursor: pointer;
  /* RJ, 2026-10-26: in a crowded table the status dropdown was squeezed to one letter. */
  min-width: max-content;
  border: 1px solid transparent;
  padding: 4px 22px 4px 10px;
  font-family: inherit;
}

/* Shown only when printing (RJ, 2026-09-04) -- e.g. a plain-text stand-in
   for the Job Ticket's status dropdown, which is itself marked .no-print. */
.print-only { display: none; }
@media print { .print-only { display: inline-block; } }
/* QA 2026-11-03: a date box prints as its text (or "—"), never the browser's "mm/dd/yyyy" placeholder. */
@media print { input[type=date].print-date { display: none !important; } }

.flash { padding: 12px 16px; border-radius: 8px; margin-bottom: 16px; font-size: 14px; }
.flash.success { background: var(--success-bg); color: var(--success-text); }
.flash.error { background: var(--error-bg); color: var(--error-text); }
.flash.info { background: var(--info-bg); color: var(--info-text); }
/* RJ, 2026-10-25: admin turned off "Show only mine" (templates/_mine_toggle.html). */
.flash.warning { background: #fff7d6; color: #5c4300; border-left: 6px solid #d4a017; }

label { display: block; font-size: 13px; font-weight: 600; margin: 14px 0 5px; }
input[type=text], input[type=email], input[type=tel], input[type=number], input[type=password], textarea, select {
  width: 100%;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 14px;
  font-family: inherit;
}
textarea { resize: vertical; min-height: 70px; }

/* Date inputs (RJ, 2026-09-04): the generic width:100% rule above stretched
   these across their whole container, leaving the native calendar icon
   sitting far away from the actual (short) date text -- size the box to
   its content instead so the icon sits right next to the date. */
input[type=date] {
  width: auto;
  min-width: 150px;
  padding: 9px 10px;
  border: 1px solid var(--border);
  border-radius: 7px;
  font-size: 14px;
  font-family: inherit;
}

.line-items-table input { padding: 6px 8px; font-size: 13px; }
.line-items-table td { vertical-align: top; }
.remove-row { color: var(--error-text); cursor: pointer; background: none; border: none; font-size: 13px; }

/* Quantity-break monotonicity warning + Value-Added job-data box (RJ, 2026-08-31) */
.warning-banner {
  background: var(--warning-bg); border: 1px solid var(--warning-border); color: var(--warning-text);
  padding: 10px 14px; border-radius: 6px; font-weight: 600; font-size: 13px; margin-top: 10px;
}
.job-data-box {
  background: var(--info-bg); border: 1px solid var(--border); border-radius: 8px;
  padding: 14px 16px; margin-bottom: 16px; font-size: 13px;
}

.actions { display: flex; gap: 10px; margin-top: 18px; }

.copy-link {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  align-items: center;
  background: var(--panel-alt-bg);
  border: 1px dashed var(--border);
  border-radius: 8px;
  padding: 10px 12px;
  font-size: 13px;
}
/* RJ, 2026-09-14: "my proof buttons don't render very well" -- word-break:
   break-all used to sit on the .copy-link container itself, which cascades
   to every descendant including its buttons. In a narrow card (e.g. the
   Proofs card in the jobs/estimates detail pages' 2-column grid), the long
   URL span was squeezing the Copy link/Email/Mark approved buttons down to
   almost no width, and break-all then broke THEIR text apart letter by
   letter ("Co py l ink") instead of just the long URL. Scope break-all to
   the URL span alone, let it shrink/wrap first (flex-wrap above is the
   fallback once it can't shrink further), and keep every button at its own
   natural, never-broken width. */
.copy-link > span {
  word-break: break-all;
  flex: 1 1 240px;
}
.copy-link > .btn,
.copy-link form {
  flex: 0 0 auto;
  white-space: nowrap;
}

.filter-tabs { display: flex; gap: 8px; margin-bottom: 18px; flex-wrap: wrap; }
.filter-tabs a {
  padding: 6px 12px; border-radius: 999px; font-size: 13px;
  text-decoration: none; color: var(--text); background: white; border: 1px solid var(--border);
}
.filter-tabs a.active { background: var(--accent); color: white; border-color: var(--accent); }

.events { list-style: none; padding: 0; margin: 0; font-size: 13px; color: var(--muted); }
.events li { padding: 4px 0; }

.center-page { max-width: 640px; margin: 60px auto; padding: 0 20px; }

/* Job detail tabs (Phase 9, RJ 2026-08-31) */
.job-tabs {
  display: flex; gap: 4px; margin-bottom: 20px; flex-wrap: wrap;
  border-bottom: 1px solid var(--border); padding-bottom: 0;
}
.job-tabs a {
  padding: 10px 14px; font-size: 14px; text-decoration: none; color: var(--muted);
  border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.job-tabs a:hover { color: var(--text); }
.job-tabs a.active { color: var(--accent); border-bottom-color: var(--accent); font-weight: 600; }
.tab-panel { display: none; }
.tab-panel.active { display: block; }

/* Inventory catalog (RJ, 2026-09-05): a much wider table than the app's
   other lists (every one of the spreadsheet's original columns, plus the
   generated Item Number) -- scrolls horizontally in its own container
   rather than the whole page, and every column gets its own compact filter
   control in a row under the headers (dropdown = exact match against
   whatever values are actually present, mirroring an Excel AutoFilter;
   text box = contains for Item Desc, exact for everything else). */
.table-scroll { overflow-x: auto; }
/* RJ (2026-09-16 follow-up): the Portal Registrations customer search
   typeahead lives inside a .table-scroll wrapper -- setting overflow-x
   alone makes the browser compute overflow-y as "auto" too (the CSS
   Overflow spec's own rule for a lone non-visible axis: whenever one axis
   is non-visible and the other is left visible, the visible one is forced
   to auto too), which clipped the dropdown's results list to the table's
   own height, so results below the fold needed scrolling the table itself
   to see. That forced-auto rule applies to the CASCADED result, not just
   the shorthand, so a rule that only overrides overflow-y (leaving
   overflow-x: auto in place) gets silently re-forced back to auto by the
   same spec rule -- verified in a real browser while fixing this. Setting
   BOTH axes to visible sidesteps it entirely; this table doesn't need its
   own horizontal scrollbar (few, narrow columns), so trading that away
   for an unclipped dropdown is a clean tradeoff, applied only on pages
   that opt in via this second class. */
.table-scroll.allow-vertical-overflow { overflow: visible; }
.inventory-table { font-size: 12.5px; white-space: nowrap; }
.inventory-table th, .inventory-table td { padding: 6px 8px; }
.inventory-table .filter-row td { padding: 4px 6px; background: var(--panel-alt-bg); }
.inventory-table .filter-row input,
.inventory-table .filter-row select {
  width: 100%; min-width: 74px; padding: 4px 6px; font-size: 12px;
  border: 1px solid var(--border); border-radius: 5px;
}

/* Column-visibility picker (RJ, 2026-09-05 follow-up): a <details> disclosure
   above the Inventory table, same collapsed-by-default pattern as
   .part-summary above -- a checkbox per column, so RJ can choose which of
   the 27 he wants to view (and, since the filter row is only built for
   visible columns, filter by) at any one time. */
.col-picker { margin-bottom: 14px; }
.col-picker summary { outline: none; cursor: pointer; font-weight: 600; padding: 3px 0; }
.col-picker summary::-webkit-details-marker { display: none; }
.col-picker summary::marker { content: ""; }
.col-picker .disclosure-arrow { display: inline-block; color: var(--muted); transition: transform 0.1s ease; width: 0.8em; }
.col-picker[open] .disclosure-arrow { transform: rotate(90deg); }
/* Phase 15 (RJ, 2026-09-07): collapsible-by-part Operations tab groups. */
.operations-part-group .disclosure-arrow { display: inline-block; color: var(--muted); transition: transform 0.1s ease; width: 0.8em; }
.operations-part-group[open] .disclosure-arrow { transform: rotate(90deg); }
.col-picker-grid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(170px, 1fr));
  gap: 4px 12px; padding: 10px 4px; font-size: 13px;
}
.col-picker-grid label { display: flex; align-items: center; gap: 6px; white-space: nowrap; }
.col-picker-actions { padding: 4px 4px 2px; }

.must-ship-flag {
  background: var(--warning-bg); border: 1px solid var(--warning-border); color: var(--warning-text);
  padding: 10px 14px; border-radius: 6px; font-weight: 600; font-size: 13px; margin-bottom: 14px;
}

.note-item, .shipment-item { padding: 12px 0; border-bottom: 1px solid var(--border); }
.note-item:last-child, .shipment-item:last-child { border-bottom: none; }
.note-item .meta, .shipment-item .meta { color: var(--muted); font-size: 12px; margin-top: 3px; }

/* Printable Job Ticket (RJ, 2026-09-04): hide site chrome and anything
   marked .no-print (the top nav bar, tab strip, flash messages, and any
   action button/section within a page that shouldn't end up on paper --
   e.g. the Job Ticket's own Print button and its Download-quote-PDF card).
   Whichever tab panel is currently .active (the Job Ticket, in practice --
   that's the only tab with a Print button) is unaffected and prints as-is. */
/* Phase 16 (RJ, 2026-09-07): "Make the default print layout portrait." --
   Chrome/most browsers otherwise remember whatever orientation was last
   picked in the print dialog; this makes portrait the page's own default
   so a fresh print (or "Reset" in the print dialog) always starts there. */
@page { size: portrait; }

@media print {
  .topbar, .job-tabs, .flash, .no-print { display: none !important; }

  /* Job Ticket printing (RJ, 2026-09-05): each card (Customer / Press Time /
     Schedule, the job summary, one per book part, Files & Shipping) should
     never split across a page boundary unless it genuinely doesn't fit on
     one page -- break-inside:avoid does exactly that: the browser keeps a
     card together whenever it can, and only lets a card that's taller than
     a full page flow onto the next one (there's no other way to render
     content too big for one page, and that's the one allowed exception).
     CSS Grid containers (the 3-column Customer/Press Time/Schedule row) are
     switched to plain block stacking for print only -- Chromium's print
     pagination for CSS Grid is unreliable and is the most common cause of a
     stray blank page appearing mid-document; a simple stacked block layout
     paginates cleanly and never does that. Screen layout is untouched --
     these rules apply only inside @media print. */
  .card { break-inside: avoid; page-break-inside: avoid; }
  .grid { display: block; }
  .grid > * { margin-bottom: 12px; }
  .grid > *:last-child { margin-bottom: 0; }
}

/* Condensed Job Ticket (RJ, 2026-09-06 follow-up): "My job tickets still
   take up too much space. I like the overall formatting, but make it so
   everything is smaller and more condensed." Same layout/content as every
   other tab -- these rules just tighten spacing/type size, and are scoped
   to #panel-ticket (the Job Ticket tab's own container) so no other tab or
   page is affected. Applies on screen AND in the printed ticket (@media
   print above only hides chrome / fixes pagination -- it doesn't set
   sizing), so the printed copy is more condensed too, which is the actual
   point of a "takes up too much space" complaint about a printed document. */
#panel-ticket .card { padding: 8px 12px; margin-bottom: 8px; }
#panel-ticket h2 { font-size: 13px; margin: 0 0 5px; }
#panel-ticket table { font-size: 11px; }
#panel-ticket th, #panel-ticket td { padding: 3px 6px; }
#panel-ticket .subtitle { margin-bottom: 6px; font-size: 11px; }
#panel-ticket .grid { gap: 8px; }
/* Phase 16 (RJ, 2026-09-07): "Condense the space required for the page 1
   information even further" -- one more pass on top of the Phase 15
   condensing above, tight enough that Customer/Assigned/Press Time, the
   job summary, each book part's spec (now including Special Instructions),
   and Files & Shipping all fit on page 1 of a typical single-book-part job. */
#panel-ticket table th, #panel-ticket table td { line-height: 1.25; }

/* RJ, 2026-10-31: "Page 1 and 2 both contain very important information that I want to be visible
   on the front page. Find a way to keep exactly the same amount of information, but adjust the
   formatting so that the sections in page one and 2 both fit on the first page (except roll width
   layout, that can go on page 2)." Printed only -- the screen keeps its layout: Customer / Assigned /
   Press Time side by side, each book part's spec in two columns, tighter type, and the text
   layout(s) on their own page right after page 1. */
#panel-ticket .ticket-layouts-print { display: none; }
@media print {
  #panel-ticket .ticket-layouts-inline { display: none; }
  #panel-ticket .ticket-layouts-print { display: block; page-break-before: always; break-before: page; }
  #panel-ticket .card { padding: 5px 9px; margin-bottom: 5px; }
  #panel-ticket h2 { font-size: 11.5px; margin: 0 0 3px; }
  #panel-ticket table { font-size: 9.5px; }
  #panel-ticket th, #panel-ticket td { padding: 1.5px 5px; line-height: 1.2; }
  #panel-ticket th { font-size: 9px; letter-spacing: 0.02em; }
  #panel-ticket .subtitle { font-size: 9.5px; margin-bottom: 3px; }
  #panel-ticket .ticket-header { margin-bottom: 5px; }
  #panel-ticket .ticket-logo { height: 32px; }
  #panel-ticket .ticket-field-label, #panel-ticket .ticket-info-form label,
  #panel-ticket .ticket-date-field label { font-size: 9.5px; margin: 0 0 1px; }
  #panel-ticket .ticket-field-value { font-size: 12px; }
  #panel-ticket .ticket-info-form input[type=text], #panel-ticket .ticket-date-field input[type=date] {
    padding: 2px 5px; font-size: 11px; min-width: 0; width: 120px; }
  /* Customer | Assigned | Press Time in one row (a flex row paginates cleanly, unlike print grids) */
  #panel-ticket > .grid.grid-3 { display: flex; gap: 5px; align-items: stretch; }
  #panel-ticket > .grid.grid-3 > * { flex: 1 1 0; margin: 0; min-width: 0; }
  #panel-ticket > .grid.grid-3 > :first-child { flex-grow: 1.6; }
  /* each book part's spec rows in two columns */
  #panel-ticket table.ticket-spec, #panel-ticket table.ticket-spec > tbody { display: block; width: 100%; }
  #panel-ticket table.ticket-spec > tbody { column-count: 2; column-gap: 14px; }
  #panel-ticket table.ticket-spec tr { display: grid; grid-template-columns: 42% 58%; break-inside: avoid;
    page-break-inside: avoid; }
  #panel-ticket table.ticket-spec th { width: auto !important; }
  #panel-ticket table.ticket-spec td { overflow-wrap: anywhere; }
  #panel-ticket .ticket-part-card p.subtitle { font-size: 9.5px !important; }
}

/* RJ, 2026-11-04: the text's margins under its layout diagram (app/text_margins.py) -- plain black
   text, a thin rule above, kept together on the printed ticket. */
.text-margins { margin: 8px 0 0; padding-top: 6px; border-top: 1px dashed var(--border); font-size: 12px;
  line-height: 1.35; break-inside: avoid; page-break-inside: avoid; }
.text-margins p { margin: 0 0 2px; }
.text-margins-title { font-size: 1em; }
.text-margins-from { font-weight: 400; color: var(--muted, #666); }
.text-margins-lines { margin: 2px 0 2px 18px; padding: 0; }
.text-margins-lines li { margin: 0 0 1px; }
.text-margins-note { color: var(--muted); font-size: 0.92em; }
.text-margins-pending { color: var(--muted, #666); font-style: italic; }
#panel-ticket .text-margins { font-size: 11px; }
@media print {
  #panel-ticket .text-margins { font-size: 10px; color: #000; }
  #panel-ticket .text-margins-from, #panel-ticket .text-margins-note, #panel-ticket .text-margins-pending { color: #222; }
}

/* RJ, 2026-11-01: "Make it very evident when printing the job ticket whether an
   insert is bound in or not." A heavy-bordered, bold badge on each insert row
   (solid black for UNBOUND so it can't be missed in a black-and-white print). */
.insert-badge { display: inline-block; font-weight: 800; letter-spacing: 0.04em; padding: 1px 7px;
  border: 2px solid #111; border-radius: 3px; font-size: 0.95em; margin-right: 6px; }
.insert-badge-bound { background: #fff; color: #111; }
.insert-badge-unbound { background: #111; color: #fff; }
.insert-badge-note { font-weight: 700; }
.ticket-insert-row .insert-detail { margin-top: 2px; }
.ticket-insert-row .insert-fold { font-weight: 700; }
@media print {
  .insert-badge { -webkit-print-color-adjust: exact; print-color-adjust: exact; border-width: 2px; }
  .insert-badge-unbound { background: #000 !important; color: #fff !important; }
  #panel-ticket table.ticket-spec tr.ticket-insert-row { outline: 2px solid #000; }
}

/* Job Ticket header: logo top-left, due/must-ship dates top-right, above the
   Customer/Assigned/Press Time row (RJ, 2026-09-07 follow-up: "put the due
   date and must ship date in the top right corner of the ticket, even above
   the customer section... add the company logo in the top left"). Kept
   tight/inline so this whole header plus the 3-card row below it stays
   within roughly the top third of a printed page, per RJ's own goal. */
#panel-ticket .ticket-header {
  display: flex; justify-content: space-between; align-items: flex-start;
  gap: 16px; margin-bottom: 12px;
}
#panel-ticket .ticket-header-left { display: flex; align-items: center; gap: 10px; }
#panel-ticket .ticket-logo { height: 40px; width: auto; }
#panel-ticket .ticket-job-number { font-size: 14px; font-weight: 700; }
#panel-ticket .ticket-dates-form { display: flex; align-items: flex-end; gap: 10px; }
#panel-ticket .ticket-date-field label { margin: 0 0 3px; font-size: 11px; }
#panel-ticket .ticket-date-field input[type=date] { min-width: 130px; padding: 5px 7px; font-size: 12px; }
#panel-ticket .ticket-dates-form .btn { padding: 6px 14px; font-size: 12px; }

/* Phase 16 (RJ, 2026-09-07): Estimate name / ISBN / CSN printed side by
   side with the job number, top-left, next to the logo -- same compact
   inline-field pattern as the due/must-ship dates on the right. */
#panel-ticket .ticket-header-fields { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; }
#panel-ticket .ticket-field { display: flex; flex-direction: column; }
#panel-ticket .ticket-field-label { font-size: 11px; color: var(--muted, #666); }
#panel-ticket .ticket-field-value { font-size: 13px; font-weight: 700; }
#panel-ticket .ticket-info-form { display: flex; align-items: flex-end; gap: 10px; }
#panel-ticket .ticket-info-form label { margin: 0 0 3px; font-size: 11px; }
#panel-ticket .ticket-info-form input[type=text] { min-width: 110px; padding: 5px 7px; font-size: 12px; }
#panel-ticket .ticket-info-form .btn { padding: 6px 14px; font-size: 12px; }
/* RJ, 2026-11-01: an ISBN and CSN per book part on a multi-part job */
#panel-ticket .ticket-part-ids { width: auto; font-size: 11px; }
#panel-ticket .ticket-part-ids th, #panel-ticket .ticket-part-ids td { padding: 2px 6px 2px 0; border: none; }
#panel-ticket .ticket-part-ids input[type=text] { min-width: 130px; padding: 3px 6px; font-size: 12px; }
@media print { #panel-ticket .ticket-part-ids input[type=text] { width: 120px; min-width: 0; padding: 1px 4px; font-size: 10px; } }

/* Phase 17 (RJ, 2026-09-07): "I want to make a 'pack via' sheet... and I
   want to print it in the back of my job tickets." Lives inside
   #panel-ticket (so it prints along with everything else there) but starts
   its own page whenever printed -- RJ's chosen default of appending it as
   an extra page in the same Job Ticket print/PDF, rather than only being
   reachable as a separate page. The standalone page (jobs/pack_via.html,
   the "optional alternative" he also asked to keep) reuses this exact same
   markup/CSS outside of #panel-ticket, so .pack-via-sheet itself carries no
   page-break rule -- only the print wrapper below does. */
.pack-via-sheet h2 { font-size: 16px; margin: 0 0 4px; letter-spacing: 0.5px; }
.pack-via-sheet .pv-subtitle { font-size: 12px; margin: 0 0 10px; line-height: 1.4; }

/* Phase 18 (RJ, 2026-09-08): table-based redesign matching RJ's uploaded
   "Ship Via" reference doc -- one row per pack, columns Due Date/Ship Via
   (carrier)/Qty/Ship Desc./Ship Details/Ship Remarks/Wgt/Bindery/Ship To,
   plus a secondary full-width row for title breakdown/comments. */
.pack-via-table { width: 100%; border-collapse: collapse; font-size: 11px; }
.pack-via-table th, .pack-via-table td { border: 1px solid var(--border, #ccc); padding: 5px 7px; text-align: left; vertical-align: top; }
.pack-via-table thead th { background: var(--panel-alt-bg); font-size: 10px; text-transform: uppercase; letter-spacing: 0.3px; }
.pack-via-table tr { break-inside: avoid; page-break-inside: avoid; }
.pack-via-table .pv-detail-row td { background: var(--panel-alt-bg); font-size: 10.5px; }
.pack-via-table .pv-title { font-weight: 600; }
.pv-badges { margin-top: 4px; }
.pv-badges .badge { margin: 0 4px 2px 0; display: inline-block; }
.pack-via-sheet .pv-footer { display: flex; flex-wrap: wrap; gap: 6px 22px; font-size: 10px; margin-top: 10px; text-transform: uppercase; letter-spacing: 0.2px; }
#panel-ticket .pack-via-print-wrapper { page-break-before: always; margin-top: 16px; }
@media print {
  .pack-via-table th, .pack-via-table td { border-color: #999; }
}
/* RJ, 2026-11-01: "At the top right of a pack via add a X of N indicator ... add a pack via summary
   report at the very end that lists a summary of all the pack vias all on one sheet." Each pack via on
   the ticket is its own page with "Pack via X of N" top right; the summary is a compact one-sheet table
   (app/pack_via_ticket.py chooses where a very long one continues onto another sheet). */
.pack-via-sheet .pv-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.pack-via-sheet .pv-x-of-n { font-size: 18px; font-weight: 800; border: 2px solid #111; padding: 2px 10px;
  text-align: right; white-space: nowrap; line-height: 1.2; }
.pack-via-sheet .pv-x-of-n-pack { display: block; font-size: 10px; font-weight: 600; letter-spacing: 0.02em; }
#panel-ticket .pack-via-summary-wrapper { page-break-before: always; break-before: page; margin-top: 16px; }
.pack-via-summary .pvs-head { display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  flex-wrap: wrap; margin-bottom: 4px; }
.pack-via-summary h2 { font-size: 14px; margin: 0; letter-spacing: 0.5px; }
.pack-via-summary .pvs-meta { font-size: 10px; }
.pvs-table, .pvs-title-totals { width: 100%; border-collapse: collapse; font-size: 9px; line-height: 1.2; }
.pvs-table th, .pvs-table td, .pvs-title-totals th, .pvs-title-totals td {
  border: 1px solid var(--border, #ccc); padding: 1.5px 4px; text-align: left; vertical-align: top; }
.pvs-table thead th, .pvs-title-totals th { background: var(--panel-alt-bg); font-size: 8.5px;
  text-transform: uppercase; letter-spacing: 0.2px; }
.pvs-table .num, .pvs-title-totals .num { text-align: right; white-space: nowrap; }
.pvs-table .pvs-n { white-space: nowrap; font-weight: 700; }
.pvs-table .pvs-pack, .pvs-table .pvs-city { white-space: nowrap; }
.pvs-table .pvs-sub, .pvs-table .pvs-flags { font-size: 8px; }
.pvs-table .pvs-flags { font-weight: 700; }
.pvs-table tr { break-inside: avoid; page-break-inside: avoid; }
.pvs-totals { margin-top: 6px; font-size: 10px; }
.pvs-totals .pvs-title-totals { width: auto; min-width: 50%; margin-top: 3px; }
#panel-ticket .pack-via-summary-wrapper table.pvs-table th, #panel-ticket .pack-via-summary-wrapper table.pvs-table td,
#panel-ticket .pack-via-summary-wrapper table.pvs-title-totals th, #panel-ticket .pack-via-summary-wrapper table.pvs-title-totals td {
  font-size: 9px; padding: 1.5px 4px; line-height: 1.2; }
@media print {
  .pvs-table th, .pvs-table td, .pvs-title-totals th, .pvs-title-totals td { border-color: #999; }
  .pack-via-sheet .pv-x-of-n { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}
/* RJ, 2026-11-02: "a diagram that prints before the pack vias on a job ticket showing how many boxes to
   stack on a layer ... with legible points of reference." One portrait Letter sheet, black and white,
   nothing under 9pt (the ticket's own condensed print sizes are overridden here). */
#panel-ticket .palletizing-print-wrapper { page-break-before: always; break-before: page; break-inside: avoid;
  page-break-inside: avoid; margin-top: 16px; }
#panel-ticket .palletizing-sheet { color: #000; font-size: 10pt; line-height: 1.25; }
#panel-ticket .palletizing-sheet .pal-head { display: flex; justify-content: space-between; align-items: baseline;
  gap: 10px; flex-wrap: wrap; border-bottom: 2px solid #000; padding-bottom: 3px; margin-bottom: 5px; }
#panel-ticket .palletizing-sheet h2 { font-size: 17pt; margin: 0; letter-spacing: 0.3px; }
#panel-ticket .palletizing-sheet .pal-count { font-size: 22pt; font-weight: 800; }
#panel-ticket .palletizing-sheet .pal-job { font-size: 9.5pt; }
#panel-ticket .palletizing-sheet .pal-calculated { border: 2px dashed #000; padding: 4px 8px; margin: 0 0 6px; font-size: 10pt; }
#panel-ticket .palletizing-sheet table.pal-facts { width: 100%; border-collapse: collapse; font-size: 10pt; margin-bottom: 4px; }
#panel-ticket .palletizing-sheet table.pal-facts th, #panel-ticket .palletizing-sheet table.pal-facts td {
  font-size: 10pt; padding: 2px 6px; line-height: 1.25; border: 1px solid #888; text-align: left; vertical-align: top; }
#panel-ticket .palletizing-sheet table.pal-facts th { width: 1%; white-space: nowrap; background: none; font-size: 9.5pt; }
#panel-ticket .palletizing-sheet .pal-desc { font-size: 9pt; }
#panel-ticket .palletizing-sheet .pal-packs span { display: inline-block; margin-right: 12px; white-space: nowrap; font-size: 9pt; }
#panel-ticket .palletizing-sheet .pal-layers { display: flex; justify-content: space-between; gap: 0.1in; margin: 4px 0 2px; }
#panel-ticket .palletizing-sheet .pal-layer { margin: 0; text-align: center; flex: 0 0 auto; }
#panel-ticket .palletizing-sheet .pal-layer figcaption { font-size: 11pt; margin-bottom: 2px; }
#panel-ticket .palletizing-sheet .pallet-svg { display: block; margin: 0 auto; max-width: 100%; height: auto; }
#panel-ticket .palletizing-sheet ul { margin: 3px 0; padding-left: 0; list-style: none; }
#panel-ticket .palletizing-sheet .pal-legend li { font-size: 9.5pt; margin: 2px 0; display: flex; align-items: center; gap: 6px; }
#panel-ticket .palletizing-sheet .pal-key { display: inline-flex; align-items: center; justify-content: center;
  width: 18px; height: 14px; flex: 0 0 auto; font-weight: 800; font-size: 10pt; border: 1px solid #000; }
#panel-ticket .palletizing-sheet .pal-key-arrow { border: none; }
#panel-ticket .palletizing-sheet .pal-key-circle { border-radius: 50%; width: 15px; height: 15px; }
#panel-ticket .palletizing-sheet .pal-key-svg { flex: 0 0 auto; }
#panel-ticket .palletizing-sheet .pal-key-grey { background: #d9d9d9; }
#panel-ticket .palletizing-sheet .pal-notes { border: 2px solid #000; padding: 4px 8px; margin-top: 5px; }
#panel-ticket .palletizing-sheet .pal-notes li { font-size: 11pt; font-weight: 700; margin: 2px 0; }
#panel-ticket .palletizing-sheet .pal-notes li::before { content: "* "; }
@media print {
  #panel-ticket .palletizing-sheet .pal-key-grey,
  #panel-ticket .palletizing-sheet .pallet-svg { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

/* Phase 28 (RJ, 2026-09-10): "I want it to print behind the ship via when
   I'm printing out my job ticket." Same page-break-before pattern as Pack
   Via's own wrapper above -- each ready runsheet (one per book part) gets
   its own page(s) after the Ship Via sheet. See jobs/_runsheet_macro.html. */
#panel-ticket .runsheet-print-wrapper { page-break-before: always; margin-top: 16px; }
/* RJ, 2026-10-30: cover templates print with the job ticket, one per page (landscape art scaled to fit). */
#panel-ticket .cover-template-print-wrapper { page-break-before: always; break-inside: avoid; margin-top: 16px; }
@media print { #panel-ticket .cover-template-print-wrapper img { max-height: 9.3in; object-fit: contain; } }
.runsheet-sheet h2 { font-size: 16px; margin: 0 0 4px; letter-spacing: 0.5px; }
.runsheet-sheet .pv-subtitle { font-size: 12px; margin: 0 0 10px; line-height: 1.4; }
.runsheet-cover-table { width: 100%; border-collapse: collapse; font-size: 11px; margin-bottom: 8px; }
.runsheet-cover-table th, .runsheet-cover-table td { border: 1px solid var(--border, #ccc); padding: 5px 7px; text-align: left; width: 25%; }
.runsheet-cover-table thead th, .runsheet-cover-table tr:first-child th { background: var(--runsheet-dark); color: #fff; font-size: 10px; text-transform: uppercase; letter-spacing: 0.3px; }
.runsheet-endsheet-table tr:first-child th { background: var(--runsheet-mid); }
.runsheet-grid-table { width: 100%; border-collapse: collapse; font-size: 10px; }
.runsheet-grid-table th, .runsheet-grid-table td { border: 1px solid var(--border, #ccc); padding: 3px 5px; text-align: left; }
.runsheet-grid-table thead th { background: var(--runsheet-dark); color: #fff; font-size: 9px; text-transform: uppercase; letter-spacing: 0.3px; }
.runsheet-grid-table tr { break-inside: avoid; page-break-inside: avoid; }
.runsheet-notes { font-size: 10px; margin-top: 8px; }
/* Phase 29 (RJ, 2026-09-10): "highlight insert cells on the runsheet" --
   matches render_runsheet_pdf()'s own #ffe066 highlight so the printed PDF
   and the printed HTML behind the Ship Via sheet agree. print-color-adjust
   is needed because browsers drop background colors on print by default. */
.runsheet-grid-table .runsheet-insert-cell { background: #ffe066; font-weight: 600; }
@media print {
  .runsheet-cover-table th, .runsheet-cover-table td,
  .runsheet-grid-table th, .runsheet-grid-table td { border-color: #999; }
  .runsheet-grid-table .runsheet-insert-cell {
    background: #ffe066 !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
}

/* Phase 19 (RJ, 2026-09-08): "make the pack via editable and savable" --
   the standalone Pack Via page's inputs sit right in the table cells, sized
   to disappear into the sheet's look rather than reading as a form; on
   print (@media print) they flatten to plain borderless text so a printed
   sheet looks identical to the read-only embedded copy. */
.pack-via-table .pv-input { width: 100%; min-width: 90px; border: 1px solid transparent; background: transparent; padding: 2px 3px; font-size: 11px; font-family: inherit; }
.pack-via-table .pv-input:hover, .pack-via-table .pv-input:focus { border-color: var(--border, #ccc); background: #fff; }
.pack-via-table .pv-input-narrow { min-width: 55px; }
@media print {
  .pack-via-table .pv-input { border: none !important; background: transparent !important; padding: 0; }
}

/* Prepress Portal (Phase 30, RJ, 2026-09-11) */
.badge.needs_review { background: var(--warning-bg); color: var(--warning-text); }
.badge.error { background: var(--error-bg); color: var(--error-text); }
.badge.ready { background: var(--success-bg); color: var(--success-text); }
.badge.pending, .badge.processing { background: var(--neutral-bg); color: var(--neutral-text); }
.check-status { display: inline-block; padding: 1px 8px; border-radius: 10px; font-size: 11px; font-weight: 600; }
.check-status.pass { background: var(--success-bg); color: var(--success-text); }
.check-status.fixed { background: var(--info-bg); color: var(--info-text); }
.check-status.flag { background: var(--warning-bg); color: var(--warning-text); }
.check-status.block { background: var(--error-bg); color: var(--error-text); }
.check-status.warning { background: var(--warning-bg); color: var(--warning-text); border: 1px solid var(--warning-border, #e8ddb5); }
.prepress-checks-table td, .prepress-checks-table th { vertical-align: top; }

/* Shop Floor kiosk (Phase 31, RJ, 2026-09-11): big, touch-friendly controls
   for a shared shop-floor terminal -- no login, just tap a name. */
/* RJ, 2026-10-26: the kiosk's name groups (1st, 2nd, 3rd, Office, POD). */
.kiosk-groups { display: flex; flex-wrap: wrap; gap: 10px; margin: 6px 0 18px; }
.kiosk-group-tab {
  display: inline-flex; align-items: center; gap: 8px; padding: 14px 26px; min-height: 56px; border-radius: 12px;
  background: var(--panel); border: 2px solid var(--border); color: var(--text); text-decoration: none;
  font-size: 18px; font-weight: 700;
}
.kiosk-group-tab:hover { border-color: var(--accent); }
.kiosk-group-tab.active { background: var(--accent); border-color: var(--accent); color: #fff; }
.kiosk-group-tab.loose { font-weight: 600; font-size: 15px; border-style: dashed; }
.kiosk-group-count { font-size: 13px; font-weight: 600; opacity: 0.7; }
.kiosk-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(160px, 1fr)); gap: 14px; }
.kiosk-name-btn {
  display: flex; align-items: center; justify-content: center; text-align: center;
  background: var(--panel); border: 1px solid var(--border); border-top: 4px solid var(--accent); border-radius: 12px;
  padding: 28px 12px; text-decoration: none; color: var(--text); font-size: 17px; font-weight: 600;
  min-height: 84px; transition: border-color 0.12s ease, box-shadow 0.12s ease, transform 0.12s ease;
}
.kiosk-name-btn:hover { border-color: var(--accent); box-shadow: 0 3px 14px rgba(0,0,0,0.07); transform: translateY(-1px); }
/* Phase 44 (RJ, 2026-09-13): this whole kiosk screen is nothing but a grid
   of these buttons -- the exact "boring white below the header" case RJ
   called out -- so cycle the same 5-swatch chip set used on Settings
   through their top edges too, purely decorative (tapping a name works
   identically regardless of its color). */
.kiosk-name-btn:nth-child(5n+2) { border-top-color: var(--info-text); }
.kiosk-name-btn:nth-child(5n+3) { border-top-color: var(--success-text); }
.kiosk-name-btn:nth-child(5n+4) { border-top-color: var(--warning-text); }
.kiosk-name-btn:nth-child(5n+5) { border-top-color: var(--neutral-text); }
.kiosk-status-card { max-width: 520px; margin: 0 auto; text-align: center; }
/* RJ, 2026-10-26: paper used on a rollfed press run. */
.kiosk-paper { text-align: left; margin-top: 14px; padding: 14px; border: 1px solid var(--border); border-radius: 12px; background: var(--panel); }
.kiosk-paper h3 { margin: 0 0 10px; }
.kiosk-paper-q { margin: 14px 0 6px; font-weight: 600; }
.kiosk-paper-choice { display: flex; align-items: flex-start; gap: 10px; padding: 10px; border: 1px solid var(--border); border-radius: 10px; margin: 0 0 8px; font-weight: normal; cursor: pointer; }
.kiosk-paper-choice input { width: auto; margin-top: 4px; }
.kiosk-paper-hint { color: var(--muted, #667); font-size: 13px; }
.kiosk-paper-error { color: var(--danger-text, #b42318); font-weight: 600; margin: 8px 0 0; }
.kiosk-status-card .kiosk-current {
  background: var(--info-bg); color: var(--info-text); border-radius: 10px; padding: 16px; margin-bottom: 18px;
  font-size: 15px; text-align: left;
}
.kiosk-big-btn {
  display: block; width: 100%; padding: 22px; font-size: 20px; font-weight: 700; border-radius: 10px;
  border: none; cursor: pointer; margin-bottom: 12px;
}
.kiosk-big-btn.start { background: var(--accent); color: #fff; }
.kiosk-big-btn.end { background: var(--error-bg); color: var(--error-text); }
.kiosk-big-btn.clock { background: var(--panel); color: var(--text); border: 2px solid var(--border); }
.kiosk-job-picked { text-align: left; background: var(--panel); border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; margin-top: 8px; }

/* Login / PIN pad (Phase 35, RJ, 2026-09-13) */
.login-wrap {
  min-height: calc(100vh - 60px);
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 28px;
}
.login-card { width: 100%; max-width: 360px; text-align: center; }
.login-card h1 { margin-bottom: 4px; }
.login-card form { text-align: left; }
.login-card .btn { width: 100%; margin-top: 18px; }

.pin-display {
  font-size: 32px;
  letter-spacing: 0.3em;
  text-align: center;
  border: 1px solid var(--border);
  border-radius: 8px;
  padding: 14px;
  margin-bottom: 18px;
  min-height: 1.4em;
  background: var(--panel);
}
.pin-pad {
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  gap: 10px;
}
.pin-pad button {
  font-size: 22px;
  padding: 18px 0;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: white;
  cursor: pointer;
}
.pin-pad button:active { background: var(--hover-bg); }
.pin-pad button.pin-clear, .pin-pad button.pin-submit { font-size: 15px; font-weight: 600; }
.pin-pad button.pin-submit { background: var(--accent); color: white; border-color: var(--accent); }


   /* RJ (2026-10-01) bugfix: elements across the app toggle visibility via
   the bare `hidden` attribute (e.g. .grid/.grid-2/.grid-3/.grid-4 fields
   like cover_cloth_color_field, split_cover_back_cloth_color_field,
   split_bind_cloth_color_field), but any class here that sets its own
   `display` (like .grid's `display: grid` above) ties the UA stylesheet's
   `[hidden]{display:none}` at equal specificity, and author CSS wins that
   tie -- so `hidden` silently stopped hiding those fields, leaving an
   empty, still-`required` Cloth Color <select> visible (and failing
   native form validation, so "Save changes" did nothing) whenever the
   cover stock wasn't actually cloth. Same footgun .modal-overlay above
   already documents, generalized here with !important so no future class
   can re-break `hidden` the same way. */
[hidden] { display: none !important; }

/* RJ, 2026-10-16: an overridden blocking prepress check. */
.check-status.overridden { background: #ede7f6; color: #4527a0; }

/* Job spec changes (RJ, 2026-10-16) */
.spec-change-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; margin: 6px 0 10px; }
.spec-change-table td, .spec-change-table th { vertical-align: top; }
.radio-row { display: block; font-weight: 400; margin: 6px 0; }
.radio-row input[type=radio] { width: auto; margin-right: 6px; }
.copy-link-row { display: flex; gap: 8px; align-items: center; }
.badge.spec-change-draft { background: #eceff1; color: #37474f; }
.badge.spec-change-pending { background: #fff3e0; color: #e65100; }
.badge.spec-change-approved, .badge.spec-change-applied { background: #e8f5e9; color: #1b5e20; }
.badge.spec-change-declined, .badge.spec-change-cancelled { background: #ffebee; color: #b71c1c; }
.spec-change-table { width: 100%; border-collapse: collapse; margin: 6px 0 12px; }
.spec-change-table th, .spec-change-table td { text-align: left; padding: 6px 8px; border-bottom: 1px solid #e0e0e0; }

/* RJ, 2026-10-16: Ready for Proofing job status + Prepress "Send to proofing" */
.badge.ready_for_proofing { background: #e3f2fd; color: #0d47a1; }
.proofing-checklist { list-style: none; padding: 0; margin: 6px 0 10px; }
.proofing-checklist li { margin: 3px 0; }
.proofing-checklist .ok { color: #1b5e20; }
.proofing-checklist .missing { color: var(--error-text); }

/* ---- Help Center + ? tips on buttons (RJ, 2026-10-22; app/help_center.py, static/help.js) ---- */
.has-help-q { position: relative; }
.help-q {
  position: absolute; top: -7px; right: -7px; z-index: 5;
  width: 16px; height: 16px; border-radius: 50%;
  background: #fff; color: var(--accent); border: 1px solid var(--accent);
  font: 700 10px/14px system-ui, sans-serif; text-align: center;
  cursor: help; box-shadow: 0 1px 2px rgba(0,0,0,.15);
  text-transform: none; letter-spacing: 0; text-decoration: none;
}
.help-q::before { content: "?"; }
.help-q:hover { background: var(--accent); color: #fff; }
.help-q.help-q-after { position: relative; display: inline-block; top: -10px; right: auto; margin-left: -6px; vertical-align: top; }
.help-tip {
  position: fixed; z-index: 10000; max-width: 300px;
  background: var(--runsheet-dark); color: #fff; padding: 8px 10px; border-radius: 6px;
  font-size: 13px; line-height: 1.4; box-shadow: 0 4px 14px rgba(0,0,0,.25); pointer-events: none;
}
.help-tip-more { margin-top: 4px; font-size: 12px; opacity: .8; }
.help-hero { background: var(--panel); border: 1px solid var(--border); border-radius: 10px; padding: 26px 24px; margin-bottom: 18px; text-align: center; }
.help-hero h1 { margin: 0 0 12px; }
.help-search { display: flex; gap: 8px; max-width: 720px; margin: 0 auto; }
.help-search input[type="search"] { flex: 1; margin: 0; font-size: 15px; padding: 10px 12px; }
.help-categories { display: grid; grid-template-columns: repeat(auto-fill, minmax(230px, 1fr)); gap: 12px; margin-bottom: 18px; }
.help-category { display: flex; flex-direction: column; gap: 4px; color: inherit; text-decoration: none; margin: 0; transition: border-color .15s, transform .15s; }
.help-category:hover { border-color: var(--accent); transform: translateY(-1px); text-decoration: none; }
.help-category .subtitle { margin: 0; }
.help-count { font-size: 12px; color: var(--muted); margin-top: auto; }
.help-list { margin: 8px 0 0; padding-left: 18px; }
.help-list li { margin: 6px 0; }
.help-article-layout { display: grid; grid-template-columns: minmax(0, 1fr) 280px; gap: 16px; margin-top: 14px; align-items: start; }
.help-aside .card { margin-bottom: 12px; }
.help-summary { font-size: 16px; color: var(--muted); margin-top: 0; }
.help-body h2 { margin-top: 22px; padding-top: 10px; border-top: 1px solid var(--border); }
.help-body ol li, .help-body ul li { margin: 5px 0; }
.help-body table { border-collapse: collapse; margin: 10px 0; }
.help-body th, .help-body td { border: 1px solid var(--border); padding: 6px 8px; text-align: left; }
.help-feedback { margin-top: 24px; padding-top: 14px; border-top: 1px solid var(--border); }
.help-feedback form { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.help-result { margin-bottom: 10px; }
.help-result .badge { margin-left: 6px; }
.help-version { border-top: 1px solid var(--border); padding: 8px 0; }
@media (max-width: 860px) { .help-article-layout { grid-template-columns: 1fr; } }
@media print { .help-q, .help-tip, .nav-help { display: none !important; } }

/* RJ, 2026-10-22 (speed): customer picker (static/customer_picker.js) -- keyboard-highlighted match. */
.typeahead-item.active { background: var(--hover-bg); }

/* RJ, 2026-10-24: Scheduling Portal additions. */
.error-text { color: var(--error-text); }
.scheduler-notes { background: #fff7e0; border: 1px solid #e8c766; border-radius: 6px; padding: 8px 12px; margin: 10px 0 4px; }
.scheduler-note { margin-top: 4px; }
.scheduling-ticket { margin: 10px 0 4px; }
.scheduling-ticket iframe { width: 100%; height: 75vh; min-height: 520px; border: 1px solid var(--border); border-radius: 6px; background: #fff; }
.recommended-date .rec-date { font-size: 15px; }
/* The job page as a ticket-only view (?view=ticket), embedded on the Scheduling Portal. */
body.ticket-only .topbar, body.ticket-only .nav-back-bar, body.ticket-only .job-header-row form,
body.ticket-only .spec-change-actions, body.ticket-only .spec-change-list, body.ticket-only .job-alerts,
body.ticket-only .purgatory-banner, body.ticket-only .job-tabs, body.ticket-only .flash { display: none !important; }
body.ticket-only .tab-panel { display: none !important; }
body.ticket-only #panel-ticket { display: block !important; }
/* Read-only there: schedule from the portal's own form, not the ticket's Save buttons. */
body.ticket-only #panel-ticket form button[type=submit] { display: none !important; }
body.ticket-only #panel-ticket form input, body.ticket-only #panel-ticket form select { pointer-events: none; background: transparent; }
/* RJ, 2026-10-24: Mentis estimate report + customer clarification. */
.ai-report { border-left: 4px solid #e8c766; }
.ai-report ul { margin: 4px 0 8px 18px; padding: 0; }
.ai-report li { margin: 2px 0; }
.ai-report .ai-flag-high { color: var(--error-text); }
.ai-customer-choice { display: flex; gap: 8px; align-items: flex-start; margin: 6px 0; font-weight: 400; }
/* RJ, 2026-10-24: pack shipping-requirement pop-ups (close button in a random corner, never top right). */
.req-popups { position: fixed; inset: 0; background: rgba(20, 30, 40, 0.45); z-index: 1000; display: flex; align-items: center; justify-content: center; }
.req-popups[hidden] { display: none; }
.req-popup { position: relative; background: #fffdf3; border: 2px solid #e8c766; border-radius: 10px; width: min(480px, 92vw); padding: 22px 26px 64px; box-shadow: 0 10px 30px rgba(0,0,0,.25); }
.req-popup[hidden] { display: none; }
.req-popup-title { margin: 0 0 8px; font-size: 17px; }
.req-popup ul { margin: 0 0 0 18px; padding: 0; }
.req-popup li { margin: 4px 0; }
.req-popup-close { position: absolute; }
.req-popup-close.corner-tl { top: 10px; left: 10px; }
.req-popup-close.corner-bl { bottom: 12px; left: 12px; }
.req-popup-close.corner-br { bottom: 12px; right: 12px; }
.req-popup:has(.corner-tl) { padding-top: 56px; }
.req-summary { background: #fff7e0; border: 1px solid #e8c766; border-radius: 6px; padding: 8px 12px; margin: 8px 0; }
.req-summary ul { margin: 4px 0 0 18px; padding: 0; }
.req-violations { border-color: var(--error-text); background: #fdecec; }

/* RJ, 2026-10-24: freight quote & dispatch (MyCarrier style) */
.freight-quote h3 { margin: 14px 0 6px; }
.freight-quote h4 { margin: 0 0 6px; font-size: 14px; }
.fq-stop { border: 1px solid var(--border); border-radius: 6px; padding: 10px 12px; }
.fq-items td, .fq-items th, .fq-grid td, .fq-grid th { padding: 4px 6px; white-space: nowrap; }
.fq-items select, .fq-items input { margin: 0; }
.fq-tiers { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 10px; }
.fq-tier { flex: 1 1 200px; border: 1px solid var(--border); border-radius: 8px; padding: 10px 12px; background: #f8fafc; }
.fq-tier-title { font-weight: 700; }
.fq-tier-price { font-size: 22px; font-weight: 800; margin: 4px 0; }
.fq-blocked td { color: #9aa3ad; text-decoration: line-through; }
.fq-blocked td:last-child { text-decoration: none; color: var(--error-text); }
.fq-pick { background: #eef7ee; }
.fq-booked { background: #eef7ee; border: 1px solid #b7dcb9; border-radius: 6px; padding: 10px 12px; margin-bottom: 10px; }
.fq-dry-run pre { white-space: pre-wrap; font-size: 11px; max-height: 320px; overflow: auto; }

/* RJ, 2026-10-25: the optional previous-job box on reprints -- shown only
   once Exact Reprint / Reprint with changes is ticked, and meant to stand out. */
.prev-job-box { border: 2px solid #d4a017; border-left-width: 6px; background: #fff7d6; border-radius: 8px;
                padding: 12px 14px; margin: 8px 0 12px; box-shadow: 0 1px 4px rgba(0,0,0,.08); }
.prev-job-box .prev-job-title { font-weight: 700; font-size: 16px; display: block; margin-bottom: 2px; color: #5c4300; }
.prev-job-box .prev-job-help { font-size: 13px; color: #6b5a2a; margin: 0 0 8px; }
.prev-job-box input[type=text] { max-width: 320px; }

/* RJ, 2026-10-26: carton labels -- cartons from the pack via's Package Details. */
.label-cartons-table { width: auto; margin: 2px 0 0; }
.label-cartons-table th, .label-cartons-table td { padding: 4px 12px 4px 0; font-size: 13px; }
.label-cartons-warn { color: var(--error-text); }
/* RJ, 2026-10-26: checkbox + its label on one line (the pack via's "On hold" box sat alone above its label). */
.checkbox-row { display: flex; align-items: flex-start; gap: 10px; margin: 10px 0; }
.checkbox-row input[type=checkbox] { width: auto; margin-top: 3px; flex: 0 0 auto; }
.checkbox-row label { margin: 0; }
.btn.btn-compact { padding: 5px 10px; font-size: 13px; }
#pkg-body select { min-width: 100px; }
.pack-labels-link { font-size: 12px; white-space: nowrap; }
/* RJ, 2026-10-26: "+ Create vendor" / "+ Create inventory item" on the PO form. */
.link-btn { background: none; border: none; padding: 0; color: var(--accent); font: inherit; cursor: pointer; text-decoration: underline; }
.po-create-btn { display: inline-block; margin-top: 6px; font-size: 13px; font-weight: 600; }
.po-create-error { color: var(--error-text); font-weight: 600; margin: 8px 0 0; }
.po-lines-table select { min-width: 110px; }

/* RJ, 2026-10-27: FSC jobs -- "add green highlights within the job". */
:root { --fsc-green: #2e7d32; --fsc-green-bg: #e8f5e9; }
.badge.fsc-badge, .fsc-badge {
  background: var(--fsc-green); color: #fff; border-radius: 4px; padding: 1px 6px;
  font-size: 11px; font-weight: 700; letter-spacing: .04em; vertical-align: middle;
}
.fsc-banner {
  background: var(--fsc-green-bg); border: 1px solid var(--fsc-green); border-left: 6px solid var(--fsc-green);
  color: #1b5e20; border-radius: 6px; padding: 8px 12px; margin: 0 0 12px; font-weight: 600;
}
body.fsc-job .job-header-row h1 { color: var(--fsc-green); }
body.fsc-job .job-tabs a.active { color: var(--fsc-green); border-bottom-color: var(--fsc-green); }
body.fsc-job .card { border-left: 4px solid var(--fsc-green); }
body.fsc-job #panel-ticket .ticket-header { border-bottom-color: var(--fsc-green); }
body.fsc-job #panel-ticket .ticket-field-value.fsc-number { background: var(--fsc-green-bg); color: #1b5e20; padding: 0 4px; border-radius: 3px; }
.fsc-text { color: var(--fsc-green); font-weight: 700; }
tr.fsc-row > td:first-child { box-shadow: inset 4px 0 0 var(--fsc-green); }
tr.fsc-row a.row-link, tr.fsc-row .job-number { color: var(--fsc-green); font-weight: 700; }
@media print {
  .fsc-badge { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
  body.fsc-job #panel-ticket .ticket-field-value.fsc-number { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

/* ---- Project Manager Portal (RJ, 2026-11-02: "far too many buttons ... a much more manageable,
   readable way") -- Needs your action + My jobs in the plant (templates/pm_portal/index.html). ---- */
.badge.pending_pm_review, .badge.payment_purgatory { background: var(--warning-bg); color: var(--warning-text); }
.topbar a.nav-pm-actions { margin-left: auto; background: var(--brand-green); color: #fff; font-weight: 700; opacity: 1;
  padding: 3px 9px; border-radius: 999px; font-size: calc(var(--nav-fs) - 1px); }
.topbar a.nav-pm-actions .n { display: inline-block; min-width: 1.2em; text-align: center; margin-left: 4px;
  background: #fff; color: var(--brand-green); border-radius: 999px; padding: 0 5px; }
.topbar a.nav-pm-actions:hover { color: #fff; background: #00382f; text-decoration: none; }
.topbar a.nav-pm-actions:not([hidden]) + a.nav-alerts,
.topbar a.nav-pm-actions:not([hidden]) + .nav-account { margin-left: 0; }
.pm-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 24px; }
.pm-a-job a.row-link { display: inline; font-weight: 600; }
.pm-head h1 { margin-bottom: 8px; }
.pm-head .subtitle, .pm-head .flash { margin: 0 0 10px; }
.pm-section-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 2px 14px; margin-bottom: 8px; }
.pm-section-head h2 { margin: 0; }
.pm-section-head .subtitle { margin: 0; font-size: 13px; }
.pm-total { display: inline-block; min-width: 1.6em; text-align: center; background: var(--brand-green); color: #fff;
  border-radius: 999px; padding: 1px 8px; font-size: 14px; vertical-align: 2px; }
.pm-caught-up { padding: 14px 16px; border-radius: 8px; background: var(--success-bg); color: var(--success-text); }
details.pm-act { border: 1px solid var(--border); border-left: 5px solid var(--accent); border-radius: 8px;
  margin: 6px 0; background: var(--panel); }
details.pm-act.is-soon { border-left-color: #c98a00; }
details.pm-act.is-late { border-left-color: var(--error-text); }
details.pm-act > summary { list-style: none; cursor: pointer; display: flex; align-items: center; flex-wrap: wrap;
  gap: 6px 12px; padding: 9px 14px; font-size: 15px; }
details.pm-act > summary::-webkit-details-marker { display: none; }
details.pm-act > summary::before { content: "\25B8"; color: var(--muted); width: 10px; }
details.pm-act[open] > summary::before { content: "\25BE"; }
details.pm-act > summary:hover { background: var(--hover-bg); }
.pm-act-n { font-weight: 700; font-size: 17px; min-width: 1.8em; text-align: right; }
.pm-act-label { font-weight: 600; }
.pm-act-meta { margin-left: auto; color: var(--muted); font-size: 13px; }
.pm-new { display: inline-block; background: #e3f2fd; color: #0d47a1; border-radius: 999px; padding: 1px 8px;
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .03em; }
.pm-late-tag { display: inline-block; background: var(--error-bg); color: var(--error-text); border-radius: 999px;
  padding: 1px 8px; font-size: 12px; font-weight: 700; }
.pm-act-body { padding: 0 14px 10px; }
.pm-act-body > .subtitle { margin: 0 0 6px; font-size: 13px; }
table.pm-act-table td { vertical-align: top; padding: 8px; }
table.pm-act-table tr.is-new td { background: #f5f9ff; }
.pm-a-job { width: 22%; min-width: 150px; }
.pm-a-detail { overflow-wrap: anywhere; }
.pm-a-when { width: 150px; white-space: nowrap; }
.pm-a-do { width: 1%; white-space: nowrap; text-align: right; }
.pm-a-do form { display: inline-block; margin: 0 0 0 6px; vertical-align: top; }
.pm-a-do .btn { padding: 6px 12px; font-size: 13px; }
.pm-a-sub { color: var(--muted); font-size: 12.5px; }
details.pm-inline { display: inline-block; vertical-align: top; text-align: left; }
details.pm-inline > summary { list-style: none; }
details.pm-inline > summary::-webkit-details-marker { display: none; }
details.pm-inline[open] { display: block; margin-bottom: 6px; }
.pm-a-do .pm-inline-form { display: flex; flex-wrap: wrap; gap: 6px; align-items: flex-end; margin: 6px 0 0; white-space: normal; }
.pm-inline-form input[type=number] { width: 110px; }
.pm-inline-form input[type=text] { width: 140px; }
.pm-reply textarea { width: min(420px, 70vw); }
.pm-date { white-space: nowrap; }
.pm-date.is-late { color: var(--error-text); font-weight: 700; }
.pm-date.is-soon { color: #8a5a00; font-weight: 700; }
.pm-toolbar { display: flex; flex-wrap: wrap; gap: 8px 14px; align-items: center; margin: 4px 0 10px; }
.pm-stages { display: flex; flex-wrap: wrap; gap: 6px; }
a.pm-stage { display: inline-flex; align-items: center; gap: 6px; padding: 5px 11px; border: 1px solid var(--border);
  border-radius: 999px; background: var(--panel); color: var(--text); text-decoration: none; font-size: 13px; white-space: nowrap; }
a.pm-stage span { background: var(--neutral-bg); color: var(--neutral-text); border-radius: 999px; padding: 0 7px; font-size: 12px; }
a.pm-stage:hover { background: var(--hover-bg); }
a.pm-stage.is-on { background: var(--accent); border-color: var(--accent); color: #fff; }
a.pm-stage.is-on span { background: #fff; color: var(--accent); }
.pm-search { display: flex; gap: 6px; align-items: center; flex: 1 1 280px; max-width: 460px; }
.pm-search input[type=search] { flex: 1; min-width: 0; padding: 7px 10px; border: 1px solid var(--border); border-radius: 7px;
  font: inherit; font-size: 14px; }
.pm-search .btn { padding: 7px 12px; }
.pm-clear { font-size: 13px; white-space: nowrap; }
details.pm-more { flex-basis: 100%; }
details.pm-more > summary { cursor: pointer; color: var(--accent); font-size: 13px; width: max-content; }
.pm-more-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 0 14px; align-items: end;
  padding: 4px 0 8px; }
.pm-more-grid label { margin-top: 8px; }
.pm-more-grid .pm-check { display: flex; align-items: center; gap: 6px; font-weight: 400; }
.pm-more-grid .pm-check input { width: auto; }
table.pm-jobs { font-size: 13.5px; }
table.pm-jobs th, table.pm-jobs td { padding: 7px 8px; vertical-align: top; }
table.pm-jobs th.is-sortable { cursor: pointer; user-select: none; white-space: nowrap; }
table.pm-jobs th[aria-sort=ascending]::after { content: " \25B4"; }
table.pm-jobs th[aria-sort=descending]::after { content: " \25BE"; }
table.pm-jobs tr.pm-row { cursor: pointer; }
table.pm-jobs tr.is-yours td:first-child { box-shadow: inset 4px 0 0 var(--brand-green); }
table.pm-jobs td:first-child { white-space: nowrap; font-weight: 600; }
.pm-cust { font-weight: 600; }
.pm-wait { display: inline-block; margin-top: 2px; border-radius: 999px; padding: 0 8px; font-size: 11.5px; font-weight: 600;
  background: var(--neutral-bg); color: var(--neutral-text); white-space: nowrap; }
.pm-wait-you { background: var(--brand-green); color: #fff; }
.pm-wait-customer { background: var(--warning-bg); color: var(--warning-text); }
.pm-wait-prepress { background: #e3f2fd; color: #0d47a1; }
.pm-flag-late, .pm-flag-soon { display: inline-block; border-radius: 4px; padding: 0 6px; font-size: 11px; font-weight: 700; }
.pm-flag-late { background: var(--error-bg); color: var(--error-text); }
.pm-flag-soon { background: #fff3e0; color: #8a5a00; }
.pm-alert { display: inline-block; margin: 0 3px 3px 0; border-radius: 4px; padding: 1px 5px; font-size: 10.5px; font-weight: 700;
  letter-spacing: .03em; background: var(--neutral-bg); color: var(--neutral-text); cursor: help; }
.pm-alert-hold { background: var(--error-bg); color: var(--error-text); }
.pm-alert-bac { background: var(--warning-bg); color: var(--warning-text); }
.pm-alert-bac-ok { background: var(--success-bg); color: var(--success-text); }
.pm-alert-fsc { background: var(--fsc-green); color: #fff; }
.pm-alert-event { background: #fff3e0; color: #8a5a00; }
.pm-alert-labels { background: #ede7f6; color: #4527a0; }
.pm-alert-stale { background: var(--error-bg); color: var(--error-text); }
.pm-alert-prepress { background: #e3f2fd; color: #0d47a1; }
.pm-just { color: var(--success-text); }
.pm-col-alerts { min-width: 90px; }
@media (max-width: 1100px) {
  .pm-col-pm { display: none; }
  .pm-a-job { width: 30%; }
}
@media (max-width: 820px) {
  .pm-col-age { display: none; }
  table.pm-act-table, table.pm-act-table tbody, table.pm-act-table tr, table.pm-act-table td { display: block; width: auto; }
  table.pm-act-table tr { border-bottom: 1px solid var(--border); padding: 6px 0; }
  table.pm-act-table td { border: none; padding: 3px 6px; }
  .pm-a-do { text-align: left; white-space: normal; }
  .pm-a-do form { margin: 0 6px 0 0; }
  .pm-act-meta { margin-left: 0; }
}

/* Show / hide password (RJ, 2026-11-03) -- static/password_toggle.js puts an eye button inside every
   password field's right edge; 44px wide so it's easy to tap on a phone. */
.pw-wrap { position: relative; display: block; }
.pw-wrap > input { padding-right: 46px; }
.pw-toggle {
  position: absolute; top: 0; right: 0; bottom: 0; width: 44px; min-height: 36px;
  display: flex; align-items: center; justify-content: center;
  margin: 0; padding: 0; border: 0; background: transparent; color: var(--muted);
  cursor: pointer; border-radius: 0 7px 7px 0; -webkit-tap-highlight-color: transparent;
}
.pw-toggle:hover { color: var(--text); background: var(--hover-bg); }
.pw-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: -2px; }
.pw-toggle[aria-pressed="true"] { color: var(--text); }
.login-card .pw-toggle { width: 44px; margin-top: 0; }

/* 2026-11-05: the public demo's banner and sign-ins (app/demo_mode.py) and the setup wizard (app/setup_wizard.py). */
.demo-banner { background: #1f2a44; color: #fff; text-align: center; padding: 6px 12px; font-size: 13px; font-weight: 600; letter-spacing: .02em; }
.demo-banner a { color: #ffd76a; }
.demo-logins { margin-top: 18px; border-top: 1px solid var(--border, #d4dae2); padding-top: 12px; text-align: left; }
.demo-logins h2 { font-size: 16px; margin: 0 0 4px; }
.demo-logins code { font-size: 12px; }
.setup-wizard { display: grid; grid-template-columns: 240px 1fr; gap: 16px; align-items: start; }
.setup-steps ol { list-style: none; margin: 0; padding: 0; }
.setup-steps li { padding: 6px 0; border-bottom: 1px solid var(--border, #e3e6ea); }
.setup-steps li.current a { font-weight: 700; }
.setup-steps li .badge { margin-left: 4px; }
.setup-nav { margin-top: 8px; }
.setup-note { background: var(--warning-bg); padding: 10px 12px; border-radius: 6px; }
@media (max-width: 800px) { .setup-wizard { grid-template-columns: 1fr; } }
