:root {
  --bg: #f5f6f8;
  --panel: #ffffff;
  --border: #e2e5ea;
  --text: #1a1d23;
  --muted: #6b7280;
  --accent: #1f2a44;
  --accent-2: #3b5bdb;
  --good: #1a7f37;
  --bad: #c92a2a;
  --warn: #b8860b;
  /* aliases used throughout the quote/portal styles */
  --line: var(--border);
  --ink: var(--text);
  --band: var(--bg);
  /* --card is what a white surface got called in everything added since; the
     token is --panel. An undefined var drops the whole declaration, so the
     backgrounds simply were not there -- a menu with no background, on a page
     with a table behind it, is a menu you cannot read. */
  --card: var(--panel);
  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: inherit;
  font-size: 14px;
}

a { color: var(--accent-2); text-decoration: none; }
a:hover { text-decoration: underline; }

.layout { display: flex; min-height: 100vh; }

.sidebar {
  width: 210px;
  background: var(--accent);
  color: #fff;
  flex-shrink: 0;
  padding: 20px 0;
}
.sidebar .brand {
  font-weight: 700;
  font-size: 16px;
  padding: 0 20px 20px;
  border-bottom: 1px solid rgba(255,255,255,0.12);
  margin-bottom: 10px;
}
.sidebar nav a {
  display: block;
  padding: 10px 20px;
  color: rgba(255,255,255,0.85);
  font-size: 14px;
}
.sidebar nav a:hover, .sidebar nav a.active {
  background: rgba(255,255,255,0.08);
  color: #fff;
  text-decoration: none;
}
.sidebar .nav-group {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: rgba(255,255,255,0.4); padding: 16px 20px 4px;
}
.sidebar nav a.nav-sub { padding-left: 32px; }

.sidebar .user {
  margin-top: 30px;
  padding: 10px 20px;
  font-size: 12px;
  color: rgba(255,255,255,0.6);
}

.main { flex: 1; padding: 28px 36px; max-width: 1200px; }

h1 { font-size: 22px; margin: 0 0 4px; }
h2 { font-size: 17px; margin: 24px 0 10px; }
.subtitle { color: var(--muted); margin-bottom: 20px; }

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

.stat-row { display: flex; gap: 16px; flex-wrap: wrap; margin-bottom: 20px; }
.stat {
  background: var(--panel);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 16px 20px;
  min-width: 150px;
}
.stat .label { font-size: 12px; color: var(--muted); text-transform: uppercase; letter-spacing: .03em; }
.stat .value { font-size: 22px; font-weight: 700; margin-top: 4px; }
.stat .value.good { color: var(--good); }
.stat .value.bad { color: var(--bad); }

table { width: 100%; border-collapse: collapse; font-size: 13px; }
th { text-align: left; color: var(--muted); font-weight: 600; font-size: 12px; text-transform: uppercase;
  letter-spacing: .02em; padding: 8px 10px; border-bottom: 1px solid var(--border); }
td { padding: 10px; border-bottom: 1px solid var(--border); vertical-align: top; }
tr:last-child td { border-bottom: none; }
tr:hover td { background: #fafbfc; }
th.sortable-col { cursor: pointer; user-select: none; }
th.sortable-col:hover { color: var(--text); }
.sort-arrow { font-size: 10px; margin-left: 3px; opacity: .6; }

.badge {
  display: inline-block; padding: 2px 10px; border-radius: 999px; font-size: 12px; font-weight: 600;
  background: #eef1f5; color: var(--accent);
}
.badge.Draft { background: #eef1f5; color: #4b5563; }
.badge.Sent { background: #e7f0ff; color: #1d4ed8; }
.badge.Won { background: #e6f7ec; color: #15803d; }
.badge.Lost { background: #fdeaea; color: #b91c1c; }
.badge.Deposit-Received { background: #fff4e0; color: #b8860b; }
.badge.In-Production { background: #f0eaff; color: #6b21a8; }
.badge.Delivered { background: #e0f7f5; color: #0f766e; }
.badge.Paid-in-Full { background: #e6f7ec; color: #15803d; }

/* manufacturer quote-request pipeline */
.badge.Preparing-RFQ { background: #eef1f5; color: #4b5563; }
.badge.Preparing { background: #eef1f5; color: #4b5563; }
.badge.RFQ-Sent { background: #e7f0ff; color: #1d4ed8; }
.badge.RFQ-Received { background: #e7f0ff; color: #1d4ed8; }
.badge.RFQ-Approved { background: #f0eaff; color: #6b21a8; }
.badge.Quoting { background: #f0eaff; color: #6b21a8; }
.badge.Quote-Received { background: #e6f7ec; color: #15803d; }
/* Past quoting entirely: an order exists and the job is being built. Reads as
   In-Production does on a quote, because that is what it is. */
.badge.Ordered { background: #f0eaff; color: #6b21a8; }
.badge.Complete { background: #e6f7ec; color: #15803d; }
.badge.On-Track { background: #e6f7ec; color: #15803d; }
.badge.Need-Clarification { background: #fff4e0; color: #b8860b; }
.badge.Date-Change-Requested { background: #fff4e0; color: #b8860b; }
.badge.At-Risk { background: #fdeaea; color: #b91c1c; }
.badge.No-Response { background: #fdeaea; color: #b91c1c; }
.badge.Overdue { background: #fdeaea; color: #b91c1c; }
.badge.Cancelled { background: #eef1f5; color: #6b7280; }
.badge.High { background: #fff4e0; color: #b8860b; }
.badge.Rush { background: #fdeaea; color: #b91c1c; }

/* to-do kinds on the dashboard queue */
.badge.Question { background: #e7f0ff; color: #1d4ed8; }
.badge.New-date-requested { background: #fff4e0; color: #b8860b; }
.badge.Date-mismatch { background: #fff4e0; color: #b8860b; }
.badge.Progress-concern { background: #fff4e0; color: #b8860b; }
.badge.Quote-came-back { background: #e6f7ec; color: #15803d; }
.badge.Marked-complete { background: #e6f7ec; color: #15803d; }
.badge.Import-failed { background: #fdeaea; color: #b91c1c; }
.badge.Chase-them { background: #fdeaea; color: #b91c1c; }
.badge.Follow-up { background: #eef1f5; color: #4b5563; }

/* production, reported by the manufacturer after a PO goes out */
.badge.Awaiting-Shop-Drawings { background: #eef1f5; color: #4b5563; }
.badge.In-Production { background: #f0eaff; color: #6b21a8; }
.badge.Completed { background: #e6f7ec; color: #15803d; }
.badge.Shipping { background: #e0f7f5; color: #0f766e; }
.badge.Delayed { background: #fff4e0; color: #b8860b; }
.badge.Problem { background: #fdeaea; color: #b91c1c; }

/* to-do kind for a production concern */
.badge.Production-problem { background: #fdeaea; color: #b91c1c; }

/* follow-up chases -- a quote that has stopped moving */
.badge.Draft-never-sent { background: #eef1f5; color: #4b5563; }
.badge.Sent-no-answer { background: #fff4e0; color: #b8860b; }
.badge.Won-no-deposit { background: #fdeaea; color: #b91c1c; }
.badge.Deposit-in-not-in-production { background: #fff4e0; color: #b8860b; }
.badge.In-production-not-delivered { background: #fff4e0; color: #b8860b; }
.badge.Delivered-not-paid-in-full { background: #fdeaea; color: #b91c1c; }

.warn { color: var(--warn); }
.card.attention { border-color: #f0c98a; background: #fffdf7; }
hr.rule { border: none; border-top: 1px solid var(--border); margin: 18px 0; }
ul.checklist { margin: 8px 0 14px; padding-left: 18px; }
ul.checklist li { margin-bottom: 4px; }

/* message thread, shown to both sides */
.thread { margin: 10px 0 16px; }
.msg { padding: 10px 12px; border-radius: 8px; margin-bottom: 8px; max-width: 90%; }
.msg .prewrap { white-space: pre-wrap; margin-top: 3px; }
.msg.us { background: #eef1f5; }
.msg.them { background: #e7f0ff; margin-left: auto; }

/* one row per vendor: contacts stacked in their cell, link in the next */
.contact-line {
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  padding: 3px 0;
}
.contact-line .small.muted { display: block; }
.linkrow.compact { flex-wrap: wrap; }
/* wide enough to recognise the link, not so wide it crowds out the contacts */
/* the row claims the space left beside the name; the input inside it must NOT
   grow, or it eats that space and pushes the buttons onto a second line */
.mfr-head > .linkrow.compact { flex: 1 1 auto; }
.linkrow.compact input { flex: 0 1 185px; min-width: 120px; font-size: 11px; padding: 4px 7px; }

/* a form kept behind a button -- <details> so it needs no JavaScript, with the
   default disclosure triangle removed so the summary reads as the button it is */
.reveal > summary { display: inline-block; cursor: pointer; list-style: none; }
.reveal > summary::-webkit-details-marker { display: none; }
.reveal[open] > summary { margin-bottom: 4px; }

/* one-of-three status picker on the manufacturer's page */
.choice-row { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 6px; }
.choice {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  padding: 9px 14px; border: 1px solid var(--border); border-radius: 8px;
  background: #fff; font-weight: 600; font-size: 13px; margin: 0;
}
.choice input { width: auto; margin: 0; }
.choice:has(input:checked) { border-color: var(--accent-2); background: #eef2ff; color: var(--accent-2); }

/* settings section tabs */
.settings-tabs {
  display: flex; flex-wrap: wrap; gap: 4px;
  border-bottom: 1px solid var(--border); margin-bottom: 20px;
}
.settings-tabs a {
  padding: 8px 14px; font-size: 13px; font-weight: 600; color: var(--muted);
  border: 1px solid transparent; border-bottom: none;
  border-radius: 8px 8px 0 0; margin-bottom: -1px;
}
.settings-tabs a:hover { color: var(--text); text-decoration: none; background: #fafbfc; }
.settings-tabs a.active {
  color: var(--text); background: var(--panel);
  border-color: var(--border); border-bottom-color: var(--panel);
}

/* dashboard to-do queue */
.todo-filters { display: flex; gap: 6px; }
.mfr-block { border: 1px solid var(--border); border-radius: 8px; padding: 14px; margin-bottom: 12px; }
.mfr-head { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }

.linkrow { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
.linkrow input {
  flex: 1; min-width: 260px; padding: 6px 9px; font-size: 12px; font-family: ui-monospace, monospace;
  border: 1px solid var(--border); border-radius: 6px; background: #fafbfc; color: var(--muted);
}

/* manufacturer's secure page -- standalone, no sidebar */
body.portal { background: var(--bg); }
.portal-wrap { max-width: 760px; margin: 0 auto; padding: 28px 20px 60px; }
.portal-header { margin-bottom: 22px; }
.portal-brand {
  font-size: 13px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--accent-2); margin-bottom: 6px;
}
.portal-title { font-size: 22px; font-weight: 700; }
.portal-foot { text-align: center; margin-top: 28px; }

.portal .card label { display: block; font-size: 12px; color: var(--muted); font-weight: 600; margin: 12px 0 4px; }
.portal .card input[type=text], .portal .card input:not([type]), .portal .card input[type=date],
.portal .card input[type=file], .portal .card textarea {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px;
  font: inherit; background: #fff;
}
.portal .card .btn { margin-top: 14px; }

table.kv { width: 100%; }
table.kv th {
  text-align: left; width: 190px; vertical-align: top; text-transform: none; letter-spacing: 0;
  font-size: 13px; color: var(--muted); font-weight: 600; padding: 7px 12px 7px 0; border: none;
}
table.kv td { padding: 7px 0; border: none; }
table.kv tr:hover td { background: none; }

.action { padding: 14px 0; border-bottom: 1px solid var(--border); }
.action:last-of-type { border-bottom: none; }
.checkline { display: flex; align-items: center; gap: 10px; font-size: 14px; color: var(--text) !important;
  margin: 0 !important; text-transform: none; font-weight: 400 !important; }
.checkline input { width: 16px; height: 16px; }

.btn-row { display: flex; gap: 8px; flex-wrap: wrap; }
.btn-row .btn { margin-top: 0; }
.two-up { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.plainlist { margin: 8px 0; padding-left: 18px; }
.plainlist li { margin-bottom: 5px; }
.prewrap { white-space: pre-wrap; }

@media (max-width: 560px) {
  table.kv th { width: auto; display: block; padding-bottom: 0; }
  table.kv td { display: block; padding-top: 2px; }
  .two-up { grid-template-columns: 1fr; }
}

.btn {
  display: inline-block; padding: 9px 16px; border-radius: 7px; border: 1px solid var(--accent-2);
  background: var(--accent-2); color: #fff; font-size: 13px; font-weight: 600; cursor: pointer;
}
.btn:hover { opacity: .92; text-decoration: none; }
.btn.secondary { background: #fff; color: var(--text); border-color: var(--border); }
.btn.small { padding: 5px 10px; font-size: 12px; }

form.inline { display: inline; }

.form-grid { display: grid; grid-template-columns: repeat(2, 1fr); gap: 14px; max-width: 640px; }
.form-grid.wide { max-width: 100%; }
.form-grid label { display: block; font-size: 12px; color: var(--muted); margin-bottom: 4px; font-weight: 600; }
.form-grid input, .form-grid select, .form-grid textarea {
  width: 100%; padding: 8px 10px; border: 1px solid var(--border); border-radius: 6px; font-size: 13px;
  font-family: inherit;
}
.field-full { grid-column: 1 / -1; }
/* Money and percentage boxes are a handful of characters wide. Two columns
   capped at 640px left most of the card empty; four across fills it and puts
   the freight figures on one line where they can be read together. */
.form-grid.four { grid-template-columns: repeat(4, 1fr); max-width: 100%; }
.form-grid.four .span-2 { grid-column: span 2; }

/* A money or percentage box only ever holds a handful of characters. Sized to
   its content rather than to the column, with the unit sitting beside it so
   the figure reads as a figure and not as a bare number. */
.unit { display: flex; align-items: baseline; gap: 5px; }
.unit input { flex: 0 0 auto; text-align: right; }
.unit .suffix, .unit .prefix { color: var(--muted); font-size: 12px; }
.form-grid .money input { width: 11ch; }
.form-grid .pct input { width: 7ch; }
.form-grid .money.short input { width: 9ch; }

/* Short fields packed left and wrapping, rather than spread across equal
   columns -- a 90px box in a 468px column leaves the row mostly empty. */
.field-row { grid-column: 1 / -1; display: flex; flex-wrap: wrap;
  gap: 12px 26px; align-items: flex-end; }
.field-row > div { flex: 0 0 auto; }
.field-row > div.grow { flex: 1 1 240px; min-width: 240px; }
.field-row > div.grow input { width: 100%; }

/* Status sits in the header and is changed there, so it never disagrees with
   a copy of itself further down the page. */
.status-pick select { padding: 2px 6px; font-size: 12px; border-radius: 999px;
  border: 1px solid var(--line); background: var(--band); color: var(--ink); }

/* A measured figure, not something you type: same slot in the grid as a
   field, so the row still lines up, but plainly not an input. */
.readout { align-self: center; color: var(--muted); font-size: 13px; }
.readout .figure { display: block; color: var(--ink); font-size: 20px;
  font-weight: 700; line-height: 1.15; }

/* A tickbox with its caption, on its own line. */
.check-line { display: flex; align-items: center; gap: 7px; font-weight: 400;
  font-size: 12px; color: var(--muted); margin-top: -2px; }
.form-grid .check-line input { width: auto; flex: 0 0 auto; margin: 0; }

/* A row of headings inside the grid, so the section reads in groups. */
.grid-heading { grid-column: 1 / -1; font-size: 11px; letter-spacing: .06em;
  text-transform: uppercase; color: var(--muted); font-weight: 700;
  margin: 6px 0 -6px; }
@media (max-width: 900px) {
  .form-grid.four { grid-template-columns: repeat(2, 1fr); }
  .form-grid.four .span-2 { grid-column: span 2; }
}

.flash { padding: 10px 14px; border-radius: 7px; margin-bottom: 14px; font-size: 13px; }
.flash.error { background: #fdeaea; color: #b91c1c; }
.flash.success { background: #e6f7ec; color: #15803d; }
.flash.warning { background: #fff4e0; color: #92660a; }

.filters { display: flex; gap: 10px; margin-bottom: 16px; }
.filters select { padding: 7px 10px; border-radius: 6px; border: 1px solid var(--border); }

.right { text-align: right; }
/* Rows of buttons in a table cell: keep them on one line and let the cell
   take the width it needs, rather than stacking as the table squeezes. */
.nowrap { white-space: nowrap; }
.row-actions { white-space: nowrap; }
.row-actions > * { vertical-align: middle; }
.row-actions > * + * { margin-left: 6px; }
.muted { color: var(--muted); }
.small { font-size: 12px; }
.good { color: var(--good); }
.bad { color: var(--bad); }

.login-wrap {
  min-height: 100vh; display: flex; align-items: center; justify-content: center; background: var(--accent);
}
.login-card {
  background: #fff; border-radius: 12px; padding: 36px; width: 340px;
}
.login-card h1 { text-align: center; margin-bottom: 24px; }
.login-card input { width: 100%; padding: 10px; margin-bottom: 12px; border: 1px solid var(--border); border-radius: 6px; }
.login-card .btn { width: 100%; text-align: center; }

.li-table input, .li-table select { width: 100%; padding: 5px 6px; font-size: 12px; border: 1px solid var(--border); border-radius: 5px; }
.li-table td { padding: 4px 6px; }

/* quote builder: quick nav, collapsible sections, compact cards */
.quicknav {
  position: sticky; top: 0; z-index: 5; background: var(--bg); padding: 8px 0 10px;
  display: flex; gap: 4px; flex-wrap: wrap; border-bottom: 1px solid var(--border); margin-bottom: 16px;
}
.quicknav a {
  font-size: 12px; font-weight: 600; color: var(--text); background: var(--panel);
  border: 1px solid var(--border); border-radius: 999px; padding: 5px 12px;
}
.quicknav a:hover { background: var(--accent-2); color: #fff; border-color: var(--accent-2); text-decoration: none; }

.card.compact { padding: 12px 16px; margin-bottom: 12px; }
.card.compact h2 { margin: 0 0 8px; font-size: 14px; }

details.card { padding: 0; }
details.card summary {
  cursor: pointer; padding: 12px 16px; font-size: 14px; font-weight: 700; list-style: none;
  display: flex; align-items: center; justify-content: space-between;
}
details.card summary::-webkit-details-marker { display: none; }
details.card summary::after { content: "▸"; color: var(--muted); font-weight: 400; transition: transform .15s; }
details.card[open] summary::after { transform: rotate(90deg); }
details.card .details-body { padding: 0 16px 16px; }

.li-toolbar { display: flex; gap: 10px; align-items: center; margin-bottom: 10px; flex-wrap: wrap; }
.li-toolbar input, .li-toolbar select { padding: 6px 10px; border-radius: 6px; border: 1px solid var(--border); font-size: 13px; }
/* Line-item figures read as columns, so they centre -- headings, values and
   the boxes you type into all on the same axis. The `right` class is left on
   the cells so the markup still says what the number is; this just wins. */
.li-table th, .li-table td, .li-table td.right, .li-table th.right { text-align: center; }
.li-table input, .li-table select { text-align: center; }
/* Except the remark, which is a sentence, not a figure. */
.li-remark-row td, .li-remark-row td.right { text-align: left; }
.li-table .li-remark-row input { text-align: left; }

/* The remark belongs to the item above it, so it reads as part of that row
   rather than as a row of its own: no rule between them, indented to the
   width of the first column, and the label sits inline with the field. */
/* An item's details open inside the table, so without real room top and
   bottom the block runs straight into the line above and the next item below,
   and it stops reading as belonging to one item. */
.li-remark-row td { border-top: 0; padding: 20px 14px 24px; background: var(--bg); }
.li-remark-row label { display: inline-block; margin: 0 8px 0 0; font-size: 11px;
  color: var(--muted); text-transform: uppercase; letter-spacing: .04em; vertical-align: middle; }
.li-table .li-remark-row input { width: calc(100% - 70px); display: inline-block;
  vertical-align: middle; }

/* The account picker: columns, so a long chart of accounts doesn't become a
   long scroll. */
.account-picker { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 18px; }

.account-parent > summary { list-style: none; cursor: pointer; margin-bottom: 4px; }
.account-parent > summary::-webkit-details-marker { display: none; }
.account-parent > summary::before { content: "\25B8"; display: inline-block; width: 12px;
  color: var(--muted); font-size: 10px; }
.account-parent[open] > summary::before { content: "\25BE"; }

/* The win-chance picker: one choice, shown as a row of buttons rather than a
   dropdown, because seeing the range is the point. */
.win-chance { display: inline-block; font-weight: normal; margin: 0 6px 6px 0; }
.win-chance input { position: absolute; opacity: 0; width: 0; }
.win-chance span { display: inline-block; padding: 6px 14px; border: 1px solid var(--border);
  border-radius: 999px; background: var(--panel); cursor: pointer; font-size: 13px; }
.win-chance input:checked + span { background: var(--accent-2); border-color: var(--accent-2);
  color: #fff; font-weight: 600; }
.win-chance input:focus-visible + span { outline: 2px solid var(--accent-2); outline-offset: 2px; }

/* The line that answers the question the report was opened for. */
.section-total td { border-top: 2px solid var(--text); background: var(--bg); }

.li-table th.sortable { cursor: pointer; user-select: none; }
.li-table th.sortable:hover { color: var(--text); }
.li-table th.sortable .arrow { font-size: 10px; margin-left: 2px; opacity: .5; }

.danger-zone { border-color: var(--bad); }
.danger-zone summary { color: var(--bad); }

/* expenses */
select.needs-attention { border-color: var(--warn); background: #fffdf7; }
tr.dup-row { background: #fffdf7; }


/* ---------- The quote as one page ---------- */
.quote-head { display: flex; justify-content: space-between; align-items: flex-start;
  gap: 24px; flex-wrap: wrap; margin-bottom: 14px; }
.quote-summary { background: #fff; border: 1px solid var(--line); border-radius: 8px;
  padding: 12px 16px; min-width: 230px; font-size: 13px; }
.quote-summary div { display: flex; justify-content: space-between; gap: 24px; padding: 2px 0; }
.quote-summary div span:first-child { color: var(--muted); }
.quote-summary .grand { border-top: 1px solid var(--line); margin-top: 6px; padding-top: 6px;
  font-weight: 700; font-size: 15px; }

.quote-tabs { display: flex; flex-wrap: wrap; align-items: center; gap: 6px;
  border-bottom: 1px solid var(--line); margin-bottom: 16px; }
.quote-tabs .tab { background: none; border: 0; border-bottom: 2px solid transparent;
  padding: 9px 14px; font: inherit; font-size: 13px; color: var(--muted);
  cursor: pointer; border-radius: 6px 6px 0 0; }
.quote-tabs .tab:hover { color: var(--ink); background: var(--band); }
.quote-tabs .tab.is-active { color: var(--ink); font-weight: 700;
  border-bottom-color: var(--ink); }
.tab-badge { display: inline-block; min-width: 17px; padding: 0 5px; margin-left: 5px;
  border-radius: 9px; background: var(--bad, #c92a2a); color: #fff; font-size: 11px;
  line-height: 17px; text-align: center; font-weight: 700; }

/* Panels: only the chosen one is in the document. */
.panel { display: none; }
.panel.is-active { display: block; }

.manage-menu { position: relative; }
.manage-menu > summary { list-style: none; cursor: pointer; }
.manage-menu > summary::-webkit-details-marker { display: none; }
.manage-items { position: absolute; z-index: 20; top: 100%; left: 0; min-width: 240px;
  background: #fff; border: 1px solid var(--line); border-radius: 8px; padding: 10px;
  display: flex; flex-direction: column; gap: 6px;
  box-shadow: 0 6px 20px rgba(0,0,0,.08); }
.manage-items .btn { text-align: left; }
.move-project { display: flex; flex-direction: column; gap: 4px; }
.move-project select { width: 100%; padding: 6px 8px; font-size: 13px;
  border: 1px solid var(--line); border-radius: 6px; }


/* Markup on the left, the figures it produces on the right -- the numbers are
   what you are watching while you change the inputs, so they belong beside
   them rather than scrolled away above. */
.pricing-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 430px);
  gap: 16px; align-items: start; }
.kpi-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px; }
.kpi-grid .stat { margin: 0; min-width: 0; padding: 12px 10px; }
.kpi-grid .stat .value { font-size: 19px; }
.kpi-grid .stat .label { font-size: 10px; }
@media (max-width: 1150px) {
  /* below this the two columns squeeze each other, so stack them and let the
     tiles run the full width instead */
  .pricing-row { grid-template-columns: minmax(0, 1fr); }
}

/* Who the job is for, across the top. */
.quote-facts { display: flex; flex-wrap: wrap; gap: 10px 34px; margin: 0 0 16px;
  padding: 12px 18px; background: #fff; border: 1px solid var(--line);
  border-radius: 8px; }
.quote-facts > div { display: flex; flex-direction: column; align-items: center;
  gap: 4px; }
.quote-facts .fact { white-space: nowrap; }
.quote-facts a.fact:hover { border-color: var(--ink); }

.head-id { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.head-id h1 { margin: 0; }
.head-status { display: flex; flex-direction: column; align-items: center; gap: 2px; }
.head-actions { display: flex; align-items: flex-start; gap: 12px; }
/* Preview Quote and Send are the same button in two colours, so they are
   measured by one rule -- different font sizes made them different heights and
   left them a pixel and a half out of line with each other. */
.head-actions .preview-quote-btn,
.head-actions .manage-menu > summary {
  font-size: 13px; line-height: 1.2; padding: 9px 16px; border-radius: 7px;
  white-space: nowrap; }
/* the kebab would be lost at button size, so only it stays large */
.head-actions .manage-menu > summary .dots { font-size: 17px; line-height: 0;
  vertical-align: -2px; margin-left: 4px; }
.head-actions .manage-items { right: 0; left: auto; }

/* The status reads as the badge it replaced, and is still the control. */



/* The extra markups and what each category totals, side by side -- you set one
   while watching the other, so they read as a pair. */
.markup-row { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 16px; align-items: start; }
.markup-row .form-grid { max-width: 100%; }
@media (max-width: 1000px) { .markup-row { grid-template-columns: minmax(0, 1fr); } }


/* ---------- Line items, laid out as the mockup ---------- */
.li-head { display: flex; justify-content: space-between; align-items: center;
  gap: 16px; margin-bottom: 10px; }

/* Everything about one item, under its row: what the customer reads on the
   left, what it was priced at before on the right. */
.li-detail { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 300px);
  gap: 18px; align-items: start; }
.li-fields { display: flex; flex-direction: column; gap: 8px; }
.li-field { display: flex; align-items: center; gap: 10px; }
.li-field > label { flex: 0 0 78px; margin: 0; font-size: 11px; text-align: right;
  color: var(--muted); font-weight: 600; }
.li-table .li-field input { width: 100%; text-align: left; }
.li-history { border-left: 1px solid var(--line); padding-left: 14px; }

.li-detail-row { display: flex; flex-wrap: wrap; align-items: flex-end; gap: 12px 18px;
  margin-top: 12px; padding-top: 12px; border-top: 1px solid var(--line); }
.li-detail-row .li-field { flex: 1 1 170px; flex-direction: column; align-items: stretch;
  gap: 3px; }
.li-detail-row .li-field > label { flex: none; text-align: left; }
.li-delete { margin-left: auto; background: #c92a2a; border-color: #c92a2a; }


/* ---------- Quote header: identity, status, running total ---------- */

/* The facts are typed into, not just read, so they look like controls.
   Deliberately NOT scoped to .quote-facts: the billing contact in Accounts
   Payable is the same kind of control and was falling back to browser chrome
   because this rule couldn't reach it. */
.fact { display: inline-block; padding: 6px 14px; border-radius: 999px;
  background: var(--band); border: 1px solid var(--line); font-size: 13px;
  font-weight: 600; color: var(--ink); text-decoration: none; text-align: center;
  min-width: 150px; font-family: inherit; }
input.fact:focus, select.fact:focus {
  outline: none; border-color: var(--ink); background: #fff; }
.fact.plain { background: none; border-color: transparent; }
/* the native arrow doesn't belong on a pill */
select.fact { appearance: none; -webkit-appearance: none; cursor: pointer;
  padding-right: 26px;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%236b7280' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 10px center; }

/* Status carries its own colour so where a quote stands reads at a glance. */
.status-pick select.pill { appearance: none; border-radius: 999px; padding: 5px 16px;
  font: inherit; font-size: 12px; font-weight: 700; text-align: center;
  border: 1px solid transparent; cursor: pointer; }
.status-pick select.status-draft { background: #cdeacd; color: #1f5d2b; }
.status-pick select.status-sent { background: #d9e6fb; color: #1b3f78; }
.status-pick select.status-won,
.status-pick select.status-deposit-received { background: #cfeee0; color: #14603f; }
.status-pick select.status-in-production,
.status-pick select.status-delivered { background: #fdecc8; color: #7a4d05; }
.status-pick select.status-paid-in-full { background: #cfeee0; color: #14603f; }
.status-pick select.status-lost { background: #f8d6d6; color: #8a1c1c; }
.status-pick select.status-superseded { background: #e6e6ea; color: #55555f; }

/* The running total follows you down the whole page -- it is the number every
   change is aimed at, so it should never be scrolled away. Sticky inside the
   header only lasts as long as the header, so it is fixed to the viewport and
   the header keeps a gap for it. */
.head-actions { align-self: flex-start; }
@media (min-width: 1250px) {
  .quote-summary { width: 260px; }
  /* Once it detaches it shrinks to the one number that matters and goes part
     transparent, so following you down never costs you a figure underneath. */
  .quote-summary.floating { position: fixed; top: 12px; right: 24px; z-index: 30;
    width: auto; padding: 8px 16px; background: rgba(255, 255, 255, .82);
    backdrop-filter: blur(3px); box-shadow: 0 4px 16px rgba(0,0,0,.08); }
  .quote-summary.floating > div:not(.grand) { display: none; }
  .quote-summary.floating .grand { border-top: 0; margin: 0; padding: 0; gap: 14px; }
}


/* Status sits under the title and centres on it, not on the page. */
.head-status { margin-top: 14px; }


/* Send is a summary element, which does not pick up .btn's colour on its own. */
/* The summary is styled as a button, so it needs the button's own text colour
   rather than the muted one the manage menus use -- white on the primary,
   normal text once it steps back to secondary behind Preview Quote. */
.send-menu > summary.btn { color: #fff; }
.send-menu > summary.btn:hover { color: #fff; }
.send-menu > summary.btn.secondary,
.send-menu > summary.btn.secondary:hover { color: var(--text, #2b3038); }


/* ---------- Markup: no container, evenly spread, labels over fields ---------- */
.markup-block { padding: 4px 0 8px; }
.markup-block .block-title { margin: 0 0 14px; font-size: 17px; }

/* Every cell the same width so the columns line up down the block, with the
   label centred over what it names. */
.markup-block .form-grid.four { grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 18px 28px; justify-items: center; }
.markup-block .form-grid.four > div { width: 100%; max-width: 190px; text-align: center; }
.markup-block .form-grid label { text-align: center; font-size: 12px; }
.markup-block .unit { justify-content: center; }
.markup-block .money input, .markup-block .pct input { text-align: center; }
.markup-block .readout { text-align: center; }
.markup-block .check-line { justify-content: center; }
/* full-width rows must beat the per-cell cap above, so they match its
   specificity -- otherwise the grouping text is squeezed into a 190px column */
.markup-block .form-grid.four > div.span-2 { max-width: 100%; }
.markup-block .form-grid.four > div.field-full { max-width: 100%; text-align: left; }
.markup-block .form-grid.four > div.field-full label { text-align: left; }

/* The tiles read as soft pills on the page, not as boxes in a card. */
.kpi-grid .stat { border-radius: 14px; text-align: center; }
.kpi-grid .stat .label { letter-spacing: .04em; }


/* Every box in Markup is the same size and reads as a soft filled field --
   the per-field widths made the row look ragged, and the point of the block
   is that the columns line up. */
.markup-block .form-grid input,
.markup-block .form-grid select {
  width: 100%; max-width: 100%;
  background: #ebedf0; border: 1px solid var(--line); border-radius: 10px;
  padding: 10px 12px; text-align: center; color: var(--ink);
}
.markup-block .form-grid input:focus,
.markup-block .form-grid select:focus { background: #fff; border-color: var(--line); outline: none; }
.markup-block .form-grid input::placeholder { color: #9aa0a6; }
.markup-block .form-grid label { color: #5b6472; font-weight: 700; margin-bottom: 6px; }
.markup-block .unit { display: block; }
.markup-block .unit .prefix, .markup-block .unit .suffix { display: none; }
.markup-block .readout .figure { font-size: 18px; }

/* The tiles match: soft fill, generous radius, everything centred. */
.kpi-grid .stat { background: #ebedf0; border: 1px solid #dfe2e6; border-radius: 16px;
  padding: 14px 12px; }


/* The per-field widths set earlier are more specific than the block rule, so
   they are unset explicitly here -- inside Markup every box is one size. */
.markup-block .form-grid .money input,
.markup-block .form-grid .money.short input,
.markup-block .form-grid .pct input { width: 100%; }


/* Save lines up with the field beside it, not with the label above it. */
/* The two Saves are the same button doing the same job, so they share one rule
   and both sit at the left edge of the first column. The grids above them have
   different column counts, so centring each in its own cell would leave them a
   couple of dozen pixels out of line with each other. */
.markup-block .form-grid.four > div.save-cell,
.markup-fold .fold-save { display: flex; align-items: flex-end; justify-content: flex-start; }
.markup-block .save-btn, .markup-fold .save-btn, .li-fold .save-btn {
  background: #6b7280; border-color: #6b7280; color: #fff;
  border-radius: 10px; padding: 10px 30px; font-size: 15px; }
.markup-block .save-btn:hover, .markup-fold .save-btn:hover, .li-fold .save-btn:hover {
  background: #565c66; border-color: #565c66; }


/* The tiles start level with the first row of fields, not with the block's
   heading -- they read as the answer to that row. */
.pricing-row .kpi-grid { margin-top: 37px; }

.markup-block .discount-line { display: flex; align-items: flex-end; }
.markup-block .discount-line input { text-align: left; }


/* A checkbox is not a text field: the block-wide width:100% was stretching it
   across its cell, which pushed its caption onto the next line. */
.markup-block .form-grid input[type="checkbox"] { width: auto; flex: 0 0 auto;
  padding: 0; border-radius: 3px; background: #fff; }
.markup-block .check-line { display: flex; flex-direction: row; align-items: center;
  justify-content: center; gap: 7px; white-space: nowrap; }

/* Two ticks now, stacked under the Domestic Shipping box they belong to. */
.markup-block .form-grid.four > div.tick-cell { display: flex; flex-direction: column;
  align-items: flex-start; justify-content: center; gap: 4px; max-width: 190px; }

/* Says out loud that the tiles are running ahead of the database. Only shown
   once something has been typed -- before that there is nothing to warn about.
   It lives inside the tile grid, spanning it, so it sits directly beneath the
   numbers it is talking about. */
.kpi-grid > .preview-note { grid-column: 1 / -1; margin: 2px 0 0; }
.preview-note {
  margin: 8px 2px 0;
  font-size: 12px;
  color: #8a6d1f;
  background: #fdf6e3;
  border: 1px solid #f0e2b6;
  border-radius: 8px;
  padding: 6px 10px;
}

/* Dimmed, not hidden: under Ex Works the domestic amount and the separate-line
   tick stop counting, but the figures stay put for when the term comes off. */
.markup-block .not-applicable { opacity: .4; }

/* Preview leads, Send follows: reading the quote the customer gets is the
   thing done most often, and it is the safe one of the two. */


/* ---- Additional Markups + By Category: one section, one toggle ---- */
/* The headings sit in the summary so both stay findable while it is shut, on
   the same two columns the body uses so they line up with what they title. */
.markup-fold > summary { list-style: none; cursor: pointer; }
.markup-fold > summary::-webkit-details-marker { display: none; }
.markup-fold .fold-head { display: grid; gap: 16px;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); align-items: center; }
/* Every foldable thing on this page wears the same heading and the same
   box-with-a-plus: the markup section, Items, the Footer, and each row inside
   Items. Drawn rather than an image so it can lose its upright and read as a
   minus once whatever it controls is open. These are deliberately NOT scoped
   to one section -- they were, and Items and the row expanders inherited
   nothing, so their marks came out blank. */
.fold-title { display: flex; align-items: center; gap: 8px;
  font-size: 17px; font-weight: 700; color: var(--ink); }
.fold-mark { width: 15px; height: 15px; border: 1.5px solid #9aa2ad;
  border-radius: 3px; position: relative; flex: 0 0 auto; display: inline-block; }
.fold-mark::before, .fold-mark::after { content: ""; position: absolute; background: #6b7280; }
.fold-mark::before { left: 3px; right: 3px; top: 6px; height: 1.5px; }
.fold-mark::after { top: 3px; bottom: 3px; left: 6px; width: 1.5px; }
details[open] > summary .fold-mark::after { display: none; }
.markup-fold .markup-row { margin-top: 12px; }
.markup-fold .fold-save { margin-top: 16px; }
.markup-fold .form-grid.three { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 14px 18px; margin-top: 12px; }
/* Same pills as the Markup block above -- it is the same kind of field, and
   the two blocks sit within a screen of each other. */
.markup-fold .form-grid input {
  width: 100%; max-width: 100%;
  background: #ebedf0; border: 1px solid var(--line); border-radius: 10px;
  padding: 10px 12px; text-align: center; color: var(--ink);
}
.markup-fold .form-grid input:focus { background: #fff; border-color: var(--line); outline: none; }
.markup-fold .form-grid input::placeholder { color: #9aa0a6; }
.markup-fold .form-grid label { display: block; text-align: center; font-size: 12px;
  color: #5b6472; font-weight: 700; margin-bottom: 6px; }
.markup-fold .unit { display: block; }
.markup-fold .unit .suffix { display: none; }

/* ---- By Category: the numbers and the control over them, together ---- */
.by-category table { width: 100%; }
.by-category .tick-col { width: 58px; text-align: center; }
.by-category .tick-col input { width: auto; margin: 0; }
/* Folded into Other Items -- still listed, still tickable, just not its own
   section on the customer's copy. */
.by-category tr.folded .cat-name, .by-category tr.folded td.right { opacity: .45; }
.by-category tr.other-items .cat-name { font-style: italic; }
.by-category table.ticks-off .tick-col { opacity: .35; }
.by-category .group-toggle { display: flex; align-items: center; gap: 8px;
  margin-top: 14px; font-weight: normal; color: var(--muted); font-size: 13px; }
.by-category .group-toggle input { width: auto; margin: 0; }

/* ---- Items: one row per line, clipped rather than wrapped ---------------
   The column widths come from the longest value each field is expected to
   hold ("Master Bedroom", "$500,891.75", "Corner Window"). Anything longer is
   cut off on screen -- deliberately not wrapped, because a row that grows to
   two lines destroys the scan down the column, and deliberately not capped
   with maxlength, because she still has to be able to type the long ones. */
.li-fold > summary { list-style: none; cursor: pointer; }
.li-fold > summary::-webkit-details-marker { display: none; }
.li-fold .li-head { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; }
.li-fold .li-head-right { display: flex; align-items: center; gap: 12px; }


/* The tick and the per-row expander, ahead of everything else. */
.li-table .pick-col { width: 34px; text-align: center; }
.li-table .exp-col { width: 30px; text-align: center; }
.li-table .pick-col input { width: auto; margin: 0; }
.li-expander { background: none; border: 0; padding: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center; }
/* Open drops the upright, so the plus reads as a minus -- same as the section
   folds above it. */
.li-expander.is-open .fold-mark::after { display: none; }
.li-table tr.picked > td { background: #eef2ff; }

/* Sized to the longest value each column is meant to show. */
.li-table { table-layout: fixed; width: 100%; min-width: 1080px; }
.li-table th, .li-table td { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.li-table col-type, .li-table th:nth-child(3) { width: 158px; }   /* Corner Window */
.li-table th:nth-child(4)  { width: 78px; }    /* W01 */
.li-table th:nth-child(5)  { width: 148px; }   /* Master Bedroom */
.li-table th:nth-child(6)  { width: 118px; }   /* 3'-0" x 4'-5.5" */
.li-table th:nth-child(7)  { width: 74px; }    /* 1000 */
.li-table th:nth-child(8)  { width: 96px; }    /* $50,891.75 */
.li-table th:nth-child(9)  { width: 62px; }    /* 1.50 */
.li-table th:nth-child(10) { width: 100px; }   /* $50,891.75 */
.li-table th:nth-child(11) { width: 108px; }   /* $500,891.75 */
.li-table th:nth-child(12) { width: 116px; }   /* $600,000.49 */
/* Inputs fill their column and scroll inside it rather than pushing it wider. */
.li-table td input, .li-table td select { width: 100%; min-width: 0; max-width: 100%; }

/* Delete is the one button here that destroys work, so it looks like it. */
.btn.danger { background: #e03131; border-color: #e03131; color: #fff; }
.btn.danger:hover:not(:disabled) { background: #c92a2a; border-color: #c92a2a; }
.btn.danger:disabled { opacity: .45; cursor: default; }

/* ---- Add Item, in its own window ---- */
.li-dialog { border: 0; border-radius: 14px; padding: 0; width: min(900px, 92vw);
  box-shadow: 0 18px 50px rgba(16, 20, 28, .28); color: var(--ink); }
.li-dialog::backdrop { background: rgba(16, 20, 28, .45); }
.li-dialog-head { display: flex; align-items: center; justify-content: space-between;
  padding: 16px 20px; border-bottom: 1px solid var(--line); }
.li-dialog-head h2 { margin: 0; font-size: 18px; }
.li-dialog-body { padding: 18px 20px 22px; max-height: 72vh; overflow-y: auto; }
.li-dialog-actions { display: flex; gap: 10px; }

/* Terms is typed into, not just read: sized past its usual value so a longer
   one has room, and given a caret-visible focus state so it doesn't read as a
   label the way the Customer pill beside it does. */
.quote-facts input.fact { min-width: 190px; cursor: text; }
/* Terms is sized by its `size` attribute so it hugs its own text like the
   Vendor pill next to it -- a fixed width would centre a short line far from
   Vendor and read as a wider gap than the even 34px it actually is. */
.quote-facts input.terms-fact { min-width: 0; width: auto; }
.quote-facts input.fact:hover { border-color: #b6bdc7; }
.quote-facts input.fact:focus { background: #fff; border-color: var(--ink);
  outline: none; text-align: left; }

/* ---- Room between the sections ----------------------------------------
   Markup, Additional Markups / By Category, Items and Footer are four
   separate pieces of work stacked on one page. Without a gap they read as one
   continuous wall, and the fold headings stop looking like headings. */
#pricingForm > .pricing-row { margin-bottom: 34px; }
#pricingForm > details { margin-bottom: 34px; padding-top: 26px; border-top: 1px solid var(--line); }
/* Say the closing out loud, once, for every fold on this page. The browser's
   own "hide everything but the summary" rule loses to any author rule that
   sets display on a child -- .markup-row sets grid, and the Footer's body was
   left showing its whole textarea with the section shut. */
#pricingForm > details:not([open]) > *:not(summary) { display: none; }
#pricingForm > details:last-child { margin-bottom: 8px; }

/* ---- Footer: the last section, and a real editing surface ---- */
.footer-fold .fold-body label { display: block; font-size: 12px; font-weight: 700;
  color: #5b6472; margin: 4px 0 8px; }
.footer-fold textarea { width: 100%; min-height: 260px; padding: 12px 14px;
  border: 1px solid var(--line); border-radius: 10px; background: #fff;
  font: inherit; line-height: 1.55; resize: vertical; }
.footer-fold textarea:focus { border-color: var(--ink); outline: none; }

/* ---- The per-row expander needs a target worth aiming at ---- */
.li-table .exp-col { width: 38px; }
.li-expander { width: 26px; height: 26px; border-radius: 6px; }
.li-expander:hover { background: #eef2ff; }


/* The tick is a 13px box in a 37px row; top-aligned cells left it riding 7px
   above the pills beside it. Everything on an item's line shares one centre. */
.li-table tbody tr[data-li] > td { vertical-align: middle; }
/* The tick and the expander are one pair, so they sit against the boundary
   between their two cells rather than each floating in the middle of its own.
   Centred separately they read as two unrelated marks 16px apart -- wider
   apart than the gap to the Type pill they precede. */
.li-table .pick-col { text-align: right; padding-right: 3px; }
.li-table .exp-col { text-align: left; padding-left: 3px; }
.li-table .pick-col input { display: inline-block; vertical-align: middle; margin: 0; }
/* inline-FLEX, not inline-block: the button has to keep centring the mark
   inside itself, or the mark drops onto the text baseline and rides low. */
.li-expander { display: inline-flex; vertical-align: middle; margin: 0; }
/* Same size as the box beside it, so neither reads as the bigger control. */
.li-table .pick-col input { width: 15px; height: 15px; }

/* Vendor Chat's heading: the vendor's name with its two buttons beside it, no
   card around it -- the same bare block heading the quote page uses. */
.vendor-head { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 12px;
  margin: 4px 0 18px; }
.vendor-head .block-title { margin: 0; margin-right: 4px; }
.vendor-head .vendor-wechat { margin-left: 6px; }

/* A fold heading that carries an action on the right, like Items does. */
.fold-head-row { display: flex; align-items: center; justify-content: space-between; gap: 16px; }
.fold-head-actions { display: flex; align-items: center; gap: 12px; }

/* Send, preview, and a note about what actually goes out. */
.compose-actions { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px; }

/* ---- The job's money, in one list ---------------------------------------
   Red is money leaving. It is the only colour in the table on purpose: which
   way a line went is the thing you scan a ledger for. */
.ledger-block { margin: 0 0 28px; }
.ledger-head { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 12px;
  margin-bottom: 14px; }
.ledger-head .block-title { margin: 0; margin-right: 4px; }
.ledger td, .ledger th { padding: 9px 10px; }
.ledger .money-out { color: #e03131; font-weight: 600; }
.ledger-entry { display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  margin-top: 14px; }
/* Same fields as the quote itself -- it is the same job being typed into. */
.ledger-entry input, .ledger-entry select { background: #ebedf0;
  border: 1px solid var(--line); border-radius: 10px; padding: 10px 12px;
  color: var(--ink); }
.ledger-entry input:focus, .ledger-entry select:focus { background: #fff;
  border-color: var(--line); outline: none; }
.ledger-entry input::placeholder { color: #9aa0a6; }
.ledger-edit-row > td { background: var(--bg); padding: 14px 10px; }
.ledger-entry input[name="notes"] { flex: 1 1 220px; min-width: 0; }
.ledger-entry .unit .prefix { padding-left: 4px; }

/* A document on a page rather than as a bare file. */
.doc-view-head { display: flex; align-items: center; justify-content: space-between;
  flex-wrap: wrap; gap: 10px 16px; }
.doc-view-head h1 { margin: 0; font-size: 20px; }
.doc-view-actions { display: flex; gap: 10px; flex-wrap: wrap; }
.doc-frame { width: 100%; height: 78vh; min-height: 480px; border: 1px solid var(--line);
  border-radius: 10px; background: #fff; margin-top: 10px; }
.doc-fallback { display: flex; flex-direction: column; align-items: center;
  justify-content: center; height: 100%; gap: 10px; color: var(--muted); }

/* ---- The manufacturer's dashboard: what is waiting on them ---------------
   One row per thing to answer with the one action that answers it, and their
   three figures beside it. Factory cost only -- a supplier never sees our
   pricing, and this page is reachable by a link we hand out. */
.vendor-top { display: grid; grid-template-columns: minmax(0, 1fr) 230px; gap: 18px;
  align-items: start; margin-bottom: 22px; }
.feed-row { display: flex; align-items: center; justify-content: space-between; gap: 16px;
  background: #e6e8eb; border-radius: 10px; padding: 14px 16px; margin-bottom: 12px; }
.feed-row.empty { background: var(--band); }
.feed-title { font-weight: 700; font-size: 15px; display: flex; align-items: center; gap: 10px; }
.feed-detail { font-size: 14px; margin-top: 2px; }
.feed-actions { display: flex; align-items: center; gap: 8px; flex: 0 0 auto; }
/* One colour for every action on this page: the row says what it is, the
   button says do it. */
.feed-action { border-radius: 999px; padding: 8px 20px; white-space: nowrap;
  background: #16181d; border-color: #16181d; color: #fff; }
.feed-action:hover { background: #2b3038; border-color: #2b3038; }
/* Reading it is not doing it, so it doesn't look like the button that does. */
.feed-peek { border-radius: 999px; padding: 8px 16px; white-space: nowrap;
  background: none; border: 1px solid #16181d; color: #16181d; font-weight: 600; }
.feed-peek:hover { background: rgba(22, 24, 29, .06); color: #16181d; }
.feed-action.stop { background: #c92a2a; border-color: #c92a2a; }
.vendor-tiles { display: flex; flex-direction: column; gap: 12px; }
.vendor-tiles .stat { text-align: center; }
@media (max-width: 820px) { .vendor-top { grid-template-columns: minmax(0, 1fr); } }
/* Stop work is an instruction, not a status: it reads as one. */
.feed-row.cancelled { background: #ffe3e3; }
.feed-action.stop:hover { background: #a51111; border-color: #a51111; }

/* ---- Their RFQ table ---------------------------------------------------- */
.rfq-head { display: flex; align-items: center; flex-wrap: wrap; gap: 10px 14px;
  margin: 4px 0 14px; }
/* The counters are a set, so they are grouped. On a wide screen the group sits
   inline exactly where the loose buttons used to. */
.rfq-counts { display: flex; align-items: center; gap: 10px 14px; }
.rfq-head .block-title { margin: 0; margin-right: 4px; }
.rfq-head input[type="text"] { background: var(--band); border: 1px solid var(--line);
  border-radius: 999px; padding: 9px 16px; min-width: 220px; }
.rfq-head input[type="text"]:focus { background: #fff; border-color: var(--line); outline: none; }
.rfq-count { display: flex; flex-direction: column; align-items: center; gap: 2px;
  background: #fff; border: 1px solid var(--line); border-radius: 10px; padding: 6px 16px; }
.rfq-count .label { font-size: 10px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); font-weight: 700; }
.rfq-count .value { font-size: 18px; font-weight: 700; }
.rfq-table { width: 100%; min-width: 980px; }
/* Heading centred over what it labels: the columns are short values, and a
   left-aligned heading over a centred value reads as two columns. */
.rfq-table td, .rfq-table th { padding: 9px 8px; white-space: nowrap; text-align: center; }
.rfq-table td.right, .rfq-table th.right { text-align: center; }
.rfq-table .rfq-detail-row > td { text-align: left; }
.rfq-table tbody tr[data-rfq] > td { vertical-align: middle; }
.rfq-table .pick-col { width: 34px; text-align: right; padding-right: 3px; }
.rfq-table .exp-col { width: 34px; text-align: left; padding-left: 3px; }
.rfq-table .pick-col input { width: 15px; height: 15px; margin: 0; vertical-align: middle; }
.rfq-table input[type="date"] { background: var(--band); border: 1px solid var(--line);
  border-radius: 999px; padding: 7px 12px; }
/* Unread from us, on the button that opens the thread. */
.chat-badge { display: inline-block; min-width: 18px; height: 18px; line-height: 18px;
  border-radius: 999px; background: #e03131; color: #fff; font-size: 11px; font-weight: 700;
  text-align: center; margin-left: 6px; padding: 0 5px; }
/* Indented to start under the Status column rather than at the table's edge,
   so the panel reads as belonging to the row above it. The tick and expander
   columns are what it clears. */
.rfq-detail-row > td { background: var(--bg); padding: 18px 14px 22px 70px; }
.rfq-detail { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 240px); gap: 20px; }
.rfq-field { margin-bottom: 12px; }
.rfq-field > label { display: block; font-size: 11px; font-weight: 700; color: #5b6472;
  text-transform: uppercase; letter-spacing: .04em; margin-bottom: 5px; }
.rfq-field .readonly { background: var(--band); border-radius: 10px; padding: 10px 14px;
  white-space: normal; }
.rfq-upload { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.rfq-plans a { display: inline-block; margin-bottom: 3px; }
.chat-msg { border-radius: 10px; padding: 10px 12px; margin-bottom: 10px; max-width: 88%; }
.chat-msg.them { background: var(--band); }
.chat-msg.us { background: #e7f5ff; margin-left: auto; }
.chat-send { margin-top: 14px; }
.chat-send textarea { width: 100%; border: 1px solid var(--line); border-radius: 10px;
  padding: 10px 12px; font: inherit; }

/* ---- Their build screen -------------------------------------------------
   Same table as ours, with the columns a manufacturer works in: size and area
   in millimetres, derived from the feet and inches rather than retyped. */
.build-table th, .build-table td { text-align: center; }
.build-table .size-mm, .build-table .sqm-cell, .build-table .cbm-cell,
.build-table .total-cell { white-space: nowrap; font-variant-numeric: tabular-nums; }
.build-table input { text-align: center; }
.build-table input.pill-cell { background: var(--band); border-radius: 999px; }
.build-table input.num { max-width: 90px; }
.btn.dup { background: #7048e8; border-color: #7048e8; color: #fff; }
.btn.dup:hover { background: #5f3dc4; border-color: #5f3dc4; }
.build-side { display: flex; flex-direction: column; gap: 8px; }
.build-side .dims { display: flex; gap: 8px; }
.build-side .dims > div { text-align: center; }
.build-side .dims input { width: 60px; text-align: center; }
.build-side .dims label { font-size: 11px; font-weight: 700; color: #5b6472; }

/* ---- Add Item: one row, read the way the table reads --------------------
   The four dimension boxes sit together because they are one measurement,
   and nothing is wider than the value it holds. */
.li-dialog:has(.add-item) { width: min(1060px, 96vw); }
.add-row { display: flex; align-items: flex-end; gap: 8px; flex-wrap: wrap; }
.add-row.four { margin-top: 14px; gap: 12px; }
.add-row.four .f { flex: 1 1 150px; }
.add-row .f { display: flex; flex-direction: column; gap: 4px; }
.add-row .f > label { font-size: 11px; font-weight: 700; color: #5b6472; text-align: center;
  white-space: nowrap; }
.add-row .f input { background: #ebedf0; border: 1px solid var(--line);
  border-radius: 999px; padding: 8px 10px; text-align: center; font: inherit; width: 100%; }
/* Worked out, not typed. A bubble says "you can change this", so anything that
   can't be overridden doesn't wear one. */
.add-row .f .ro { padding: 8px 10px; text-align: center; width: 100%; }
.add-row .f input:focus { background: #fff; border-color: var(--line); outline: none; }
.add-row .f.name { flex: 1 1 190px; min-width: 150px; }
.add-row .f.loc  { flex: 0 1 150px; }
.add-row .f.code { flex: 0 0 74px; }
.add-row .f.tiny { flex: 0 0 54px; }
.add-row .f.small { flex: 0 0 68px; }
.add-row .f.price { flex: 0 0 104px; }
.add-row .f.total { flex: 0 0 108px; }
.add-row .f .ro { font-weight: 700; color: var(--ink); }
.add-row .f .unit { display: block; position: relative; }
.add-row .f .unit .prefix { position: absolute; left: 10px; top: 50%;
  transform: translateY(-50%); color: var(--muted); font-size: 12px; }
.add-row .f .unit input { padding-left: 18px; }
.add-size { margin: 8px 0 4px; }
.add-body { margin-top: 10px; }
.add-line { display: flex; align-items: center; gap: 10px; margin-bottom: 8px; }
/* Glass, Remark and the two drawings share one label column, so the left edge
   of the form is a single line. 118px is what the longest of them needs
   without wrapping. */
.add-line > label { flex: 0 0 118px; font-size: 12px; font-weight: 700; color: #5b6472;
  text-align: right; }
.add-line.stack { flex-direction: column; align-items: stretch; gap: 4px; }
.add-line.stack > label { flex: none; text-align: left; }
.add-line input { flex: 1 1 auto; min-width: 0; background: #ebedf0;
  border: 1px solid var(--line); border-radius: 10px; padding: 9px 12px; font: inherit; }
.add-line input:focus { background: #fff; border-color: var(--line); outline: none; }
.add-actions { display: flex; justify-content: flex-end; gap: 10px; }
/* Where a request stands, in their words. */
.badge.state-draft { background: #e9ecef; color: #495057; }
.badge.state-rfq-accepted { background: #d0ebff; color: #1864ab; }
.badge.state-completed { background: #d3f9d8; color: #2b8a3e; }
.badge.state-awarded { background: #fff3bf; color: #8a6d1f; }
.badge.state-po-received { background: #e5dbff; color: #5f3dc4; }
.badge.state-cancelled-void { background: #ffe3e3; color: #c92a2a; }
/* What the job costs beyond the items: quoted once for the shipment. */
.shipment-costs { display: flex; align-items: flex-end; flex-wrap: wrap; gap: 14px;
  margin: 18px 0; }
/* Worked out, so no bubble -- but on the same line as the figures that made
   them, at the end where a total belongs. */
.shipment-costs .f.derived { text-align: center; margin-left: 6px; min-width: 92px; }
.shipment-costs .f.derived .ro { font-size: 18px; font-weight: 700; padding: 9px 14px; }
/* The caption hangs below rather than sitting in the box, so the total lines
   up with the fields beside it instead of riding above them. */
.shipment-costs .f.derived { position: relative; }
.shipment-costs .f.derived .tiny-note { position: absolute; top: 100%; left: 0; right: 0;
  font-size: 11px; color: var(--muted); white-space: nowrap; }
.shipment-costs .f { display: flex; flex-direction: column; gap: 4px; }
/* Label centred over its value -- left-aligned labels above centred figures
   read as two columns that don't line up. */
.shipment-costs .f > label { font-size: 11px; font-weight: 700; color: #5b6472;
  text-align: center; display: block; }
.shipment-costs input { background: #ebedf0; border: 1px solid var(--line);
  border-radius: 999px; padding: 9px 12px; text-align: center; font: inherit;
  max-width: 118px; }
.shipment-costs input:focus { background: #fff; border-color: var(--line);
  outline: none; text-align: left; }
.shipment-costs .unit { display: block; position: relative; }
.shipment-costs .unit .prefix { position: absolute; left: 12px; top: 50%;
  transform: translateY(-50%); color: var(--muted); font-size: 12px; }
.shipment-costs .unit input { padding-left: 22px; }
.add-row .f.type { flex: 0 1 150px; }
.add-row .f select { background: #ebedf0; border: 1px solid var(--line); border-radius: 999px;
  padding: 8px 10px; text-align: center; font: inherit; width: 100%; }
.add-row .f select:focus { background: #fff; border-color: var(--line); outline: none; }
.add-row .f input[name="new_section"] { margin-top: 5px; }
.build-table select.pill-cell { background: var(--band); border: 1px solid var(--line);
  border-radius: 999px; padding: 7px 10px; text-align: center; }

/* Choose File in the same clothes as everything else, rather than whatever
   the browser ships. The name of the chosen file stays visible beside it. */
/* The button is the control; the box around it was a second one saying the
   same thing, and it squeezed the filename into an ellipsis. */
input[type="file"] { font: inherit; font-size: 13px; color: var(--muted);
  background: none; border: 0; padding: 0; width: 100%; }
input[type="file"]::file-selector-button {
  background: #4b5563; color: #fff; border: 0; border-radius: 999px;
  padding: 8px 18px; margin-right: 10px; font: inherit; font-weight: 600;
  cursor: pointer;
}
input[type="file"]::file-selector-button:hover { background: #374151; }
/* Drawings stacked down the left; the buttons ride the last of them. */
.add-foot { display: flex; align-items: flex-end; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; margin-top: 16px; }
.add-foot .drawings { display: flex; flex-direction: column; gap: 10px; }
/* Label beside the button, not over it -- the same shape as Glass and Remark
   above, so the left edge of the form reads as one column. */
.add-foot .drawings .f { display: flex; align-items: center; gap: 10px; }
.add-foot .drawings .f > label { flex: 0 0 118px; text-align: right;
  font-size: 12px; font-weight: 700; color: #5b6472; }
/* the grey pill rule for typed fields would otherwise put a box round the
   picker, which is a second control saying what the button already says */
.add-row .f input[type="file"], .add-foot input[type="file"] {
  background: none; border: 0; padding: 0; }
/* The dimensions row carries ten boxes; it needs air above it and a break
   between what is measured and what that comes to. */
.add-row.sizes { margin-top: 18px; gap: 10px; }
.add-row.sizes .add-gap { flex: 0 0 18px; }
.add-size { margin: 12px 0 4px; }

/* Centred text is easy to read and hard to type into -- the caret sits in the
   middle and the words push out both ways. So a centred field left-aligns
   while it has focus and settles back to centre once it's done. */
.add-row .f input:focus,
.add-foot input:focus,
.build-table input:focus,
.rfq-table input:focus,
.markup-block .form-grid input:focus,
.markup-fold .form-grid input:focus,
.quote-facts input.fact:focus,
.ledger-entry input:focus { text-align: left; }

/* ---- Their projects: a placed job, and what has passed either way ---- */
/* A select is as wide as its longest option plus whatever the browser reserves
   for its own arrow -- which was 23px of nothing. Same drawn chevron the rest
   of the app uses, so the pill ends where the words do. */
.prod-pick { background: var(--band); border: 1px solid var(--line); border-radius: 999px;
  padding: 6px 21px 6px 10px; font-weight: 600; font-size: 12px;
  appearance: none; -webkit-appearance: none; cursor: pointer; width: auto;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath d='M1 1l4 4 4-4' stroke='%236b7280' stroke-width='1.6' fill='none' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat; background-position: right 7px center; }
/* One colour per real status -- the earlier set named two that don't exist
   ("delivered", "shipped"), so Shipping and Completed came out plain grey. */
.prod-pick.status-awaiting-shop-drawings { background: #e7f5ff; color: #1864ab; }
.prod-pick.status-in-production { background: #ffe8cc; color: #b8560f; }
.prod-pick.status-shipping { background: #e5dbff; color: #5f3dc4; }
.prod-pick.status-completed { background: #d3f9d8; color: #2b8a3e; }
/* The journey after the factory floor, warming as it gets closer to home.
   Packed and Shipped are the factory's last two; Ocean, At Port and Delivered
   are ours and never appear as a choice on their side. */
.prod-pick.status-packed { background: #e5dbff; color: #5f3dc4; }
.prod-pick.status-shipped { background: #d0ebff; color: #1864ab; }
.prod-pick.status-ocean { background: #c5f6fa; color: #0b7285; }
.prod-pick.status-at-port { background: #e6fcf5; color: #087f5b; }
.prod-pick.status-delivered { background: #d3f9d8; color: #2b8a3e; }
.prod-pick.status-delayed, .prod-pick.status-problem { background: #ffe3e3; color: #c92a2a; }
.prod-pick.status-not-started { background: var(--band); color: var(--muted); }
.doc-count { display: inline-block; min-width: 18px; height: 18px; line-height: 18px;
  border-radius: 999px; background: var(--band); color: var(--ink); font-size: 11px;
  font-weight: 700; text-align: center; margin-left: 6px; padding: 0 5px; }
.docs-send { margin-bottom: 16px; }
.docs-send-row { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; }
.docs-send input[name="label"] { background: var(--band); border: 1px solid var(--line);
  border-radius: 999px; padding: 9px 16px; }
.docs-note { flex: 1 1 260px; min-width: 0; background: var(--band);
  border: 1px solid var(--line); border-radius: 999px; padding: 9px 16px; }
.docs-hint { margin: 8px 0 0; }
.docs-send input:focus { background: #fff; border-color: var(--line); outline: none; }
.docs-table { width: 100%; }
.docs-table td, .docs-table th { padding: 9px 10px; }
.doc-remove { background: none; border: 0; color: #e03131; font-size: 20px; line-height: 1;
  cursor: pointer; padding: 0 4px; }
.doc-remove:hover { color: #a51111; }
/* Eight columns, all of them short -- it fits without the RFQ table's floor,
   and a row that scrolls sideways is a row you read half of. */
.projects-table { min-width: 0; }
.projects-table td, .projects-table th { padding: 9px 5px; }
.projects-table .prod-pick { font-size: 11px; padding: 6px 19px 6px 9px; }
.projects-table input[type="date"] { padding: 7px 8px; max-width: 132px; }
.projects-table .prod-pick { max-width: 100%; }

/* Priority reads at a glance: red is urgent, then orange, then yellow.
   Covers both the words this app uses (Rush / High / Normal) and the ones
   Kim asked for (High / Medium / Low), so whichever is set gets its colour. */
.badge.priority-rush,
.badge.priority-high { background: #ffe3e3; color: #c92a2a; }
.badge.priority-medium { background: #ffe8cc; color: #b8560f; }
.badge.priority-low { background: #fff3bf; color: #8a6d1f; }
.badge.priority-normal { background: var(--band); color: var(--muted); }

/* The chosen status sits centred in its pill like the rest of the row. */
.prod-pick { text-align: center; text-align-last: center; }

/* One order, on its own page. */
.po-head { display: flex; align-items: flex-end; justify-content: space-between;
  flex-wrap: wrap; gap: 16px; margin-bottom: 22px; }
.po-facts { display: flex; gap: 26px; flex-wrap: wrap; }
.po-facts > div { display: flex; flex-direction: column; gap: 3px; }
.po-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.po-who { background: var(--band); border: 1px solid var(--line); border-radius: 999px;
  padding: 8px 16px; max-width: 150px; }
.po-who:focus { background: #fff; border-color: var(--line); outline: none; }
/* Opened from the feed: a moment of colour so it is obvious which row the
   button just took you to. */
.rfq-table tr.just-opened > td { background: #fff3bf; transition: background .4s ease; }
/* The four spec fields sit centred under the item they describe, now that the
   Save button has gone up to join Duplicate and Delete. */
.build-table .li-detail-row { justify-content: center; }

/* The build table's columns are its own -- it inherited widths meant for the
   quote's Items table, which left Type sprawling and squeezed the three
   buttons off the end. Sized to what each column actually holds, with the
   measurements packed together because they are read as a group. */
.build-table { min-width: 1080px; }
.build-table th:nth-child(1)  { width: 30px; }   /* tick */
.build-table th:nth-child(2)  { width: 30px; }   /* open */
.build-table th:nth-child(3)  { width: 124px; }  /* Type */
.build-table th:nth-child(4)  { width: 78px; }   /* Code */
.build-table th:nth-child(5)  { width: 132px; }  /* Location */
.build-table th:nth-child(6)  { width: 126px; }  /* Size(mm) */
.build-table th:nth-child(7)  { width: 58px; }   /* QTY */
.build-table th:nth-child(8)  { width: 58px; }   /* CBM */
.build-table th:nth-child(9)  { width: 54px; }   /* SQM */
.build-table th:nth-child(10) { width: 98px; }   /* Price/Unit */
.build-table th:nth-child(11) { width: 104px; }  /* Total */
.build-table th:nth-child(12) { width: 210px; }  /* Save / Duplicate / Delete */
.build-table .row-actions { text-align: center; }
.build-table .row-actions .btn { padding: 6px 12px; }
/* Every control on an item's line shares one centre. The middle-align rule
   written for the quote's Items table keys off tr[data-li]; these rows are
   tr[data-item], so it never reached them and they sat top-aligned. */
.build-table tbody tr[data-item] > td { vertical-align: middle; }
.build-table tbody tr[data-item] input,
.build-table tbody tr[data-item] select,
.build-table tbody tr[data-item] .btn,
.build-table tbody tr[data-item] .unit { vertical-align: middle; }
.build-table .pick-col input, .build-table .li-expander { vertical-align: middle; }
/* The open panel stays inside the row it belongs to: bounded, not sprawled
   across the whole table, with the plans column beside it rather than adrift
   at the far edge. */
.rfq-detail { max-width: 1000px; grid-template-columns: minmax(0, 1fr) minmax(0, 220px); }
.rfq-detail .readonly { max-width: 100%; }
.rfq-field .rfq-upload { flex-wrap: wrap; }
/* The counts are filters, so they look pressable and show when one is on. */
button.rfq-count { cursor: pointer; font: inherit; }
button.rfq-count:hover { border-color: #b6bdc7; }
button.rfq-count.is-on { border-color: var(--ink); background: var(--band); }
button.rfq-count.is-on .label { color: var(--ink); }
/* Sortable headings on the vendor tables. */
.rfq-table th.sortable-col { cursor: pointer; user-select: none; }
.rfq-table th.sortable-col:hover { color: var(--ink); }
.rfq-table .sort-arrow { font-size: 10px; color: var(--muted); }
/* A sentence, inheriting nowrap from the table it sits in. It fitted on a wide
   screen and ran off the edge on a narrow one, which is the sort of thing that
   only shows up when somebody looks at it on a phone. */
.rfq-either { margin-bottom: 4px; white-space: normal; }

/* A box you can type in says so: every editable field carries a border, not
   just a fill, so it is obvious where to click. Readouts stay bare -- they are
   not typed into, and that difference is the point. */
.add-row .f .ro, .shipment-costs .f.derived .ro { border: 0; }

/* The two ways of returning a quote are peers, so they wear the same button. */
.rfq-field .btn.small.secondary { background: #4b5563; border-color: #4b5563; color: #fff;
  border-radius: 999px; padding: 8px 18px; font-weight: 600; }
.rfq-field .btn.small.secondary:hover { background: #374151; border-color: #374151; color: #fff; }

/* The name on a feed row is the way into the job -- the order's own page once
   it is ordered, the request in the table below while it is still a quote. */
.feed-go { font: inherit; color: inherit; background: none; border: 0; padding: 0;
  cursor: pointer; text-align: left; text-decoration: none; }
.feed-go:hover, .feed-go:focus-visible { text-decoration: underline;
  text-underline-offset: 3px; }

/* ---- Our dashboard queue, in the manufacturer's shape --------------------
   Their page and ours are two views of the same conversation, so a row reads
   the same on both: the job's name is the way in, what happened sits under it,
   and the one action that answers it is on the right. What ours adds is whose
   job it is -- that is the part they don't have. */
.todo-feed .feed-row { align-items: flex-start; }
.todo-feed .feed-actions { align-items: center; }
.feed-meta { margin-top: 4px; }
/* Nobody has taken it. A line rather than a fill: the row still has to read as
   one of the same rows. */
.feed-row.todo-row.unowned { box-shadow: inset 3px 0 0 var(--warn); }
.todo-assign { border-radius: 999px; padding: 7px 12px; font-size: 13px;
  background: #fff; border: 1px solid var(--line); }
/* The queue is not in a card -- the rows are the panel, the same way the
   factory's feed sits straight on their page. */
.todo-panel { margin-bottom: 18px; }
.todo-panel .mfr-head { margin-bottom: 12px; }

/* ---- An order inside the conversation -----------------------------------
   Posted into the thread rather than attached to an email, so it reads as a
   record in the conversation and carries its own state. Both sides see the
   same card; the link on each goes to their own copy of the order. */
.po-card { margin-top: 8px; border: 1px solid var(--line); border-radius: 10px;
  background: #fff; padding: 10px 12px; }
.po-card-line { display: flex; flex-wrap: wrap; align-items: baseline; gap: 10px; }
.po-card-line + .po-card-line { margin-top: 4px; }

/* ---- Signing in as a supplier -------------------------------------------
   Its own narrow page. Nothing here belongs to the internal app, including
   the shape of it. */
.portal-narrow { max-width: 420px; }
.portal-form { display: flex; flex-direction: column; gap: 6px;
  background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
  padding: 18px; }
.portal-form input { border: 1px solid var(--line); border-radius: 10px;
  padding: 10px 12px; font: inherit; background: var(--band); }
.portal-form label { margin-top: 6px; }
.portal-form .btn { margin-top: 14px; }
/* Who is signed in, and the way out. Sits with the page title, not in a nav --
   there is no nav on this side. */
.portal-who { display: flex; align-items: center; gap: 10px; margin-top: 8px;
  font-size: 13px; }

/* ---- A rep sending a job, in the Add Item window --------------------------
   The plan rows keep the name beside the file it names, so a submission with
   four drawings arrives saying which is which. */
.add-row.plan-row { margin-top: 10px; align-items: center; }
.add-row .f.file { flex: 0 0 auto; }
.add-row .f.file > label { text-align: left; }
/* The heading belongs to the column, not to every row in it: repeated down a
   list of four plans it stops being a label and becomes noise. */
#planRows .plan-row:not(:first-child) .f > label { display: none; }

/* ---- Every file on the job, in one list ---------------------------------
   The upload row reads like the manufacturer's Add Item: name it, choose it,
   send it. */
.doc-add { margin-bottom: 14px; }
.doc-add-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.doc-add input[name="label"] { flex: 0 1 200px; border: 1px solid var(--line);
  border-radius: 999px; padding: 9px 14px; font: inherit; background: var(--band); }
/* inputs are full-width by default here, which pushed the picker and the
   button onto lines of their own */
.doc-add-row input[type="file"] { width: auto; flex: 0 1 auto; }
.doc-add-row .btn { flex: 0 0 auto; }
.doc-note { display: block; width: 100%; margin-top: 10px; border: 1px solid var(--line);
  border-radius: 999px; padding: 9px 14px; font: inherit; background: var(--band); }
.doc-tools { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 16px 0 10px; }
.doc-tools input { flex: 0 1 260px; border: 1px solid var(--line); border-radius: 999px;
  padding: 9px 14px; font: inherit; background: var(--band); }
.doc-table th.tick, .doc-table td.tick { width: 28px; }
.doc-to { border: 1px solid var(--line); border-radius: 999px; padding: 8px 12px;
  font: inherit; font-size: 13px; background: #fff; }
/* A row this recipient may not have. Greyed rather than hidden: knowing it
   exists and can't go is worth more than a shorter list. */
.doc-table tr.is-blocked { opacity: .5; }
.doc-table tr.is-blocked a { pointer-events: auto; }

/* ---- The signature that goes on a contract ------------------------------ */
.sig-row { display: flex; align-items: flex-end; gap: 14px; flex-wrap: wrap; }
.sig-row input[type="file"] { width: auto; }
.sig-preview { margin-top: 14px; display: flex; align-items: center; gap: 14px; }
.sig-preview img { max-height: 60px; max-width: 260px;
  border: 1px solid var(--line); border-radius: 8px; padding: 6px; background: #fff; }

/* ---- A feed row on a phone ----------------------------------------------
   Side by side, the buttons keep their width and squeeze the title into a
   four-line column -- "Losavio / Project — / from Kim". On a narrow screen the
   action goes under what it acts on, full width, which is also where a thumb
   expects it. */
@media (max-width: 640px) {
  .feed-row { flex-direction: column; align-items: stretch; gap: 12px; }
  .feed-actions { width: 100%; }
  .feed-actions form.inline, .feed-actions form { flex: 1 1 auto; }
  .feed-actions .btn { width: 100%; justify-content: center; text-align: center; }
  /* our own queue rows carry a select and a Done -- they share the line */
  .todo-feed .feed-actions { flex-wrap: wrap; }
  .todo-feed .feed-actions .todo-assign { flex: 1 1 140px; width: auto; }
}

/* ---- The RFQ and Projects tables, as cards on a phone -------------------
   Nine columns on a 375px screen is a table nobody reads. Same markup, laid
   out differently: each row becomes a card, each cell a labelled line, and the
   cells that are buttons keep to themselves at the foot of it. The labels come
   from data-label on each cell, so there is still one table to keep right. */
@media (max-width: 640px) {
  .rfq-table thead { display: none; }   /* the labels are on the cells now */
  .rfq-table, .rfq-table tbody, .rfq-table tr, .rfq-table td { display: block; width: auto; }
  /* ...but an author display rule beats the browser's own hide rule, so a
     closed detail panel was opening itself on a phone. Say it explicitly. */
  .rfq-table tr[hidden], .rfq-table td[hidden] { display: none; }
  .rfq-table tr {
    background: var(--panel); border: 1px solid var(--line); border-radius: 12px;
    padding: 12px 14px; margin-bottom: 12px;
  }
  .rfq-table td {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    border: 0; padding: 5px 0; text-align: left;
  }
  /* a card is narrow, so text wraps in it -- except the cells that are a date
     or a number, where a break mid-value reads as two values */
  .rfq-table td:not(.nowrap) { white-space: normal; }
  /* One size, one weight, one colour down the whole card. The label and the
     value are the same kind of thing to read -- making the label small and
     shouty and the value big turns a list of facts into a poster. */
  .rfq-table td, .rfq-table td[data-label]::before, .rfq-table td .btn,
  .rfq-table td input, .rfq-table td select, .rfq-table td .badge {
    font-size: 14px; font-weight: 500; font-family: inherit;
  }
  .rfq-table td[data-label]::before {
    content: attr(data-label);
    color: var(--muted); flex: 0 0 auto; letter-spacing: 0; text-transform: none;
  }
  .rfq-table td.cell-title::before { display: none; }
  /* a cell with no label is a control: it gets the full width and no gutter */
  .rfq-table td:not([data-label]) { justify-content: flex-start; }
  .rfq-table td .btn { flex: 1 1 auto; justify-content: center; }
  .rfq-table td input[type="date"] { flex: 0 1 auto; }
  /* the tick and the expander belong together at the top of the card */
  .rfq-table td.pick-col, .rfq-table td.exp-col {
    display: inline-flex; width: auto; padding-right: 10px;
  }
  /* the detail row a card expands into is already a block; let it fill */
  .rfq-table tr[data-detail-rfq] { padding: 0; border: 0; background: none; }
  /* it scrolls as a page now, not as a table */
  .rfq-table { min-width: 0; }

  /* The panel a card opens into is two columns on a desk and one on a phone;
     the deep left indent was making room for the expander, which is now at the
     top of the card instead. */
  /* the detail cell is a panel, not one of the labelled lines above it */
  .rfq-detail-row > td { display: block; padding: 14px; width: auto; }
  .rfq-detail { grid-template-columns: minmax(0, 1fr); gap: 14px;
    max-width: 100%; width: 100%; }
  .rfq-detail > * { min-width: 0; }
  .rfq-field .readonly { max-width: 100%; word-break: break-word; }
  /* the panel matches the card it opened out of: same size, same weight, the
     label grey and the value in ink */
  .rfq-field > label, .rfq-field .readonly, .rfq-field .small,
  .rfq-field .btn, .rfq-field input, .rfq-either {
    font-size: 14px; font-weight: 500; letter-spacing: 0; text-transform: none;
  }
  .rfq-field > label { color: var(--muted); margin-bottom: 2px; }
  .rfq-field .readonly { padding: 4px 0; background: none; }
}

/* ---- The vendor page's furniture, on a phone -----------------------------
   Three things that read fine on a desk and badly on a 375px screen. */
@media (max-width: 640px) {

  /* 1. The counters wrapped into a ragged two-by-two with Show completed.
        One row that scrolls sideways instead: they are a set to skim, and a
        set reads better in a line than in a grid that changes shape as the
        numbers change. */
  .rfq-head { flex-wrap: wrap; gap: 8px; }
  .rfq-head > h2, .rfq-head > .block-title { flex: 0 0 auto; }
  /* the search takes its own line: sharing one with the counters left them
     all scrolled off the right-hand edge, which is the same as not having
     them */
  .rfq-head input[type="text"] { flex: 1 0 100%; width: 100%; order: -1; }
  /* the counters share the line under it, sized to fit three across rather
     than wrapping into a ragged grid that changes shape as the numbers do */
  /* the group takes a line of its own, three across, rather than squeezing in
     beside the title where the labels collided with each other */
  .rfq-counts { flex: 1 0 100%; gap: 8px; }
  .rfq-head > .btn { flex: 1 1 auto; }
  .rfq-head .rfq-count { flex: 1 1 0; min-width: 0; padding: 8px 4px; }
  .rfq-head .rfq-count .label { font-size: 9px; letter-spacing: .02em; }
  .rfq-head .rfq-count .value { font-size: 15px; }
  .rfq-head .btn { flex: 0 0 auto; }

  /* 2. Status is the one thing they change from the factory floor, and as a
        badge-shaped select it did not look like anything you could press.
        Full width, a proper target, and an arrow you can see. */
  .prod-pick { width: 100%; padding: 10px 30px 10px 14px; font-size: 14px;
    font-weight: 500; text-align: left; text-align-last: left;
    background-position: right 12px center; }
  .rfq-table td[data-label] .prod-pick { flex: 1 1 auto; }

  /* 3. The three figures took a whole screen before anything you could act on.
        Side by side above the feed, small -- they are context, not the job. */
  .vendor-tiles { flex-direction: row; gap: 8px; }
  .vendor-tiles .stat { flex: 1 1 0; min-width: 0; padding: 10px 6px; }
  .vendor-tiles .stat .label { font-size: 9px; letter-spacing: .02em; }
  .vendor-tiles .stat .value { font-size: 15px; }
  .vendor-tiles .stat .small { display: none; }   /* the caption is the first thing to go */
}

/* ---- Sending a photo or a file from a phone ------------------------------
   A supplier standing at a machine has one thumb and a camera. The picker is
   the whole point of the screen, so it stops being a small grey pill beside
   some grey text and becomes the target. */
@media (max-width: 640px) {
  input[type="file"] { display: block; width: 100%; }
  input[type="file"]::file-selector-button {
    display: block; width: 100%; margin: 0 0 8px; padding: 14px 18px;
    font-size: 15px; text-align: center;
  }

  /* Removing a file wants a thumb, not a cursor. */
  .doc-remove { padding: 10px 14px; font-size: 24px; }

  /* The dialog is nearly the screen, so it uses it. */
  .li-dialog { width: min(96vw, 560px); }
  .li-dialog-body { padding: 14px 16px 18px; max-height: 78vh; }
  .li-dialog-head { padding: 14px 16px; }
  .li-dialog-head h2 { font-size: 16px; }

  /* The file list: the date and who sent it are captions, the name is the row. */
  .docs-table td, .docs-table th { padding: 8px 6px; }
  .docs-send-row { display: flex; flex-direction: column; gap: 8px; }
  /* flex: 0 0 auto, or the note inherits a grow from the row it used to sit
     in and stretches down the dialog -- and a 999px radius on a tall box is a
     circle */
  .docs-send-row > * { width: 100%; flex: 0 0 auto; height: auto; }
  .docs-hint { margin-top: 2px; }
}
/* The dashboard heading carries the notifications control beside it. */
.dash-head { display: flex; align-items: center; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; }
/* The date and its buttons on one line, in the panel a row opens into. */
.rfq-accept { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.rfq-accept input[type="date"] { width: auto; }

/* Accept Quote, once there is a date to accept with. Green because it is the
   go-ahead, and because the row gives no other clue that the date box halfway
   along it belongs to this button. */
.accept-btn.is-armed { background: #1a7f37; border-color: #1a7f37; }
.accept-btn.is-armed:hover { background: #14682c; border-color: #14682c; }
.cc-quick { display: flex; gap: 6px; margin-top: 6px; flex-wrap: wrap; }
/* A count beside a dashboard tab: how much is in the list you are not looking at. */
.quote-tabs .tab-count { display: inline-block; margin-left: 6px; padding: 1px 7px;
  border-radius: 999px; background: var(--band); border: 1px solid var(--line);
  font-size: 11px; font-weight: 700; color: var(--muted); }
.quote-tabs .tab.is-active .tab-count { background: #e7f0ff; border-color: #cfe0ff;
  color: #1d4ed8; }

/* ---- The freight chain ---------------------------------------------------
   Five steps from the factory floor to a driveway in Los Angeles. Read as a
   row on a desk and as a stack on a phone, because "where is my order" is
   asked far more often from a phone than from a desk. */
.ship-chain {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
  gap: 10px;
  margin-bottom: 14px;
}
.ship-step {
  display: flex;
  gap: 8px;
  padding: 10px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
}
/* Done is quiet, next is the one you are here for, waiting is barely there.
   Colour carries the state and the tick repeats it, so it still reads without
   the colour. */
.ship-step.is-done { opacity: .75; }
.ship-step.is-next { border-color: var(--accent); background: var(--card); }
.ship-step.is-waiting { opacity: .55; }
.ship-step.is-late { border-color: #b42318; }

.ship-mark {
  width: 18px; height: 18px;
  flex: 0 0 18px;
  border-radius: 50%;
  border: 1px solid var(--border);
  font-size: 11px;
  line-height: 16px;
  text-align: center;
  margin-top: 2px;
}
.ship-step.is-done .ship-mark { background: #1a7f37; border-color: #1a7f37; color: #fff; }
.ship-step.is-next .ship-mark { border-color: var(--accent); border-width: 2px; }
.ship-step.is-late .ship-mark { border-color: #b42318; border-width: 2px; }

.ship-body { flex: 1 1 auto; min-width: 0; }
/* A step's box is narrow by design -- five of them across. Anything in it
   that will not fit has to wrap rather than run into the step beside it,
   which is what "due 08/22/2026, not reported yet" was doing. */
.ship-body > * { overflow-wrap: anywhere; }
.ship-label { font-size: 13px; font-weight: 600; margin-bottom: 6px; }

.ship-date { display: block; margin-bottom: 6px; }
.ship-date > span {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  margin-bottom: 2px;
}
.ship-date input, .ship-date select {
  width: 100%;
  padding: 5px 7px;
  font-size: 13px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  color: var(--text);
}

.ship-foot {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: 10px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.ship-foot .ship-date { margin-bottom: 0; min-width: 190px; }

/* An RFQ that has been asked more than once. A quiet count, not a badge --
   it is a fact about the row, not a state to act on. */
.rfq-again {
  display: inline-block;
  margin-left: 6px;
  padding: 1px 6px;
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 11px;
  color: var(--muted);
  white-space: nowrap;
}
.rfq-earlier {
  margin-top: 12px;
  padding-top: 10px;
  border-top: 1px solid var(--border);
}
.rfq-earlier-row {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 8px;
  padding: 5px 0;
}

@media (max-width: 640px) {
  .ship-chain { grid-template-columns: 1fr; }
  .ship-foot .ship-date { min-width: 0; width: 100%; }
}

/* Five milestones plus the job's own columns is wider than a laptop on a
   split screen. The table scrolls inside its card rather than taking the
   whole page sideways with it. */
.card:has(> .rfq-table) { overflow-x: auto; }

/* The one control on an Orders row. Sized to the longest stage so the column
   does not jump width when a job moves along. */
.stage-pick {
  padding: 4px 6px;
  font-size: 12px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  color: var(--text);
  max-width: 170px;
}
.stage-form { max-width: 320px; }


/* Who is carrying it. Two companies side by side on a desk, stacked on a
   phone, because they are read together -- "who has it and how do I reach
   them" is one question, not two. */
.mover-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
  gap: 14px;
  margin-bottom: 14px;
}
.mover {
  padding: 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--bg);
}
.mover-head {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
  margin-bottom: 8px;
}
/* Their details are a fact, not a field: shown, never retyped per job. */
.mover-contact {
  margin: 0 0 8px;
  padding: 8px 10px;
  border-left: 2px solid var(--accent);
  background: var(--card);
  border-radius: 0 6px 6px 0;
}
.mover-contact > div { margin-bottom: 2px; }
.mover-contact .prewrap { white-space: pre-wrap; }

.mover .ship-date textarea,
.mover-add textarea {
  width: 100%;
  padding: 5px 7px;
  font-size: 13px;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  color: var(--text);
  font-family: inherit;
  resize: vertical;
}
.mover-add { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
.mover-add summary { cursor: pointer; color: var(--accent); }
.mover-add-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
  gap: 10px;
  margin: 10px 0;
}

@media (max-width: 640px) {
  .mover-grid, .mover-add-grid { grid-template-columns: 1fr; }
}

/* Who entered a thing, said once beside its heading. The forwarder comes from
   the factory and the delivery is ours, and knowing which is which is the
   difference between chasing it and waiting for it. */
.mover-source {
  margin-left: 6px;
  padding: 1px 6px;
  border: 1px solid var(--border);
  border-radius: 10px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0;
  text-transform: none;
  color: var(--muted);
}
.mover-name { font-size: 15px; font-weight: 600; margin-bottom: 6px; }
.mover-fact { font-size: 13px; margin-top: 6px; }
.mover-fact > span {
  display: block;
  font-size: 11px;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--muted);
}

/* The factory's side of it: their order page, where they name the forwarder.
   Rides on .shipment-costs rather than inventing a second look -- it is the
   same kind of form, three inches down the same page, and two sets of input
   styling on one screen reads as two different apps. What it adds is width:
   118px suits a date and a CBM figure and not an email address. */
.fwd-form { margin-top: 6px; }
.fwd-form select {
  background: #ebedf0;
  border: 1px solid var(--line);
  border-radius: 999px;
  padding: 9px 12px;
  font: inherit;
  text-align: center;
  max-width: 100%;
}
.fwd-form select:focus { background: #fff; border-color: var(--line); outline: none; }
.fwd-form input { max-width: none; width: 100%; }
.fwd-form .f { min-width: 0; }
/* Sized to their contents, not stretched to fill: two fields sharing a row
   at half the page each read as a form, while the dates three inches above
   read as facts. They are the same kind of thing and should look it. */
.fwd-form .f.wide  { flex: 0 1 230px; }
.fwd-form .f.mid   { flex: 0 1 165px; }
.fwd-form .f.small { flex: 0 1 120px; }

/* display: contents lets the boxes inside sit in the form's own flex row.
   It also beats [hidden], which is how a picked forwarder still showed the
   fields for adding a new one -- an author display rule wins, so hiding has
   to be said again louder. */
.fwd-new { display: contents; }
.fwd-new[hidden] { display: none; }

/* The note is a sentence, not a figure: full width and left aligned, so it
   does not sit centred in a pill pretending to be a number. */
.fwd-form .fwd-note { flex: 1 1 100%; }
.fwd-form textarea {
  width: 100%;
  padding: 9px 12px;
  font: inherit;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: #ebedf0;
  color: var(--text);
  resize: vertical;
}
.fwd-form textarea:focus { background: #fff; outline: none; }

@media (max-width: 640px) {
  .fwd-form { flex-direction: column; align-items: stretch; }
  .fwd-new[hidden] { display: none; }
}

/* The shipment, folded. Wears the same heading and box-with-a-plus as every
   other foldable thing, so a supplier who has opened one has opened them all.
   Shut by default: most visits to an order are to read what it covers or to
   send a file, and the shipment is what you open when you have news. */
.po-fold {
  margin: 22px 0;
  padding: 14px 16px;
  border: 1px solid var(--line);
  border-radius: 10px;
  background: var(--card);
}
.po-fold > summary {
  list-style: none;
  cursor: pointer;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 6px 14px;
}
.po-fold > summary::-webkit-details-marker { display: none; }
/* A closed <details> hides its content through a browser rule that any author
   display wins against -- and .shipment-costs inside is display:flex, so the
   whole shipment stayed on screen with the fold reading as shut. Same shape
   of bug as display:contents beating [hidden]: say the hiding again, louder. */
.po-fold:not([open]) > *:not(summary) { display: none; }
/* What a shut fold still tells you: where the job is, when it leaves, who has
   it. Enough that opening it is a choice rather than the only way to look. */
.po-fold .fold-summary { color: var(--muted); }
.po-fold[open] > summary { margin-bottom: 4px; }
.po-fold[open] .fold-summary { display: none; }
.po-fold .fold-sub {
  margin: 20px 0 0;
  font-size: 14px;
  font-weight: 700;
  color: var(--ink);
}
.po-fold .shipment-costs { margin-top: 10px; }

@media (max-width: 640px) {
  .po-fold > summary { flex-direction: column; align-items: flex-start; gap: 4px; }
}

/* The stage list in Settings. Plain rows: this is a list you edit two or
   three times a year, not a screen you live in. */
.stage-table { width: 100%; margin-bottom: 14px; }
.stage-table .stage-ord { width: 90px; }
.stage-table input, .stage-table select {
  width: 100%;
  padding: 6px 8px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  color: var(--text);
}
.stage-table tr.is-retired { opacity: .55; }
.stage-retire {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 14px;
  padding-top: 12px;
  border-top: 1px solid var(--border);
}
.stage-add { display: flex; flex-wrap: wrap; gap: 12px; align-items: flex-end; }
.stage-add .ship-date { min-width: 190px; margin-bottom: 0; }

/* Whose a step is, said on the step itself. */
.ship-whose {
  font-size: 10px;
  font-weight: 600;
  color: var(--muted);
  border: 1px solid var(--border);
  border-radius: 10px;
  padding: 0 5px;
}
.ship-step.is-archived { opacity: .6; border-style: dashed; }

/* What has been said about a job, newest first. A log rather than a grid of
   forecast dates: a date for every step was a lot of boxes nobody filled in,
   and "rolled off the 8/12 vessel" is the thing anybody actually wants. */
.stage-log { margin-top: 16px; border-top: 1px solid var(--border); }
.stage-log-row {
  display: flex;
  gap: 12px;
  padding: 10px 0;
  border-bottom: 1px solid var(--border);
}
.stage-log-row:last-child { border-bottom: none; }
.stage-log-when { flex: 0 0 62px; padding-top: 1px; }
.stage-log-what { flex: 1 1 auto; min-width: 0; }
.stage-log-what .prewrap { white-space: pre-wrap; margin-top: 2px; }

.stage-form { max-width: 420px; }
.stage-form textarea {
  width: 100%;
  padding: 7px 9px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card);
  color: var(--text);
  resize: vertical;
}
.stage-form .btn { margin: 4px 0 8px; }

/* The supplier's status and note, on one row like everything else there. */
.prod-form { display: contents; }

/* Which round of asking this is. Quiet: a fact about the row, not a state to
   act on -- the one you act on is the button beside the older rounds. */
.rfq-rev {
  flex: 0 0 auto;
  font-size: 11px;
  font-weight: 700;
  color: var(--muted);
  min-width: 42px;
}
.rfq-note { align-self: center; }

/* Renaming a document in place. The pencil is quiet until you want it; the
   box that replaces the name is a real bordered field, because a box you can
   type in should look like one. */
.doc-rename-open {
  border: none;
  background: none;
  cursor: pointer;
  color: var(--muted);
  font-size: 12px;
  padding: 0 4px;
  line-height: 1;
}
.doc-rename-open:hover { color: var(--accent); }
.doc-rename { display: flex; gap: 6px; align-items: center; }
/* display:flex on the form beats [hidden] -- said again, louder. */
.doc-rename[hidden] { display: none; }
.doc-rename input[type="text"] {
  flex: 1 1 auto;
  min-width: 0;
  padding: 5px 8px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: var(--card);
  color: var(--text);
}

/* The RFQ screen's own controls: search, the completed toggle, and the three
   counts the screen is opened to answer. One row, so the eye lands on the
   numbers before the table. */
.rfq-tools {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 12px;
  margin-bottom: 14px;
}
.rfq-tools input[type="text"],
.rfq-tools input[type="search"] {
  flex: 0 1 280px;
  padding: 9px 12px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card);
  color: var(--text);
}
.rfq-toggle.is-on { background: #344054; color: #fff; border-color: #344054; }
.rfq-tiles { display: flex; gap: 10px; flex: 1 1 auto; justify-content: flex-end; }
.rfq-tile {
  min-width: 108px;
  padding: 8px 14px;
  text-align: center;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--card);
}
.rfq-tile .label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .05em;
  text-transform: uppercase;
  color: var(--muted);
}
.rfq-tile .value { font-size: 20px; font-weight: 700; }

/* The two conversations a job has, as buttons on the row. The badge is what
   they said that nobody here has read. */
.rfq-act { position: relative; white-space: nowrap; }
.rfq-act.is-off { opacity: .45; cursor: default; }
.act-badge {
  position: absolute;
  top: -7px; left: -7px;
  min-width: 17px;
  height: 17px;
  padding: 0 4px;
  border-radius: 9px;
  background: #d92d20;
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 17px;
  text-align: center;
}

/* The fold: what you would otherwise open a page to read, with the plans and
   the two things you act on beside it. */
.rfq-detail { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 22px; }
.rfq-fields { display: block; }
.rfq-field { margin-bottom: 12px; }
.rfq-field > label {
  display: block;
  font-size: 11px;
  font-weight: 700;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: 4px;
}
.rfq-field input[type="text"],
.rfq-field input[type="date"],
.rfq-field textarea {
  width: 100%;
  padding: 8px 10px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card);
  color: var(--text);
}
.rfq-field input[readonly] { background: var(--bg); color: var(--muted); }
.rfq-field textarea { resize: vertical; }
.rfq-pair { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
.rfq-pair input[type="text"] { flex: 1 1 160px; width: auto; }
.plan-list { display: flex; flex-direction: column; gap: 4px; }

@media (max-width: 900px) {
  .rfq-detail { grid-template-columns: 1fr; }
  .rfq-tiles { margin-left: 0; width: 100%; }
  .rfq-tile { flex: 1 1 0; min-width: 0; }
}

/* Email goes to two different people and which one depends on the question,
   so it asks instead of guessing. A <details> rather than a script: it opens
   on a phone, it closes on Escape, and it works with nothing loaded. */
.email-pick { position: relative; display: inline-block; }
.email-pick > summary { list-style: none; cursor: pointer; }
.email-pick > summary::-webkit-details-marker { display: none; }
.email-menu {
  position: absolute;
  z-index: 20;
  top: calc(100% + 4px);
  right: 0;
  min-width: 200px;
  padding: 6px;
  display: flex;
  flex-direction: column;
  gap: 2px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--card);
  box-shadow: 0 6px 20px rgba(16, 24, 40, .14);
  text-align: left;
  white-space: nowrap;
}
.email-menu > a, .email-menu > span {
  padding: 7px 9px;
  border-radius: 6px;
  font-size: 13px;
}
.email-menu > a:hover { background: var(--bg); text-decoration: none; }

/* The rounds a job has already had, under the button that adds another --
   which is the question that button raises. */
.rfq-rounds { margin-top: 10px; padding-top: 8px; border-top: 1px solid var(--border); }
.email-pick .dots { opacity: .6; letter-spacing: 1px; }

/* Accept beside the date, not under it. The field rule above stretches every
   input to the full width, which pushed the button onto its own line and made
   two steps out of one. */
.rfq-accept { display: flex; align-items: center; gap: 8px; }
.rfq-accept input[type="date"] { flex: 1 1 auto; width: auto; min-width: 0; }
.rfq-accept .btn { flex: 0 0 auto; }

/* Writing down what was said, from the row that made you ask. A <details> so
   it costs nothing until you want it, and stays out of the way of the queue. */
.note-pick { position: relative; }
.note-pick > summary { list-style: none; cursor: pointer; }
.note-pick > summary::-webkit-details-marker { display: none; }
.note-form {
  position: absolute;
  z-index: 30;
  top: calc(100% + 6px);
  right: 0;
  width: 320px;
  max-width: 78vw;
  padding: 12px;
  display: flex;
  flex-direction: column;
  gap: 8px;
  text-align: left;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--panel);
  box-shadow: 0 8px 24px rgba(16, 24, 40, .16);
}
.note-kinds { display: flex; gap: 12px; font-size: 13px; }
.note-kinds label { display: flex; align-items: center; gap: 4px; font-weight: 500; }
.note-kinds input { width: auto; margin: 0; }
.note-form input[type="text"], .note-form textarea {
  width: 100%;
  padding: 7px 9px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--panel);
  color: var(--text);
  resize: vertical;
}

/* Notes on a job, read back. Kept plain: this is a record, not a conversation
   -- nobody replies to it, they just need to find what was said. */
.job-notes { margin-bottom: 14px; }
.job-note { padding: 10px 0; border-bottom: 1px solid var(--border); }
.job-note:last-child { border-bottom: none; }
.job-note .prewrap { white-space: pre-wrap; margin-top: 3px; }
.note-inline { padding-top: 12px; border-top: 1px solid var(--border); }
.note-inline .note-kinds { margin-bottom: 8px; flex-wrap: wrap; align-items: center; }
.note-inline input[type="text"], .note-inline textarea {
  padding: 7px 9px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--panel);
  color: var(--text);
}
.note-inline textarea { width: 100%; margin-bottom: 8px; resize: vertical; }
.badge.Client { background: #e7f5ff; color: #1864ab; }
.badge.Rep { background: #fff4e6; color: #b8560f; }
.badge.Team { background: #f1f3f5; color: #495057; }

/* The compose window: the Email tab's fields, in a dialog, because the act is
   the same wherever you started it. */
.compose { display: flex; flex-direction: column; gap: 12px; }
.compose > label, .compose-row > label {
  display: block;
  font-size: 12px;
  font-weight: 700;
  color: var(--muted);
}
.compose-row { display: grid; grid-template-columns: 1fr 1fr; gap: 12px; }
.compose input, .compose select, .compose textarea {
  width: 100%;
  margin-top: 4px;
  padding: 9px 11px;
  font: inherit;
  font-weight: 400;
  color: var(--text);
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel);
}
.compose textarea { resize: vertical; }
.compose-foot { display: flex; align-items: center; gap: 12px; }
.email-menu > button.email-go {
  border: none;
  background: none;
  font: inherit;
  text-align: left;
  cursor: pointer;
  color: var(--accent-2);
  padding: 7px 9px;
  border-radius: 6px;
}
.email-menu > button.email-go:hover { background: var(--bg); }
@media (max-width: 640px) { .compose-row { grid-template-columns: 1fr; } }

/* Ask or note: two buttons because they are two different acts, said plainly
   under them rather than left to be guessed at. */
.team-send { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 8px; }
.prod-pick.status-hold { background: #fff4e6; color: #b8560f; }
.prod-pick.status-sent { background: #e7f5ff; color: #1864ab; }
.prod-pick.status-won { background: #d3f9d8; color: #2b8a3e; }
.prod-pick.status-lost { background: #ffe3e3; color: #c92a2a; }
.prod-pick.status-draft { background: #f1f3f5; color: #495057; }

/* What a dialog says back when you post from inside it. */
.dialog-said { margin-top: 8px; }
.dialog-said.good { color: var(--good); }
.dialog-said.bad { color: var(--bad); }

/* The job request under its estimate. The line is always rendered, empty or
   not, so the numbers stay level down the column whether or not a given job
   has a request behind it -- which is what made the rows ragged before. */
.est-cell { white-space: nowrap; }
.est-cell .est-job {
  display: block;
  min-height: 17px;
  font-size: 12px;
  color: var(--muted);
}
.est-cell .est-job a { color: var(--muted); }
.est-cell .est-job a:hover { color: var(--accent-2); }

/* One window, three conversations. They are genuinely different -- the
   factory can read theirs, the notes are ours, the email went outside -- so
   they are sections rather than one merged stream. */
.comms-tabs {
  display: flex;
  gap: 4px;
  margin-bottom: 14px;
  border-bottom: 1px solid var(--border);
}
.comms-tab {
  border: none;
  background: none;
  font: inherit;
  font-weight: 600;
  color: var(--muted);
  padding: 8px 14px;
  cursor: pointer;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.comms-tab.is-active { color: var(--text); border-bottom-color: var(--accent-2); }
.comms-sec { display: none; }
.comms-sec.is-active { display: block; }
.comms-mail-to { display: flex; flex-wrap: wrap; gap: 8px; margin-top: 14px; }

/* The "who you spoke to" box, styled like every other box in these windows
   rather than falling back to the browser's own. */
.note-kinds .note-who {
  flex: 1 1 180px;
  min-width: 0;
  padding: 7px 9px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--panel);
  color: var(--text);
}

/* A count on a tab sits beside its label rather than over the corner -- there
   is no corner to hang off, and it has to read as part of the word. */
.act-badge.inline {
  position: static;
  display: inline-block;
  margin-left: 6px;
  vertical-align: 1px;
}

/* A sent email, openable. The list answers "did we tell them"; opening one
   answers "what did we say", which is the half that was missing. */
.sent-mail { border-bottom: 1px solid var(--border); }
.sent-mail:last-of-type { border-bottom: none; }
.sent-mail > summary {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 10px;
  padding: 9px 2px;
  cursor: pointer;
  list-style: none;
}
.sent-mail > summary::-webkit-details-marker { display: none; }
.sent-mail > summary::before {
  content: "▸";
  color: var(--muted);
  font-size: 11px;
}
.sent-mail[open] > summary::before { content: "▾"; }
.sent-subject { font-weight: 600; flex: 1 1 200px; min-width: 0; }
.sent-body {
  padding: 4px 2px 14px 18px;
  border-left: 2px solid var(--border);
  margin-left: 4px;
}
.sent-body .prewrap { white-space: pre-wrap; margin-top: 6px; }

/* A tile you can click. It says what it counted, and clicking it shows you
   exactly that -- so it has to look pressable, and look pressed. */
a.rfq-tile { display: block; color: inherit; text-decoration: none; }
a.rfq-tile:hover { border-color: var(--accent-2); text-decoration: none; }
.rfq-tile.is-on {
  border-color: var(--accent-2);
  border-width: 2px;
  padding: 7px 13px;
  background: #f4f6ff;
}
.ord-date { padding: 6px 8px; font: inherit; border: 1px solid var(--border);
  border-radius: 7px; background: var(--panel); color: var(--text); }
.ord-pm {
  padding: 6px 8px;
  font: inherit;
  border: 1px solid var(--border);
  border-radius: 7px;
  background: var(--panel);
  color: var(--text);
  max-width: 150px;
  text-align: center;
  text-align-last: center;
}
.rfq-table td.center { text-align: center; }
.rfq-table td.center .inline { display: inline-block; }

/* A money tile opens the list it counted, so it has to read as pressable --
   the same as the counts on the Tracker. */
a.stat { color: inherit; text-decoration: none; display: block; }
a.stat:hover { border-color: var(--accent-2); text-decoration: none; }
a.stat:hover .label { color: var(--accent-2); }

/* A reply reads differently from something we sent: it is the other side of
   the conversation, and the eye should find it without reading the address. */
.sent-mail.is-reply > summary { background: #f4f8ff; border-radius: 6px; }
.sent-mail.is-reply .sent-subject { font-weight: 700; }

/* What is still waiting on this job, and the one control that clears it. */
.comms-read {
  display: flex;
  align-items: center;
  gap: 10px;
  margin-bottom: 12px;
  padding: 8px 12px;
  border: 1px solid #ffd7d7;
  border-radius: 8px;
  background: #fff5f5;
  color: #c92a2a;
}

/* A name in a note reads as a name, not as punctuation. */
.at {
  font-weight: 600;
  color: var(--accent, #1f6feb);
}
.at-hint { color: var(--muted); font-size: 11px; margin-top: 4px; }

/* Settings sits level with the brand line, at the far end of it. */
.portal-headline {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 6px;
}
.portal-headline .portal-brand { margin-bottom: 0; }
.portal-settings {
  font-size: 13px;
  font-weight: 600;
  color: var(--muted);
  text-decoration: none;
  white-space: nowrap;
}
.portal-settings:hover { color: var(--ink); text-decoration: underline; }

/* The accounts a rep used to be paid into. Shut by default -- it matters only
   when you are checking, and an open list of them is noise the rest of the time. */
.bank-history { margin: 0 0 14px; }
.bank-history > summary { cursor: pointer; color: var(--muted); font-weight: 600; }
.bank-history table { margin-top: 8px; }

/* ---- The rep's three tabs ---------------------------------------------
   Same idea as the Tracker's: three questions, one on screen at a time. With
   no JavaScript every panel stays visible, which is a long page but a working
   one -- so the hiding is defined here, on the class the script sets. */
.rep-tabs {
  display: flex;
  gap: 6px;
  flex-wrap: wrap;
  margin: 6px 0 14px;
}
.rep-tab {
  border: 1px solid var(--border);
  background: var(--panel);
  color: var(--muted);
  border-radius: 999px;
  padding: 7px 16px;
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  cursor: pointer;
}
.rep-tab:hover { color: var(--ink); }
.rep-tab.is-active {
  background: var(--ink);
  border-color: var(--ink);
  color: #fff;
}
.rep-count {
  display: inline-block;
  margin-left: 4px;
  opacity: .7;
  font-weight: 500;
}
.rep-panel { display: none; }
.rep-panel.is-active { display: block; }
/* Before the script runs -- and if it never does -- show them all. */
.rep-panel:only-of-type { display: block; }

/* ---- One order, as a row that stacks ---- */
.ord-list { display: flex; flex-direction: column; gap: 2px; }
.ord-row {
  display: flex;
  gap: 16px;
  align-items: flex-start;
  padding: 14px 0;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}
.ord-row:first-child { border-top: 0; }
.ord-main { flex: 1 1 260px; min-width: 0; }
.ord-title { font-weight: 600; margin-bottom: 2px; }
.ord-sub { margin-bottom: 6px; }
.ord-facts { display: flex; flex-wrap: wrap; gap: 10px; align-items: center; }
.ord-money { flex: 0 0 auto; text-align: right; min-width: 120px; }
.ord-total { font-size: 15px; font-weight: 600; }
.ord-comm { color: var(--good, #1a7f4b); }
.ord-get { flex: 0 0 auto; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }

/* ---- The shelf ---- */
.res-list { display: flex; flex-direction: column; gap: 2px; }
.res-row {
  display: flex;
  gap: 16px;
  align-items: center;
  justify-content: space-between;
  padding: 12px 0;
  border-top: 1px solid var(--border);
  flex-wrap: wrap;
}
.res-row:first-child { border-top: 0; }
.res-text { flex: 1 1 240px; min-width: 0; }
.res-title { font-weight: 600; }
.res-blurb { color: var(--muted); margin-top: 2px; }

@media (max-width: 640px) {
  .ord-money { text-align: left; min-width: 0; }
  .ord-row, .res-row { gap: 8px; }
}

/* The rep's own side of the thread: a fold on the row, not a window over it.
   They are often on a phone, and one more thing that opens over the page is
   one more thing to get back out of. */
.rep-thread { margin-top: 8px; }
.rep-thread > summary {
  cursor: pointer;
  color: var(--muted);
  font-weight: 600;
  list-style: none;
}
.rep-thread > summary::-webkit-details-marker { display: none; }
.rep-thread > summary::before { content: "▸ "; }
.rep-thread[open] > summary::before { content: "▾ "; }
.rep-thread > summary:hover { color: var(--ink); }
.rep-thread-body {
  margin-top: 8px;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--panel);
}
.rep-thread-body .chat-msg { margin-bottom: 8px; }

/* A wide table scrolls inside its own box; the page itself must not. */
.table-scroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.rep-table { width: 100%; min-width: 720px; }
.rep-table td { vertical-align: top; }
.nowrap { white-space: nowrap; }

/* What a rep sent us, read back as pairs rather than a form. */
.job-read { display: flex; flex-direction: column; gap: 10px; margin-bottom: 16px; }
.job-read > div > span { display: block; }
