:root {
  --ink: #16191d;
  --muted: #6b7280;
  --line: #e3e6ea;
  --bg: #f6f7f9;
  --card: #fff;
  --accent: #1f5eff;
  --good: #0a7c42;
  --good-bg: #eaf6ef;
  --warn: #8a5a00;
  --warn-bg: #fdf4e3;
  --bad: #b3261e;
  --bad-bg: #fdeceb;

  /* The three cell states. Everything that reports status — cells, row
     pills, dashboard boxes, filters — draws from these and nothing else, so
     a colour means one thing wherever it appears. */
  --s-ok-bg: #fff;
  --s-ok-line: var(--line);
  --s-auto-bg: #fdf6e3;
  --s-auto-line: #efe0b4;
  --s-auto-ink: #7a5c05;
  --s-error-bg: #fdeeed;
  --s-error-line: #f2cecb;
  --s-error-ink: var(--bad);
}

* { box-sizing: border-box; }

body {
  margin: 0;
  font: 15px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  color: var(--ink);
  background: var(--bg);
}

main { max-width: 960px; margin: 0 auto; padding: 24px 20px 64px; }

.card {
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  padding: 22px; margin-bottom: 18px;
}
.card.narrow { max-width: 420px; margin: 60px auto; }

h1 { font-size: 22px; margin: 0 0 6px; }
h2 { font-size: 17px; margin: 0 0 10px; }
.muted { color: var(--muted); }
.empty { color: var(--muted); padding: 18px 0; }
.hint { font-size: 13px; color: var(--muted); margin: 4px 0 0; }
.ok { color: var(--good); }
.warn { color: var(--warn); }
.bad { color: var(--bad); }
code { background: #f0f1f3; padding: 1px 5px; border-radius: 4px; font-size: 13px; }

.topline { display: flex; align-items: center; justify-content: space-between;
  gap: 16px; margin: 0 0 16px; flex-wrap: wrap; }
.topline-actions { display: flex; align-items: center; gap: 10px; }
.topline-actions button { margin-top: 0; }
.icon-btn { position: relative; width: 34px; height: 34px; padding: 0;
  border-radius: 8px; background: var(--card); color: var(--muted);
  border: 1px solid var(--line); font-size: 15px; line-height: 1; }
.icon-btn:hover { color: var(--ink); border-color: var(--accent); opacity: 1; }
.icon-btn[data-tip]::after {
  content: attr(data-tip); position: absolute; right: 0; top: calc(100% + 8px);
  width: 260px; padding: 9px 12px; background: var(--ink); color: #fff;
  border-radius: 7px; font: 400 12.5px/1.5 -apple-system, BlinkMacSystemFont,
  "Segoe UI", Roboto, sans-serif; text-align: left; opacity: 0;
  visibility: hidden; transition: opacity .12s; z-index: 50;
  pointer-events: none; box-shadow: 0 4px 16px rgba(0,0,0,.18); }
.icon-btn:hover::after, .icon-btn:focus::after { opacity: 1; visibility: visible; }

/* Where the review runs but the upload does not exist yet. Says so in place of
   the button rather than letting someone reach the confirmation and bounce. */
.not-ready { position: relative; display: inline-block; padding: 8px 14px;
  border: 1px dashed var(--line); border-radius: 8px; color: var(--muted);
  font-size: 13px; cursor: default; }
.not-ready[data-tip]::after {
  content: attr(data-tip); position: absolute; right: 0; top: calc(100% + 8px);
  width: 260px; padding: 9px 12px; background: var(--ink); color: #fff;
  border-radius: 7px; font: 400 12.5px/1.5 -apple-system, BlinkMacSystemFont,
  "Segoe UI", Roboto, sans-serif; text-align: left; opacity: 0;
  visibility: hidden; transition: opacity .12s; z-index: 50;
  pointer-events: none; box-shadow: 0 4px 16px rgba(0,0,0,.18); }
.not-ready:hover::after { opacity: 1; visibility: visible; }

.steps { display: flex; gap: 8px; list-style: none; padding: 0; margin: 0;
  font-size: 13px; color: var(--muted); flex-wrap: wrap; }
.steps li { border-radius: 999px; background: var(--card);
  border: 1px solid var(--line); }
.steps a, .steps span { display: block; padding: 4px 12px; border-radius: 999px;
  color: var(--muted); text-decoration: none; }
.steps a:hover { color: var(--ink); background: #eceef1; }
.steps li.now { background: var(--ink); border-color: var(--ink); }
.steps li.now a, .steps li.now span { color: #fff; }
.steps li.now a:hover { background: none; color: #fff; }
.steps span { color: #b3b8c0; cursor: default; }

label { display: block; font-weight: 550; margin: 14px 0 5px; }
input[type=text], input[type=tel], input[type=search], select, input[type=file] {
  width: 100%; padding: 9px 11px; border: 1px solid var(--line);
  border-radius: 7px; font: inherit; background: #fff;
}
.field { margin-bottom: 6px; }
button, .primary {
  display: inline-block; margin-top: 14px; padding: 9px 16px;
  background: var(--ink); color: #fff; border: 0; border-radius: 7px;
  font: inherit; font-weight: 550; cursor: pointer; text-decoration: none;
}
button:hover { opacity: .9; }
button:disabled { opacity: .5; cursor: default; }
button.danger { background: var(--bad); }
button.link, .link {
  background: none; color: var(--accent); padding: 0; margin-left: 12px;
  font-weight: 450; text-decoration: none;
}

.flash { padding: 10px 14px; border-radius: 8px; margin-bottom: 14px; }
.flash.error, .note.error { background: var(--bad-bg); color: var(--bad); }
.flash.ok { background: var(--good-bg); color: var(--good); }
/* Something we could not read, on a screen that still works — a stale
   master list, say. Not an error and not a success; the same yellow
   the review grid uses for a value we changed on the user's behalf. */
.flash.warn { background: var(--warn-bg); color: var(--warn); }

/* --- Waiting on Onsite --------------------------------------------------- */
/* Deliberately covering and deliberately plain. The wait is real work (a large
   party library is dozens of paginated calls), and the only wrong thing to do
   with it is let the screen look idle. */
.waiting {
  position: fixed; inset: 0; z-index: 100; cursor: progress;
  background: rgba(255, 255, 255, 0.72);
  display: flex; align-items: center; justify-content: center;
  backdrop-filter: blur(1.5px);
}
/* An author-level `display` beats the browser's `[hidden] { display: none }`,
   so it has to opt back out — otherwise the overlay covers every page. */
.waiting[hidden] { display: none; }
.waiting-card {
  display: flex; align-items: center; gap: 12px;
  background: var(--card, #fff); color: var(--ink, #222);
  border: 1px solid var(--line, #e3e3e3); border-radius: 10px;
  padding: 14px 20px; box-shadow: 0 8px 28px rgba(0, 0, 0, 0.12);
  font-size: 14px;
}
.spinner {
  width: 16px; height: 16px; flex: none; border-radius: 50%;
  border: 2px solid var(--line, #ddd); border-top-color: var(--accent, #2b6cb0);
  animation: spin 0.7s linear infinite;
}
@keyframes spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .spinner { animation-duration: 2.4s; }
}
.note { padding: 10px 14px; border-radius: 8px; margin: 10px 0; font-size: 14px; }
.note.warning, .note.warn { background: var(--warn-bg); color: var(--warn); }
.note.info { background: #eef2fb; color: #2c4a86; }

table { width: 100%; border-collapse: collapse; font-size: 14px; }
th { text-align: left; font-weight: 550; color: var(--muted); font-size: 12px;
  text-transform: uppercase; letter-spacing: .04em; padding: 8px 10px 8px 0;
  border-bottom: 1px solid var(--line); }
td { padding: 9px 10px 9px 0; border-bottom: 1px solid var(--line);
  vertical-align: middle; }
.scroll { overflow-x: auto; }
.preview td { white-space: nowrap; max-width: 260px; overflow: hidden;
  text-overflow: ellipsis; }


.tallies { display: flex; gap: 12px; margin: 16px 0; flex-wrap: wrap; }
.tally { flex: 1; min-width: 120px; padding: 14px; border-radius: 8px;
  background: var(--bg); border: 1px solid var(--line); }
.tally strong { display: block; font-size: 26px; line-height: 1.1; }
.tally span { font-size: 13px; color: var(--muted); }
.tally.good { background: var(--good-bg); border-color: #cbe7d7; }
.tally.bad { background: var(--bad-bg); border-color: #f3cfcd; }

.group { border: 1px solid var(--line); border-radius: 8px; padding: 14px;
  margin: 12px 0; }
.group-head { display: flex; justify-content: space-between; gap: 12px;
  align-items: baseline; flex-wrap: wrap; }
.group .query { font-size: 16px; }
.resolve { display: flex; gap: 8px; align-items: center; margin-top: 10px;
  flex-wrap: wrap; }
.resolve select { flex: 2; min-width: 260px; }
.resolve .finder { flex: 1; min-width: 160px; }
.resolve button { margin-top: 0; white-space: nowrap; }
.inline-edit { display: flex; gap: 6px; }
.inline-edit input { min-width: 130px; }
.inline-edit button { margin-top: 0; padding: 5px 10px; font-size: 13px; }
.fixes td { font-size: 13px; }

.commit-statement {
  font-size: 17px; line-height: 1.5; padding: 16px; border-radius: 8px;
  background: var(--bg); border: 1px solid var(--line); margin: 12px 0;
}
.commit-statement .company-name { border-bottom: 2px solid var(--accent); }
/* The result screen's answer, before any detail. Did it go in, yes or no —
   a tally of committed against not-sent rows made the reader do arithmetic to
   find that out, and for an all-or-nothing endpoint the arithmetic was of a
   split that cannot happen. */
.verdict { padding: 16px 18px; border-radius: 10px; margin: 4px 0 6px; }
.verdict h1 { font-size: 24px; margin: 0 0 4px; }
.verdict p { margin: 0; font-size: 14px; line-height: 1.5; }
.verdict.good { background: var(--good-bg); color: var(--good); }
.verdict.warn { background: var(--warn-bg); color: var(--warn); }
.verdict.bad  { background: var(--bad-bg);  color: var(--bad); }
.verdict.good h1, .verdict.warn h1, .verdict.bad h1 { color: inherit; }
.actions-row { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }

/* Which Onsite an upload is about to be written into. Loud on live, because
   that is the one that cannot be undone. */
.env-line { font-size: 13px; margin: 0 0 10px; padding: 6px 10px;
  border-radius: 6px; display: inline-block; }
.env-line.live { background: var(--bad-bg); color: var(--bad); font-weight: 600; }
.env-line.test { background: var(--bg); color: var(--muted); }

/* A column of theirs nobody has ruled on. Red, because grey read as
   "nothing to see" and the whole failure is that it looks unimportant. */
.grid th.undecided { color: var(--bad); background: var(--bad-bg); }
.grid th.undecided .sample { color: var(--bad); opacity: .85; }
.grid th.ignored { color: var(--muted); text-decoration: line-through; }

/* A cell that is fine is still openable, and should not shout about it: no
   border, no mark, just the value — until the pointer is over it. */
.grid .cell-fix.quiet { border-color: transparent; background: none; }
.grid .cell-fix.quiet:hover { border-color: var(--line); background: var(--card); }
/* What the column's values look like. Said in the header so it reads without
   opening the picker, and phrased as a guess because that is what it is. */
.grid th .suggests { display: block; font-style: normal; font-size: 11px;
  font-weight: 500; margin-top: 2px; color: var(--accent); }
.grid option.suggested { font-weight: 600; }
/* A value from the column, under its heading: "cat" says nothing,
   "painting material" is what makes it recognisable. */
.grid th .sample b { font-weight: 500; opacity: .8; }
.grid th .sample { display: block; font-style: normal; font-weight: 400;
  font-size: 11px; margin-top: 2px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; max-width: 160px; }

/* A row left out by hand. Struck through rather than hidden: it is still part
   of the file the user is looking at, and they have to be able to see what
   they took out in order to put it back. */
.grid tr.excluded td { opacity: 0.55; }
.grid tr.excluded .txt { text-decoration: line-through; }
.grid td.c-pick, .grid th.c-pick { width: 30px; text-align: center; }
/* The same outline the tree uses for a picked row, rather than a tint:
   a background would fight the yellow and red the cells already carry. */
.grid tr.picked > td:not(.c-state) { box-shadow: inset 0 0 0 1px var(--accent); }

/* How much of the list you are looking at. A picker showing 20 of 300 with
   nothing to say so reads as a list of 20. */
.finder-note { font-size: 12px; color: var(--muted); margin: 6px 2px 0; }

.facts { display: grid; grid-template-columns: 120px 1fr; gap: 6px 14px;
  margin: 14px 0; font-size: 14px; }
.facts dt { color: var(--muted); }
.facts dd { margin: 0; }
.canonical { background: #12151a; color: #d7dde5; padding: 14px;
  border-radius: 8px; overflow-x: auto; font-size: 12.5px; line-height: 1.6; }

.status { font-size: 12px; padding: 2px 9px; border-radius: 999px;
  background: var(--bg); border: 1px solid var(--line); }
.status.committed { background: var(--good-bg); color: var(--good); border-color: #cbe7d7; }
/* Everything not yet sent reads as a draft, whatever stage it stopped at —
   "mapped" and "committing" are the machine's words, not anybody's. */
.status.mapped, .status.ok, .status.new, .status.parsed, .status.committing {
  background: var(--bg); color: var(--muted); }
.status.failed, .status.unknown, .status.partial {
  background: var(--bad-bg); color: var(--bad); border-color: #f3cfcd; }

.companies { list-style: none; padding: 0; }
.companies li { margin-bottom: 8px; }
.company-pick { display: block; width: 100%; text-align: left; margin: 0;
  background: var(--card); color: var(--ink); border: 1px solid var(--line);
  padding: 14px 16px; }
.company-pick:hover { border-color: var(--accent); }
.company-pick strong { display: block; }
.company-pick .muted { font-size: 13px; font-weight: 400; }

/* --- Shell: logo bar, sidebar, content ------------------------------------
   The company is in the top bar on every screen because uploads cannot be
   undone and posting into the wrong one is the worst realistic mistake. */
.topbar {
  display: flex; align-items: center; gap: 24px;
  padding: 10px 20px; background: var(--card);
  border-bottom: 1px solid var(--line);
}
.brand { display: flex; align-items: center; gap: 10px;
  text-decoration: none; color: var(--ink); font-weight: 650; }
/* The mark is not square (452×430), so height alone — setting both
   squashes it, and a border-radius on a transparent round mark
   clips its own edge. */
.brand img { height: 26px; width: auto; }
.topbar-right { margin-left: auto; display: flex; align-items: center; gap: 16px;
  font-size: 13.5px; }
.topbar-right a { color: var(--muted); text-decoration: none; }
.topbar-right a:hover { color: var(--ink); }
.topbar-right .who { color: var(--ink); font-weight: 550; }
.env { font-size: 11px; font-weight: 650; text-transform: uppercase;
  letter-spacing: .05em; padding: 3px 9px; border-radius: 999px;
  background: var(--warn-bg); color: var(--warn); }

.body { display: flex; align-items: flex-start; }
.body.no-chrome { justify-content: center; }
.sidebar { width: 218px; flex: none; padding: 18px 12px; position: sticky; top: 0; }
.company-switch { position: relative; margin: 0 2px 4px; }
.company-current { display: flex; align-items: center; gap: 8px; width: 100%;
  margin: 0; padding: 9px 11px; font-size: 14px; font-weight: 600;
  text-align: left; border-radius: 8px; border: 1px solid var(--line);
  background: var(--card); color: var(--ink); }
.company-current:hover { border-color: var(--accent); opacity: 1; }
.company-current span { flex: 1; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.company-current em { font-style: normal; color: var(--muted); font-size: 11px; }

/* Opens downwards, from the control. */
.company-menu { position: absolute; top: calc(100% + 4px); left: 0; right: 0;
  z-index: 70; background: var(--card); border: 1px solid var(--line);
  border-radius: 9px; box-shadow: 0 8px 28px rgba(0,0,0,.16); padding: 6px;
  max-height: 340px; overflow-y: auto; }
.company-menu[hidden] { display: none; }
.company-menu input[type=search] { width: 100%; padding: 7px 9px;
  font-size: 13px; margin-bottom: 4px; }
.company-menu ul { list-style: none; margin: 0; padding: 0; }
.company-menu li button { display: block; width: 100%; margin: 0;
  padding: 7px 10px; text-align: left; font-size: 13.5px; font-weight: 400;
  background: none; color: var(--ink); border: 0; border-radius: 6px;
  white-space: normal; }
.company-menu li button:hover { background: #eceef1; opacity: 1; }
.company-menu li button.on { background: var(--ink); color: #fff;
  font-weight: 600; }
.side-label { font-size: 10.5px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--muted); margin: 16px 10px 6px; font-weight: 650; }
.side-label:first-child { margin-top: 0; }
.sidebar ul { list-style: none; margin: 0; padding: 0; }
.sidebar li { margin-bottom: 1px; }
.sidebar a, .sidebar .soon {
  display: flex; align-items: center; justify-content: space-between;
  padding: 8px 10px; border-radius: 7px; text-decoration: none;
  color: var(--ink); font-size: 14px;
}
.sidebar a:hover { background: #eceef1; }
.sidebar a.on { background: var(--ink); color: #fff; font-weight: 550; }
.sidebar .soon { color: #a3a9b3; cursor: default; }
.sidebar .soon em { font-style: normal; font-size: 10px; text-transform: uppercase;
  letter-spacing: .05em; background: var(--bg); border: 1px solid var(--line);
  color: var(--muted); padding: 1px 6px; border-radius: 999px; }

/* `min-width: 0` is what makes the wide review table scroll inside its
   own box. A flex item will not shrink below its content by default, so
   a thirty-column file stretched the whole page sideways instead —
   taking the step tabs, the topbar and the Upload button off screen
   with it, which is how the button became unreachable. */
main { flex: 1; min-width: 0; max-width: 900px; padding: 22px 24px 72px; }
.body.no-chrome main { max-width: 460px; }

.login-logo { height: 48px; width: auto; margin-bottom: 10px; }
.env-line { margin: 8px 0 0; }

/* --- Review: their file as a table ----------------------------------------
   The point of this screen is that a person recognises their own spreadsheet
   and can see, at a glance, which cells we could not make sense of. Status is
   carried by colour AND a mark, never colour alone. */
.lead { font-size: 15px; color: #444b55; margin: 6px 0 14px; max-width: 62ch; }
.primary.big { font-size: 15.5px; padding: 11px 20px; }
button.secondary, .secondary { background: var(--card); color: var(--ink);
  border: 1px solid var(--line); }
.review-head { display: flex; justify-content: space-between; gap: 24px;
  align-items: flex-start; flex-wrap: wrap; }
.tallies.compact { margin: 0; gap: 8px; }
.tallies.compact .tally { min-width: 104px; padding: 10px 14px; }
.tallies.compact .tally strong { font-size: 21px; }

.table-card { padding: 16px 0 0; }
.table-controls { display: flex; gap: 16px; flex-wrap: wrap;
  padding: 0 18px 14px; }
.filters { display: flex; gap: 2px; background: var(--bg);
  border: 1px solid var(--line); border-radius: 8px; padding: 3px; }
.filters a { padding: 5px 11px; border-radius: 6px; font-size: 13px;
  color: var(--muted); text-decoration: none; white-space: nowrap; }
.filters a.on { background: var(--card); color: var(--ink);
  font-weight: 600; box-shadow: 0 1px 2px rgba(0,0,0,.08); }
.filters a.on[href*="filter=auto"] { background: var(--s-auto-bg);
  color: var(--s-auto-ink); }
.filters a.on[href*="filter=errors"] { background: var(--s-error-bg);
  color: var(--s-error-ink); }

.grid-scroll { overflow-x: auto; border-top: 1px solid var(--line); }
table.grid { border-collapse: separate; border-spacing: 0; font-size: 13.5px;
  min-width: 100%; }
table.grid th { padding: 8px 12px; background: var(--bg); text-align: left;
  border-bottom: 1px solid var(--line); white-space: nowrap;
  font-size: 12px; text-transform: none; letter-spacing: 0; color: var(--ink);
  font-weight: 600; vertical-align: top; }
table.grid th em { display: block; font-style: normal; font-size: 10.5px;
  color: var(--muted); font-weight: 400; text-transform: uppercase;
  letter-spacing: .05em; margin-top: 1px; }
table.grid th.not-uploaded { color: #a3a9b3; }
table.grid th .req { color: var(--bad); margin-left: 2px; }
table.grid td { padding: 0; border-bottom: 1px solid var(--line);
  max-width: 260px; }
table.grid td .txt { display: block; padding: 8px 12px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.c-num { color: var(--muted); font-variant-numeric: tabular-nums;
  text-align: right; padding: 8px 6px 8px 14px !important; width: 1%; }
.c-state { width: 1%; padding: 0 6px !important; text-align: center; }
.pill { display: inline-block; width: 18px; height: 18px; line-height: 18px;
  border-radius: 50%; font-size: 11px; font-weight: 700; }
.pill.ok { background: #eef1f4; color: #7b828c; }
.pill.auto { background: var(--s-auto-bg); color: var(--s-auto-ink);
  border: 1px solid var(--s-auto-line); }
.pill.error { background: var(--s-error-bg); color: var(--s-error-ink);
  border: 1px solid var(--s-error-line); }

td.cell.ok { background: var(--s-ok-bg); }
td.cell.auto { background: var(--s-auto-bg); }
td.cell.error { background: var(--s-error-bg); }
td.cell.not-uploaded .txt { color: #b3b8c0; }
td.cell.edited { box-shadow: inset 3px 0 0 var(--accent); }
tr.row-error .c-num { color: var(--bad); font-weight: 600; }
td.cell.focus { outline: 2px solid var(--accent); outline-offset: -2px; }
/* A remark is the longest thing on this screen and the column cannot be wide
   enough for it. Clicking a clipped cell lets it wrap: the row grows, the
   columns do not move, and clicking again puts it back. Hovering gives the
   whole value as a tooltip for anyone who does not think to click. */
td.cell.open { white-space: normal; }
td.cell.open .txt { overflow: visible; text-overflow: clip;
  white-space: normal; word-break: break-word; }
td.cell.clipped .txt { cursor: zoom-in; }
td.cell.open .txt { cursor: zoom-out; }
/* What they wrote, under what we made of it. Grey, because it is context
   rather than a second value. */
td.cell .was { display: block; padding: 0 12px 6px; font-style: normal;
  font-size: 11px; color: #9aa1ab; }

.cell-fix { display: flex; align-items: center; gap: 6px; width: 100%;
  margin: 0; padding: 0; background: none; border: 0; border-radius: 0;
  color: inherit; font: inherit; text-align: left; cursor: pointer; }
.cell-fix:hover { background: rgba(0,0,0,.04); }
.cell-fix .mark { margin-left: auto; padding-right: 10px; font-weight: 700;
  font-size: 11px; }
td.cell.error .cell-fix .mark { color: var(--s-error-ink); }
td.cell.auto .cell-fix .mark { color: var(--s-auto-ink); }

tr.row-note td { padding: 4px 12px 8px; font-size: 12.5px; background: inherit; }
tr.row-note .warning { color: var(--s-auto-ink); }
tr.row-note .error { color: var(--s-error-ink); }

.pager { display: flex; justify-content: space-between; align-items: center;
  gap: 16px; padding: 12px 18px; flex-wrap: wrap; font-size: 13.5px; }
.pager .pages { display: flex; gap: 14px; align-items: center; }
.pager a { color: var(--accent); text-decoration: none; }

.reload { display: flex; align-items: center; gap: 12px; margin: -4px 0 24px 2px; }
.reload button { margin-top: 0; }
.reload .hint { margin: 0; }

/* The fix panel — one in the page, moved beside whichever cell was clicked. */
.panel { position: absolute; z-index: 40; width: 400px; max-width: calc(100vw - 32px);
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 8px 32px rgba(0,0,0,.16); padding: 16px 18px 18px; }
.panel-close { position: absolute; top: 8px; right: 10px; margin: 0; padding: 2px 8px;
  background: none; color: var(--muted); font-size: 18px; line-height: 1; }
.panel-kind { margin: 0; font-size: 10.5px; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted); font-weight: 650; }
.panel-value { margin: 2px 0 6px; font-size: 17px; word-break: break-word; }
.panel-why { margin: 0 0 12px; font-size: 13px; color: var(--s-error-ink); }
.panel-why:empty { display: none; }
.panel .hint { margin: 10px 0 0; }
.panel .clash td { padding: 7px 12px 7px 0; border-bottom: 1px solid var(--line); }
.panel .clash tr:last-child td { border-bottom: 0; }
.panel .choices { list-style: none; margin: 0 0 10px; padding: 0;
  max-height: 220px; overflow-y: auto; }
.panel .choices li + li { margin-top: 5px; }
.panel .choices label { display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 7px;
  cursor: pointer; font-size: 13.5px; }
.panel .choices label:hover { border-color: var(--accent); }
.panel .choices label:has(input:checked) { border-color: var(--accent);
  background: #f5f8ff; }
.panel .choices .name { flex: 1; }
.panel .choices .none { color: var(--muted); font-size: 13px; padding: 6px 2px; }
.panel .tag { font-size: 10.5px; color: var(--muted); background: var(--bg);
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 7px; }
.panel .score { font-size: 11.5px; color: var(--good); font-weight: 600; }
.panel input[type=text], .panel input[type=search] { width: 100%; }
.panel-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
  margin-top: 10px; }
.panel-actions button { margin-top: 0; font-size: 13.5px; }

.panel .revert { margin-top: 10px; border-top: 1px solid var(--line);
  padding-top: 10px; }
.panel .revert button { margin: 0; font-size: 13px; }
.do-this { background: var(--warn-bg); color: #6d4a06; border-radius: 8px;
  padding: 11px 13px; font-size: 13.5px; margin: 0 0 10px; }
.clash td { padding: 5px 10px 5px 0; font-size: 13.5px; }

/* The review screen gets the full window — a table squeezed into a
   reading-width column means horizontal scrolling to see your own file. */
main.wide { max-width: none; padding-right: 24px; }
main.wide .grid-scroll { max-height: calc(100vh - 220px); overflow: auto; }
main.wide table.grid thead th { position: sticky; top: 0; z-index: 2; }
main.wide table.grid .c-num,
main.wide table.grid .c-state { position: sticky; z-index: 1;
  background: var(--card); }
main.wide table.grid .c-num { left: 0; }
main.wide table.grid .c-state { left: 46px; }
main.wide table.grid thead .c-num,
main.wide table.grid thead .c-state { z-index: 3; background: var(--bg); }
main.wide tr.row-error .c-num, main.wide tr.row-error .c-state { background: #fffafa; }
main.wide tr.just-fixed .c-num, main.wide tr.just-fixed .c-state { background: var(--good-bg); }

/* Mode picker — two options side by side, and an icon rather than a
   paragraph. The difference between them is one sentence, and a sentence
   does not need a third of the screen. */
.mode-row { margin: 16px 0 4px; }
.mode-row > label { display: flex; align-items: center; gap: 7px; }
.segmented { display: inline-flex; background: var(--bg); padding: 3px;
  border: 1px solid var(--line); border-radius: 8px; gap: 2px; }
.segmented label { display: flex; align-items: center; gap: 7px; margin: 0;
  padding: 7px 14px; border-radius: 6px; cursor: pointer; font-weight: 450;
  font-size: 14px; color: var(--muted); white-space: nowrap; }
.segmented label.on { background: var(--card); color: var(--ink);
  font-weight: 600; box-shadow: 0 1px 2px rgba(0,0,0,.08); }
.segmented input { width: auto; margin: 0; }

.info { position: relative; display: inline-flex; align-items: center;
  justify-content: center; width: 15px; height: 15px; border-radius: 50%;
  background: var(--muted); color: #fff; font-size: 10.5px; font-weight: 700;
  font-style: italic; cursor: help; flex: none; }
.info:hover, .info:focus { background: var(--accent); outline: none; }
.info[data-tip]::after {
  content: attr(data-tip); position: absolute; left: 50%; top: calc(100% + 8px);
  transform: translateX(-50%); width: 300px; padding: 9px 12px;
  background: var(--ink); color: #fff; border-radius: 7px; font: 400 12.5px/1.5
  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-style: normal; text-align: left; opacity: 0; visibility: hidden;
  transition: opacity .12s; z-index: 50; pointer-events: none;
  box-shadow: 0 4px 16px rgba(0,0,0,.18); }
.info:hover::after, .info:focus::after { opacity: 1; visibility: visible; }
.field .req { color: var(--bad); margin-left: 2px; }

/* --- Review: status bar, in-table mapping, confirmation -------------------
   One box of numbers, one action. Progress is "how many of the decisions this
   file needed are settled" — a bare "2 to fix" says nothing about how far
   through you are. */
.lead { font-size: 15px; color: #444b55; margin: 6px 0 14px; max-width: 62ch; }
.primary.big { font-size: 15.5px; padding: 11px 20px; }
button.secondary, .secondary { background: var(--card); color: var(--ink);
  border: 1px solid var(--line); }
.review-head { display: flex; justify-content: space-between; gap: 24px;
  align-items: flex-start; flex-wrap: wrap; }
.tallies.compact { margin: 0; gap: 8px; }
.tallies.compact .tally { min-width: 104px; padding: 10px 14px; }
.tallies.compact .tally strong { font-size: 21px; }

.table-card { padding: 16px 0 0; }
.table-controls { display: flex; gap: 16px; flex-wrap: wrap;
  padding: 0 18px 14px; }
.filters { display: flex; gap: 2px; background: var(--bg);
  border: 1px solid var(--line); border-radius: 8px; padding: 3px; }
.filters a { padding: 5px 11px; border-radius: 6px; font-size: 13px;
  color: var(--muted); text-decoration: none; white-space: nowrap; }
.filters a.on { background: var(--card); color: var(--ink);
  font-weight: 600; box-shadow: 0 1px 2px rgba(0,0,0,.08); }
.filters a.on[href*="filter=auto"] { background: var(--s-auto-bg);
  color: var(--s-auto-ink); }
.filters a.on[href*="filter=errors"] { background: var(--s-error-bg);
  color: var(--s-error-ink); }

.grid-scroll { overflow-x: auto; border-top: 1px solid var(--line); }
table.grid { border-collapse: separate; border-spacing: 0; font-size: 13.5px;
  min-width: 100%; }
table.grid th { padding: 8px 12px; background: var(--bg); text-align: left;
  border-bottom: 1px solid var(--line); white-space: nowrap;
  font-size: 12px; text-transform: none; letter-spacing: 0; color: var(--ink);
  font-weight: 600; vertical-align: top; }
table.grid th em { display: block; font-style: normal; font-size: 10.5px;
  color: var(--muted); font-weight: 400; text-transform: uppercase;
  letter-spacing: .05em; margin-top: 1px; }
table.grid th.not-uploaded { color: #a3a9b3; }
table.grid th .req { color: var(--bad); margin-left: 2px; }
table.grid td { padding: 0; border-bottom: 1px solid var(--line);
  max-width: 260px; }
table.grid td .txt { display: block; padding: 8px 12px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.c-num { color: var(--muted); font-variant-numeric: tabular-nums;
  text-align: right; padding: 8px 6px 8px 14px !important; width: 1%; }
.c-state { width: 1%; padding: 0 6px !important; text-align: center; }
.pill { display: inline-block; width: 18px; height: 18px; line-height: 18px;
  border-radius: 50%; font-size: 11px; font-weight: 700; }
.pill.ok { background: #eef1f4; color: #7b828c; }
.pill.auto { background: var(--s-auto-bg); color: var(--s-auto-ink);
  border: 1px solid var(--s-auto-line); }
.pill.error { background: var(--s-error-bg); color: var(--s-error-ink);
  border: 1px solid var(--s-error-line); }

td.cell.ok { background: var(--s-ok-bg); }
td.cell.auto { background: var(--s-auto-bg); }
td.cell.error { background: var(--s-error-bg); }
td.cell.not-uploaded .txt { color: #b3b8c0; }
td.cell.edited { box-shadow: inset 3px 0 0 var(--accent); }
tr.row-error .c-num { color: var(--bad); font-weight: 600; }
td.cell.focus { outline: 2px solid var(--accent); outline-offset: -2px; }
/* What they wrote, under what we made of it. Grey, because it is context
   rather than a second value. */
td.cell .was { display: block; padding: 0 12px 6px; font-style: normal;
  font-size: 11px; color: #9aa1ab; }

.cell-fix { display: flex; align-items: center; gap: 6px; width: 100%;
  margin: 0; padding: 0; background: none; border: 0; border-radius: 0;
  color: inherit; font: inherit; text-align: left; cursor: pointer; }
.cell-fix:hover { background: rgba(0,0,0,.04); }
.cell-fix .mark { margin-left: auto; padding-right: 10px; font-weight: 700;
  font-size: 11px; }
td.cell.error .cell-fix .mark { color: var(--s-error-ink); }
td.cell.auto .cell-fix .mark { color: var(--s-auto-ink); }

tr.row-note td { padding: 4px 12px 8px; font-size: 12.5px; background: inherit; }
tr.row-note .warning { color: var(--s-auto-ink); }
tr.row-note .error { color: var(--s-error-ink); }

.pager { display: flex; justify-content: space-between; align-items: center;
  gap: 16px; padding: 12px 18px; flex-wrap: wrap; font-size: 13.5px; }
.pager .pages { display: flex; gap: 14px; align-items: center; }
.pager a { color: var(--accent); text-decoration: none; }

.reload { display: flex; align-items: center; gap: 12px; margin: -4px 0 24px 2px; }
.reload button { margin-top: 0; }
.reload .hint { margin: 0; }

/* The fix panel — one in the page, moved beside whichever cell was clicked. */
.panel { position: absolute; z-index: 40; width: 400px; max-width: calc(100vw - 32px);
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 8px 32px rgba(0,0,0,.16); padding: 16px 18px 18px; }
.panel-close { position: absolute; top: 8px; right: 10px; margin: 0; padding: 2px 8px;
  background: none; color: var(--muted); font-size: 18px; line-height: 1; }
.panel-kind { margin: 0; font-size: 10.5px; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted); font-weight: 650; }
.panel-value { margin: 2px 0 6px; font-size: 17px; word-break: break-word; }
.panel-why { margin: 0 0 12px; font-size: 13px; color: var(--s-error-ink); }
.panel-why:empty { display: none; }
.panel .hint { margin: 10px 0 0; }
.panel .clash td { padding: 7px 12px 7px 0; border-bottom: 1px solid var(--line); }
.panel .clash tr:last-child td { border-bottom: 0; }
.panel .choices { list-style: none; margin: 0 0 10px; padding: 0;
  max-height: 220px; overflow-y: auto; }
.panel .choices li + li { margin-top: 5px; }
.panel .choices label { display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 7px;
  cursor: pointer; font-size: 13.5px; }
.panel .choices label:hover { border-color: var(--accent); }
.panel .choices label:has(input:checked) { border-color: var(--accent);
  background: #f5f8ff; }
.panel .choices .name { flex: 1; }
.panel .choices .none { color: var(--muted); font-size: 13px; padding: 6px 2px; }
.panel .tag { font-size: 10.5px; color: var(--muted); background: var(--bg);
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 7px; }
.panel .score { font-size: 11.5px; color: var(--good); font-weight: 600; }
.panel input[type=text], .panel input[type=search] { width: 100%; }
.panel-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
  margin-top: 10px; }
.panel-actions button { margin-top: 0; font-size: 13.5px; }

.panel .revert { margin-top: 10px; border-top: 1px solid var(--line);
  padding-top: 10px; }
.panel .revert button { margin: 0; font-size: 13px; }
.do-this { background: var(--warn-bg); color: #6d4a06; border-radius: 8px;
  padding: 11px 13px; font-size: 13.5px; margin: 0 0 10px; }
.clash td { padding: 5px 10px 5px 0; font-size: 13.5px; }

/* The review screen gets the full window — a table squeezed into a
   reading-width column means horizontal scrolling to see your own file. */
main.wide { max-width: none; padding-right: 24px; }
main.wide .grid-scroll { max-height: calc(100vh - 220px); overflow: auto; }
main.wide table.grid thead th { position: sticky; top: 0; z-index: 2; }
main.wide table.grid .c-num,
main.wide table.grid .c-state { position: sticky; z-index: 1;
  background: var(--card); }
main.wide table.grid .c-num { left: 0; }
main.wide table.grid .c-state { left: 46px; }
main.wide table.grid thead .c-num,
main.wide table.grid thead .c-state { z-index: 3; background: var(--bg); }
main.wide tr.row-error .c-num, main.wide tr.row-error .c-state { background: #fffafa; }
main.wide tr.just-fixed .c-num, main.wide tr.just-fixed .c-state { background: var(--good-bg); }

/* Mode picker — two options side by side, and an icon rather than a
   paragraph. The difference between them is one sentence, and a sentence
   does not need a third of the screen. */
.mode-row { margin: 16px 0 4px; }
.mode-row > label { display: flex; align-items: center; gap: 7px; }
.segmented { display: inline-flex; background: var(--bg); padding: 3px;
  border: 1px solid var(--line); border-radius: 8px; gap: 2px; }
.segmented label { display: flex; align-items: center; gap: 7px; margin: 0;
  padding: 7px 14px; border-radius: 6px; cursor: pointer; font-weight: 450;
  font-size: 14px; color: var(--muted); white-space: nowrap; }
.segmented label.on { background: var(--card); color: var(--ink);
  font-weight: 600; box-shadow: 0 1px 2px rgba(0,0,0,.08); }
.segmented input { width: auto; margin: 0; }

.info { position: relative; display: inline-flex; align-items: center;
  justify-content: center; width: 15px; height: 15px; border-radius: 50%;
  background: var(--muted); color: #fff; font-size: 10.5px; font-weight: 700;
  font-style: italic; cursor: help; flex: none; }
.info:hover, .info:focus { background: var(--accent); outline: none; }
.info[data-tip]::after {
  content: attr(data-tip); position: absolute; left: 50%; top: calc(100% + 8px);
  transform: translateX(-50%); width: 300px; padding: 9px 12px;
  background: var(--ink); color: #fff; border-radius: 7px; font: 400 12.5px/1.5
  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-style: normal; text-align: left; opacity: 0; visibility: hidden;
  transition: opacity .12s; z-index: 50; pointer-events: none;
  box-shadow: 0 4px 16px rgba(0,0,0,.18); }
.info:hover::after, .info:focus::after { opacity: 1; visibility: visible; }
.field .req { color: var(--bad); margin-left: 2px; }

/* --- Review: status bar, in-table mapping, confirmation -------------------
   One box of numbers, one action. Progress is "how many of the decisions this
   file needed are settled" — a bare "2 to fix" says nothing about how far
   through you are. */
.action-bar { display: flex; align-items: center; justify-content: space-between;
  gap: 20px; flex-wrap: wrap; padding: 14px 20px; }
.action-bar p { margin: 0; font-size: 13.5px; max-width: 62ch; }
.action-bar a { color: var(--accent); text-decoration: none; white-space: nowrap;
  margin-left: 10px; }
.action-bar button { margin-top: 0; }

.lead { font-size: 15px; color: #444b55; margin: 6px 0 14px; max-width: 62ch; }
.primary.big { font-size: 15.5px; padding: 11px 20px; }
button.secondary, .secondary { background: var(--card); color: var(--ink);
  border: 1px solid var(--line); }
.review-head { display: flex; justify-content: space-between; gap: 24px;
  align-items: flex-start; flex-wrap: wrap; }
.tallies.compact { margin: 0; gap: 8px; }
.tallies.compact .tally { min-width: 104px; padding: 10px 14px; }
.tallies.compact .tally strong { font-size: 21px; }

.table-card { padding: 16px 0 0; }
.table-controls { display: flex; gap: 16px; flex-wrap: wrap;
  padding: 0 18px 14px; }
.filters { display: flex; gap: 2px; background: var(--bg);
  border: 1px solid var(--line); border-radius: 8px; padding: 3px; }
.filters a { padding: 5px 11px; border-radius: 6px; font-size: 13px;
  color: var(--muted); text-decoration: none; white-space: nowrap; }
.filters a.on { background: var(--card); color: var(--ink);
  font-weight: 600; box-shadow: 0 1px 2px rgba(0,0,0,.08); }
.filters a.on[href*="filter=auto"] { background: var(--s-auto-bg);
  color: var(--s-auto-ink); }
.filters a.on[href*="filter=errors"] { background: var(--s-error-bg);
  color: var(--s-error-ink); }

.grid-scroll { overflow-x: auto; border-top: 1px solid var(--line); }
table.grid { border-collapse: separate; border-spacing: 0; font-size: 13.5px;
  min-width: 100%; }
table.grid th { padding: 8px 12px; background: var(--bg); text-align: left;
  border-bottom: 1px solid var(--line); white-space: nowrap;
  font-size: 12px; text-transform: none; letter-spacing: 0; color: var(--ink);
  font-weight: 600; vertical-align: top; }
table.grid th em { display: block; font-style: normal; font-size: 10.5px;
  color: var(--muted); font-weight: 400; text-transform: uppercase;
  letter-spacing: .05em; margin-top: 1px; }
table.grid th.not-uploaded { color: #a3a9b3; }
table.grid th .req { color: var(--bad); margin-left: 2px; }
table.grid td { padding: 0; border-bottom: 1px solid var(--line);
  max-width: 260px; }
table.grid td .txt { display: block; padding: 8px 12px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.c-num { color: var(--muted); font-variant-numeric: tabular-nums;
  text-align: right; padding: 8px 6px 8px 14px !important; width: 1%; }
.c-state { width: 1%; padding: 0 6px !important; text-align: center; }
.pill { display: inline-block; width: 18px; height: 18px; line-height: 18px;
  border-radius: 50%; font-size: 11px; font-weight: 700; }
.pill.ok { background: #eef1f4; color: #7b828c; }
.pill.auto { background: var(--s-auto-bg); color: var(--s-auto-ink);
  border: 1px solid var(--s-auto-line); }
.pill.error { background: var(--s-error-bg); color: var(--s-error-ink);
  border: 1px solid var(--s-error-line); }

td.cell.ok { background: var(--s-ok-bg); }
td.cell.auto { background: var(--s-auto-bg); }
td.cell.error { background: var(--s-error-bg); }
td.cell.not-uploaded .txt { color: #b3b8c0; }
td.cell.edited { box-shadow: inset 3px 0 0 var(--accent); }
tr.row-error .c-num { color: var(--bad); font-weight: 600; }
td.cell.focus { outline: 2px solid var(--accent); outline-offset: -2px; }
/* What they wrote, under what we made of it. Grey, because it is context
   rather than a second value. */
td.cell .was { display: block; padding: 0 12px 6px; font-style: normal;
  font-size: 11px; color: #9aa1ab; }

.cell-fix { display: flex; align-items: center; gap: 6px; width: 100%;
  margin: 0; padding: 0; background: none; border: 0; border-radius: 0;
  color: inherit; font: inherit; text-align: left; cursor: pointer; }
.cell-fix:hover { background: rgba(0,0,0,.04); }
.cell-fix .mark { margin-left: auto; padding-right: 10px; font-weight: 700;
  font-size: 11px; }
td.cell.error .cell-fix .mark { color: var(--s-error-ink); }
td.cell.auto .cell-fix .mark { color: var(--s-auto-ink); }

tr.row-note td { padding: 4px 12px 8px; font-size: 12.5px; background: inherit; }
tr.row-note .warning { color: var(--s-auto-ink); }
tr.row-note .error { color: var(--s-error-ink); }

.pager { display: flex; justify-content: space-between; align-items: center;
  gap: 16px; padding: 12px 18px; flex-wrap: wrap; font-size: 13.5px; }
.pager .pages { display: flex; gap: 14px; align-items: center; }
.pager a { color: var(--accent); text-decoration: none; }

.reload { display: flex; align-items: center; gap: 12px; margin: -4px 0 24px 2px; }
.reload button { margin-top: 0; }
.reload .hint { margin: 0; }

/* The fix panel — one in the page, moved beside whichever cell was clicked. */
.panel { position: absolute; z-index: 40; width: 400px; max-width: calc(100vw - 32px);
  background: var(--card); border: 1px solid var(--line); border-radius: 10px;
  box-shadow: 0 8px 32px rgba(0,0,0,.16); padding: 16px 18px 18px; }
.panel-close { position: absolute; top: 8px; right: 10px; margin: 0; padding: 2px 8px;
  background: none; color: var(--muted); font-size: 18px; line-height: 1; }
.panel-kind { margin: 0; font-size: 10.5px; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted); font-weight: 650; }
.panel-value { margin: 2px 0 6px; font-size: 17px; word-break: break-word; }
.panel-why { margin: 0 0 12px; font-size: 13px; color: var(--s-error-ink); }
.panel-why:empty { display: none; }
.panel .hint { margin: 10px 0 0; }
.panel .clash td { padding: 7px 12px 7px 0; border-bottom: 1px solid var(--line); }
.panel .clash tr:last-child td { border-bottom: 0; }
.panel .choices { list-style: none; margin: 0 0 10px; padding: 0;
  max-height: 220px; overflow-y: auto; }
.panel .choices li + li { margin-top: 5px; }
.panel .choices label { display: flex; align-items: center; gap: 9px;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 7px;
  cursor: pointer; font-size: 13.5px; }
.panel .choices label:hover { border-color: var(--accent); }
.panel .choices label:has(input:checked) { border-color: var(--accent);
  background: #f5f8ff; }
.panel .choices .name { flex: 1; }
.panel .choices .none { color: var(--muted); font-size: 13px; padding: 6px 2px; }
.panel .tag { font-size: 10.5px; color: var(--muted); background: var(--bg);
  border: 1px solid var(--line); border-radius: 999px; padding: 1px 7px; }
.panel .score { font-size: 11.5px; color: var(--good); font-weight: 600; }
.panel input[type=text], .panel input[type=search] { width: 100%; }
.panel-actions { display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
  margin-top: 10px; }
.panel-actions button { margin-top: 0; font-size: 13.5px; }

.panel .revert { margin-top: 10px; border-top: 1px solid var(--line);
  padding-top: 10px; }
.panel .revert button { margin: 0; font-size: 13px; }
.do-this { background: var(--warn-bg); color: #6d4a06; border-radius: 8px;
  padding: 11px 13px; font-size: 13.5px; margin: 0 0 10px; }
.clash td { padding: 5px 10px 5px 0; font-size: 13.5px; }

/* The review screen gets the full window — a table squeezed into a
   reading-width column means horizontal scrolling to see your own file. */
main.wide { max-width: none; padding-right: 24px; }
main.wide .grid-scroll { max-height: calc(100vh - 220px); overflow: auto; }
main.wide table.grid thead th { position: sticky; top: 0; z-index: 2; }
main.wide table.grid .c-num,
main.wide table.grid .c-state { position: sticky; z-index: 1;
  background: var(--card); }
main.wide table.grid .c-num { left: 0; }
main.wide table.grid .c-state { left: 46px; }
main.wide table.grid thead .c-num,
main.wide table.grid thead .c-state { z-index: 3; background: var(--bg); }
main.wide tr.row-error .c-num, main.wide tr.row-error .c-state { background: #fffafa; }
main.wide tr.just-fixed .c-num, main.wide tr.just-fixed .c-state { background: var(--good-bg); }

/* Mode picker — two options side by side, and an icon rather than a
   paragraph. The difference between them is one sentence, and a sentence
   does not need a third of the screen. */
.mode-row { margin: 16px 0 4px; }
.mode-row > label { display: flex; align-items: center; gap: 7px; }
.segmented { display: inline-flex; background: var(--bg); padding: 3px;
  border: 1px solid var(--line); border-radius: 8px; gap: 2px; }
.segmented label { display: flex; align-items: center; gap: 7px; margin: 0;
  padding: 7px 14px; border-radius: 6px; cursor: pointer; font-weight: 450;
  font-size: 14px; color: var(--muted); white-space: nowrap; }
.segmented label.on { background: var(--card); color: var(--ink);
  font-weight: 600; box-shadow: 0 1px 2px rgba(0,0,0,.08); }
.segmented input { width: auto; margin: 0; }

.info { position: relative; display: inline-flex; align-items: center;
  justify-content: center; width: 15px; height: 15px; border-radius: 50%;
  background: var(--muted); color: #fff; font-size: 10.5px; font-weight: 700;
  font-style: italic; cursor: help; flex: none; }
.info:hover, .info:focus { background: var(--accent); outline: none; }
.info[data-tip]::after {
  content: attr(data-tip); position: absolute; left: 50%; top: calc(100% + 8px);
  transform: translateX(-50%); width: 300px; padding: 9px 12px;
  background: var(--ink); color: #fff; border-radius: 7px; font: 400 12.5px/1.5
  -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  font-style: normal; text-align: left; opacity: 0; visibility: hidden;
  transition: opacity .12s; z-index: 50; pointer-events: none;
  box-shadow: 0 4px 16px rgba(0,0,0,.18); }
.info:hover::after, .info:focus::after { opacity: 1; visibility: visible; }
.field .req { color: var(--bad); margin-left: 2px; }

/* --- Review: status bar, in-table mapping, confirmation -------------------
   One box of numbers, one action. Progress is "how many of the decisions this
   file needed are settled" — a bare "2 to fix" says nothing about how far
   through you are. */
.status-bar { display: flex; align-items: center; justify-content: space-between;
  gap: 24px; flex-wrap: wrap; padding: 16px 20px; }
.status-counts { display: flex; gap: 10px; }
.stat { min-width: 96px; padding: 10px 14px; border-radius: 9px;
  background: var(--bg); border: 1px solid var(--line); }
.stat strong { display: block; font-size: 22px; line-height: 1.15; }
.stat .of { font-size: 14px; color: var(--muted); font-weight: 500; }
.stat span { font-size: 12.5px; color: var(--muted); }
.stat.ok { background: var(--s-ok-bg); border-color: var(--s-ok-line); }
.stat.auto { background: var(--s-auto-bg); border-color: var(--s-auto-line); }
.stat.auto strong { color: var(--s-auto-ink); }
.stat.error { background: var(--s-error-bg); border-color: var(--s-error-line); }
.stat.error strong { color: var(--s-error-ink); }
.stat em { display: block; font-style: normal; font-size: 11px;
  color: var(--muted); margin-top: 2px; }
.status-action { display: flex; align-items: center; gap: 16px; margin-left: auto;
  flex-wrap: wrap; justify-content: flex-end; }
.status-action p { margin: 0; font-size: 13px; max-width: 34ch; }
.status-action a { color: var(--accent); text-decoration: none; white-space: nowrap; }
.status-action button { margin-top: 0; }
.lead { font-size: 15px; color: #444b55; margin: 6px 0 14px; max-width: 62ch; }
.primary.big { font-size: 15.5px; padding: 11px 20px; }
button.secondary, .secondary { background: var(--card); color: var(--ink);
  border: 1px solid var(--line); }

/* Mapping lives in the header, under each column's own name. */
tr.map-row th { padding: 0 8px 8px; background: var(--bg); font-weight: 400;
  border-bottom: 1px solid var(--line); }
tr.map-row em { font-style: normal; font-size: 10.5px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .05em; }
.map-pick { width: 100%; min-width: 132px; padding: 4px 7px; font-size: 12px;
  border-radius: 6px; border: 1px solid var(--line); background: var(--card);
  color: var(--muted); }
.map-pick:hover { border-color: var(--accent); color: var(--ink); }
.map-pick:not([value=""]) { color: var(--ink); }

.drop-form { display: flex; gap: 12px; align-items: center; flex-wrap: wrap; }
.drop-form input[type=file] { flex: 1; min-width: 260px; }
.drop-form button { margin-top: 0; }
/* Shown only once a PDF has been picked. A password box on an Excel file
   reads as a demand rather than an offer. */
.pdf-password { display: flex; flex-direction: column; gap: 3px;
  font-size: 12.5px; color: var(--muted); }
.pdf-password input { min-width: 210px; padding: 6px 9px; font-size: 13px;
  border: 1px solid var(--line); border-radius: 7px; }
.pdf-password em { font-style: normal; font-size: 11px; }
/* An author-level `display` beats the browser's own `[hidden]` rule. */
.pdf-password[hidden] { display: none; }

.modal { position: fixed; inset: 0; background: rgba(16,19,23,.45);
  display: flex; align-items: center; justify-content: center; z-index: 60;
  padding: 20px; }
/* An author-level `display` beats the browser's `[hidden] { display: none }`,
   so anything given a display value has to opt back in explicitly or the
   hidden attribute silently does nothing. */
.modal[hidden] { display: none; }
.modal-card { background: var(--card); border-radius: 12px; padding: 24px;
  width: 520px; max-width: 100%; max-height: 90vh; overflow-y: auto;
  box-shadow: 0 12px 48px rgba(0,0,0,.28); }
.modal-card.wide { width: 940px; }
.modal-card h2 { font-size: 19px; margin: 0 0 10px; }
.modal-card .note { font-size: 13.5px; }
.commit-statement { font-size: 15.5px; line-height: 1.55; padding: 14px;
  border-radius: 8px; background: var(--bg); border: 1px solid var(--line);
  margin: 0 0 12px; }
.modal-actions { display: flex; gap: 10px; align-items: center; margin-top: 4px; }
.modal-actions button { margin-top: 0; }

/* A field with nothing feeding it. The whole column is empty, which is the
   point of ordering the table by Onsite's fields rather than by theirs — laid
   out their way there is no column for it to be missing from. */
/* Nothing feeds this field. Red only when the field is required — an empty
   optional column is an ordinary thing for a file to do. */
table.grid th.missing { color: var(--muted); }
table.grid th.missing-required,
table.grid tr.map-row th.missing-required { background: var(--s-error-bg);
  color: var(--s-error-ink); }
table.grid th.spacer, table.grid td.spacer { border-left: 2px solid var(--line); }
tr.map-row th em { font-style: normal; font-size: 10.5px; color: #b3b8c0;
  text-transform: uppercase; letter-spacing: .05em; }

/* --- The BOQ outline -------------------------------------------------------
   Not a table. A BOQ line is a description with a few numbers attached, and a
   description runs to 300 characters — pouring that into a fixed-width column
   beside eight short numeric ones gave clipped text, a 1400px minimum width
   and a page you had to scroll sideways to read. The description takes the
   whole line; the numbers sit under it on a strip, aligned down the page so a
   rate is still scannable.
*/
.tree-controls { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; padding: 10px 14px; }
.tree-tools { display: flex; gap: 6px; align-items: center; }
.ghost { background: none; border: 1px solid var(--line); color: var(--muted);
  border-radius: 7px; padding: 5px 10px; font-size: 12.5px; text-decoration: none;
  display: inline-block; }
.ghost:hover { color: var(--ink); border-color: var(--accent); }
.ghost.on { background: var(--ink); border-color: var(--ink); color: #fff; }

.outline { border-top: 1px solid var(--line); }

/* One line of the BOQ, as a grid.

   The numbers *are* tabular and belong in tracks — a rate has to line up with
   the rate above it, and a flex strip with minimum widths cannot promise that,
   because a longer value simply pushes the next one along. Only the
   description is not tabular, so it gets a wide track and everything that
   belongs to it — tags, notes, warnings — sits beneath it in the same track.

   That is the whole shape: columns for what is short, one wide column for
   prose, and prose's overflow underneath rather than beside. */
.line { display: grid;
  grid-template-columns:
    56px                 /* select and status, in the margin */
    96px                 /* the serial we generated */
    minmax(240px, 1fr)   /* the description */
    104px                /* unit */
    92px                 /* quantity */
    108px                /* rate */
    128px                /* amount, ours */
    118px;               /* amount, theirs */
  align-items: start; gap: 2px 8px; padding: 9px 14px 10px;
  border-bottom: 1px solid var(--line); }
.line:last-child { border-bottom: 0; }
/* Beneath the description, sharing its width and everything to its right. */
.line > .extras { grid-column: 3 / -1; }
.line > .theirs-rows { grid-column: 3 / -1; }

.line.head, .line.foot { background: var(--bg); font-size: 12px;
  font-weight: 600; color: var(--ink); }
.line.head em, .line.foot em { display: block; font-style: normal;
  font-size: 10.5px; color: var(--muted); font-weight: 400;
  text-transform: uppercase; letter-spacing: .05em; }
.line.head .unit, .line.head .num { color: var(--muted);
  text-transform: uppercase; font-size: 10.5px; letter-spacing: .05em; }
.line.foot { border-top: 2px solid var(--line); border-bottom: 0;
  font-weight: 400; color: var(--muted); }

.gutter { display: flex; align-items: center; gap: 6px; padding-top: 2px; }
.serial { color: var(--muted); font-size: 12.5px; white-space: nowrap;
  font-variant-numeric: tabular-nums; padding-top: 3px; }
.serial[data-tip] { cursor: help; }
.twisty { background: none; border: 0; color: var(--muted); font-size: 11px;
  width: 14px; padding: 0; cursor: pointer; }
.twisty[aria-expanded="false"] { transform: rotate(-90deg); display: inline-block; }
.twisty-gap { display: inline-block; width: 14px; }

/* Two lines of whatever width the line actually has. The browser measures;
   a character count cannot, and cut every description short of its own line. */
.desc { font-size: 13.5px; display: -webkit-box; -webkit-line-clamp: 2;
  line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  border-radius: 5px; padding-left: calc(var(--d, 0) * 18px);
  min-width: 0; }
.desc.edit { cursor: text; }
.desc.edit:hover { background: #f4f6f8; }
.line.group .desc, .line.section .desc { font-weight: 400; }
.line.item .desc { font-weight: 600; color: #0f5132; }
.line.group { background: #eceff3; }
.line.section { background: #f7f8fa; }
.line.just-fixed { background: var(--good-bg); }

.unit, .num { min-width: 0; }
.num { text-align: right; }
.amount { text-align: right; font-variant-numeric: tabular-nums;
  font-size: 13.5px; padding-top: 1px; }
/* Their own figure is no longer a column of its own — it is identical to ours
   on every line that is right, so the column earned its width only on the rare
   line where it differed. Red says the two disagree; the figure is on hover. */
.amount.mismatch { color: var(--bad); font-weight: 600; }

/* Everything that belongs to the line but is not one of its numbers. */
.extras { display: flex; align-items: center; flex-wrap: wrap; gap: 4px 8px;
  margin-top: 5px; font-size: 12.5px; color: var(--muted);
  padding-left: calc(var(--d, 0) * 18px); }
.note-text { color: var(--muted); font-size: 12px; max-width: 460px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; cursor: help; }

/* A value. Red is what Onsite will refuse, yellow is what we changed from the
   file — on the value, not on the line, or you know a line is wrong without
   knowing which part of it. */
.v { display: block; padding: 1px 5px; border-radius: 4px; color: var(--ink);
  font-variant-numeric: tabular-nums; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; }
.extras .v { display: inline-block; }
.v.edit { cursor: text; }
.v.edit:hover { background: #eef1f4; box-shadow: inset 0 0 0 1px var(--line); }
.v.auto { background: var(--s-auto-bg); color: var(--s-auto-ink);
  box-shadow: inset 0 0 0 1px var(--s-auto-line); }
.v.error { background: var(--s-error-bg); color: var(--s-error-ink);
  font-weight: 600; box-shadow: inset 0 0 0 1px var(--s-error-line); }
.editing { background: #fff !important; outline: 2px solid var(--accent);
  outline-offset: 1px; box-shadow: none !important; -webkit-line-clamp: unset;
  white-space: normal; overflow: visible; }

/* The unit is chosen, not typed: Onsite refuses the whole file over one it
   cannot match, and offers its list only after the upload has failed. It fills
   its track and no more — left to size itself it took the description's room. */
.u { width: 100%; max-width: 100%; font: inherit; font-size: 12.5px;
  color: var(--ink); padding: 1px 4px; border: 1px solid transparent;
  border-radius: 4px; background: transparent; }
.u:hover { border-color: var(--line); background: #fff; }
.u.error { background: var(--s-error-bg); color: var(--s-error-ink);
  font-weight: 600; border-color: var(--s-error-line); }
.u.auto { background: var(--s-auto-bg); color: var(--s-auto-ink);
  border-color: var(--s-auto-line); }

.tag { padding: 1px 7px; border-radius: 4px; background: #eef1f4; color: #7b828c;
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .04em;
  white-space: nowrap; }
.tag[data-full] { cursor: help; }
.tag.warn { background: var(--s-auto-bg); color: var(--s-auto-ink);
  border: 1px solid var(--s-auto-line); }
.tag.bad { background: var(--s-error-bg); color: var(--s-error-ink);
  border: 1px solid var(--s-error-line); text-transform: none;
  letter-spacing: 0; font-size: 11.5px; white-space: normal; }

/* Their rows, under ours, only when asked for. */
.theirs-rows { grid-column: 3 / -1; margin-top: 8px; padding: 8px 10px;
  border-left: 2px solid var(--line); background: #fbfcfd; border-radius: 0 6px 6px 0; }
.their-row { font-size: 11.5px; color: var(--muted); display: flex;
  flex-wrap: wrap; gap: 2px 12px; padding: 2px 0; }
.their-row b { color: #a3a9b3; font-weight: 400; min-width: 26px;
  font-variant-numeric: tabular-nums; }
.their-row i { font-style: normal; color: #b3b8c0; margin-right: 4px; }

/* Which of their columns feeds what. Out of the way until wanted — it is
   checked once and then not thought about again. */
.mapping { border-bottom: 1px solid var(--line); }
.mapping summary { padding: 11px 14px; cursor: pointer; font-size: 13px;
  color: var(--muted); }
.mapping summary b { color: var(--ink); }
.mapping .cols { display: inline-block; padding: 2px 8px; margin-right: 8px;
  border: 1px solid var(--line); border-radius: 6px; font-size: 12px; }
.map-body { display: flex; flex-wrap: wrap; gap: 10px 18px; padding: 4px 14px 14px; }
.map-one { display: flex; flex-direction: column; gap: 3px; font-size: 12px;
  color: var(--muted); }
.map-one > span { font-weight: 500; color: var(--ink); }
.map-one i { font-style: normal; color: var(--muted); font-weight: 400; }
.map-one .req { color: var(--bad); }
.map-one.missing-required > span { color: var(--bad); }
.map-pick, .map-pick-col { font: inherit; font-size: 12.5px; padding: 4px 6px;
  border: 1px solid var(--line); border-radius: 6px; background: #fff;
  max-width: 210px; }
.unused-note { flex-basis: 100%; margin: 4px 0 0; font-size: 12.5px;
  color: var(--muted); display: flex; align-items: center; gap: 8px;
  flex-wrap: wrap; }
.unused-col { display: inline-flex; align-items: center; gap: 6px;
  background: #fff; border: 1px solid var(--line); border-radius: 7px;
  padding: 3px 4px 3px 9px; font-size: 12.5px; }

/* Their file, beside ours. Real columns of the same table, so the headings sit
   over the values and the halves cannot drift apart however long a description
   runs — nothing to scroll-sync and get wrong. Rendered only when asked for,
   so there is no display toggle here. */
table.grid.compare-on td.compare, table.grid.compare-on th.compare {
  background: #fcfcfd; vertical-align: top; font-size: 12px;
  color: var(--muted); max-width: 240px; }
table.grid.compare-on .c-theirs { border-left: 2px solid var(--line); }
table.grid.compare-on th.c-theirs { border-left: 2px solid var(--line); }
/* One line per row of their file that went into this line, stacked in place so
   a merge is visible as a merge. */
table.grid.compare-on td.compare span { display: block; padding: 4px 10px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
table.grid.compare-on td.c-theirs span { text-align: right; color: #a3a9b3;
  font-variant-numeric: tabular-nums; }

/* Columns of theirs feeding nothing. Named rather than shown, because the
   outline is what we are about to send — but named loudly, since a column we
   failed to recognise is otherwise invisible. */
.note.unused { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.unused-col { display: inline-flex; align-items: center; gap: 6px;
  background: var(--card); border: 1px solid var(--line); border-radius: 7px;
  padding: 3px 4px 3px 9px; font-size: 12.5px; }
.unused-col select { font-size: 12px; max-width: 150px; }

/* Selecting lines to restructure. The bar only exists while something is
   selected — a permanent toolbar of actions that need a selection reads as
   broken every time you look at it without one. */
.tree tr.picked > td:not(.c-state) { box-shadow: inset 0 0 0 1px var(--accent); }
/* Floating, not in the flow: appearing in the flow pushed the whole table
   down the moment you ticked something, so the row you had just picked moved
   out from under the pointer. */
.selection-bar { position: fixed; bottom: 22px; left: 50%;
  transform: translateX(-50%); z-index: 60; display: flex;
  align-items: center; gap: 8px; flex-wrap: wrap; max-width: calc(100vw - 60px);
  padding: 9px 14px; border-radius: 10px; background: var(--ink); color: #fff;
  box-shadow: 0 8px 30px rgba(0,0,0,.3); font-size: 13px; }
.selection-bar[hidden] { display: none; }
.selection-bar .count { color: #fff; }
.selection-bar .label { color: #b9bfc9; font-size: 12.5px; }
.selection-bar .sep { width: 1px; height: 18px; background: #3a4049; }
.selection-bar button { background: #2b313a; border: 1px solid #3f4650;
  color: #fff; border-radius: 7px; padding: 5px 10px; font-size: 12.5px; }
.selection-bar button:hover { background: #39414c; border-color: #556; }
.selection-bar button.clear { background: none; border-color: transparent;
  color: #b9bfc9; margin-left: auto; }

/* The rest of a clamped cell. Dark, roomy and immediate — a BOQ description is
   two or three sentences and needs to be read, not squinted at. */
.tip { position: fixed; z-index: 100; max-width: 520px; padding: 10px 13px;
  border-radius: 8px; background: var(--ink); color: #fff; font-size: 12.5px;
  line-height: 1.55; box-shadow: 0 8px 30px rgba(0,0,0,.28);
  pointer-events: none; white-space: pre-wrap; }
.tip[hidden] { display: none; }
[data-full] { cursor: help; }

/* Where a BOQ is going, beside the steps rather than buried in the table.
   Not a "BOQ name" — Onsite has no such object; a BOQ is the billing
   activities of a work order, and that is what gets chosen. */
/* Side by side: project then BOQ, left to right in the order they are chosen.
   Wrapping put one under the other and made them look unrelated. */
.target { display: flex; align-items: center; gap: 8px; flex-wrap: nowrap;
  font-size: 13px; margin-left: 18px; min-width: 0; }
.target select { flex: 0 1 auto; min-width: 0; }
.target select:disabled { background: var(--bg); color: #b3b8c0; }
.target select { font: inherit; font-size: 12.5px; padding: 5px 8px;
  border: 1px solid var(--line); border-radius: 8px; background: #fff;
  max-width: 260px; color: var(--ink); }
.target.unset select[name="workorder_id"] { border-color: var(--s-auto-line);
  background: var(--s-auto-bg); }
.target .why { font-size: 12px; color: var(--muted); }
.target .why.bad { color: var(--bad); }
.target .why.ok { color: var(--good); }

/* Units Onsite will refuse, grouped by what the file says. Fifty lines saying
   MT is one decision, not fifty — and remembering it means the next file from
   this client needs no decision at all. */
.unit-fixes { padding: 12px 14px; border-bottom: 1px solid var(--line);
  background: var(--s-error-bg); }
.unit-fixes .lead { margin: 0 0 9px; font-size: 13px; color: var(--ink); }
.unit-fix { display: flex; align-items: center; gap: 8px; margin-top: 6px;
  font-size: 12.5px; }
.unit-fix .was { font-weight: 600; color: var(--bad); min-width: 84px;
  background: #fff; border: 1px solid var(--s-error-line); border-radius: 5px;
  padding: 2px 8px; }
.unit-fix .count { color: var(--muted); min-width: 66px; }
.unit-fix .arrow { color: var(--muted); }
.unit-fix select { font: inherit; font-size: 12.5px; padding: 4px 7px;
  border: 1px solid var(--line); border-radius: 6px; background: #fff;
  min-width: 130px; }
.unit-fix .remember { color: var(--muted); display: flex; align-items: center;
  gap: 4px; cursor: help; }
.unit-fix button { font: inherit; font-size: 12.5px; padding: 4px 12px;
  border: 1px solid var(--ink); border-radius: 6px; background: var(--ink);
  color: #fff; }

/* A line left out of the upload. Still here, still restorable — the file is
   untouched and Onsite cannot delete what has already been sent. */
.line.excluded > * { opacity: .5; }
.line.excluded .desc { text-decoration: line-through; }
.line.excluded .extras { opacity: 1; }
.tag.out { background: #eceff3; color: #6b7280; border: 1px solid var(--line); }
.linkish { background: none; border: 0; color: var(--accent); font: inherit;
  font-size: 12px; padding: 0 2px; cursor: pointer; text-decoration: underline; }
.selection-bar button.danger { background: #4a2320; border-color: #6b3430; }
.selection-bar button.danger:hover { background: #5d2b27; }

/* Openers for the panels that used to sit above the outline. Their counts ride
   on the button, so the problems are visible without opening anything. */
.topline-actions .ghost { display: inline-flex; align-items: center; gap: 6px; }
.topline-actions .ghost b { color: var(--ink); font-variant-numeric: tabular-nums; }
.topline-actions .ghost em { font-style: normal; font-size: 11px;
  color: var(--muted); }
.topline-actions .ghost.bad { border-color: var(--s-error-line);
  background: var(--s-error-bg); color: var(--bad); }
.topline-actions .ghost.bad b { color: var(--bad); }

/* Inside the dialog the mapping pickers get room to lay out in a grid rather
   than a wrapped row. */
.modal-card .map-body { display: grid; padding: 0;
  grid-template-columns: repeat(auto-fill, minmax(210px, 1fr)); gap: 12px 18px; }
.modal-card .unused-note { grid-column: 1 / -1; }
.modal-card .unit-fix { margin-top: 8px; }
.modal-card .unit-fix select { flex: 1 1 auto; min-width: 160px; }

/* --- The wide table: what stays put while you scroll ----------------------
   Appended deliberately at the end. The review rules above exist in three
   near-identical copies from earlier edits, and the last one wins — so a fix
   written into one of them may or may not take effect depending which. This
   block is last, and therefore certain.

   Two things were wrong. Sticking every `th` pinned both header rows to the
   same line, so the mapping pickers sat on top of the column names; sticking
   the `thead` instead keeps the pair together. And the frozen left-hand
   columns still assumed the layout from before the select-all checkbox was
   added, so they overlapped each other once a wide file was scrolled
   sideways. */
main.wide table.grid thead { position: sticky; top: 0; z-index: 3; }
main.wide table.grid thead th { position: static; }

main.wide table.grid .c-pick,
main.wide table.grid .c-num,
main.wide table.grid .c-state {
  position: sticky; z-index: 1; background: var(--card);
}
main.wide table.grid .c-pick  { left: 0; }
main.wide table.grid .c-num   { left: 30px; }
main.wide table.grid .c-state { left: 76px; }
main.wide table.grid thead .c-pick,
main.wide table.grid thead .c-num,
main.wide table.grid thead .c-state { z-index: 4; background: var(--bg); }

/* The frozen cells have to carry the row's own colour, or a red row turns
   white for its first three columns as soon as it is scrolled past. */
main.wide tr.row-error .c-pick,
main.wide tr.row-error .c-num,
main.wide tr.row-error .c-state { background: #fffafa; }
main.wide tr.just-fixed .c-pick,
main.wide tr.just-fixed .c-num,
main.wide tr.just-fixed .c-state { background: var(--good-bg); }
main.wide tr.excluded .c-pick,
main.wide tr.excluded .c-num,
main.wide tr.excluded .c-state { background: var(--bg); }

/* --- The side panel, as Onsite draws it -----------------------------------
   A dark navy column with the company at the top, dashed rules between
   groups, and the open item boxed rather than merely tinted. Deliberately a
   cooler blue than the app's own indigo: this is the upload tool sitting
   beside Onsite, and looking *exactly* like it would suggest it is the same
   screen, which it is not.

   Appended at the end, where it is certain to win — the review rules above
   exist in three near-identical copies from earlier edits. */
:root {
  --side-bg: #17204a;          /* cooler and a shade lighter than Onsite's */
  --side-ink: #e8ecf7;
  --side-dim: #97a3c7;
  --side-line: rgba(255, 255, 255, .12);
  --side-on-bg: #223065;
  --side-on-line: #5b7cfa;
}

.body { align-items: stretch; }
.sidebar {
  width: 232px; padding: 16px 12px 24px;
  background: var(--side-bg); color: var(--side-ink);
  position: sticky; top: 0; align-self: stretch; min-height: 100vh;
}

.sidebar .side-label {
  color: var(--side-dim); margin: 18px 12px 6px;
  border-top: 1px dashed var(--side-line); padding-top: 14px;
}
.sidebar .side-label:first-of-type { border-top: 0; padding-top: 0; }

.sidebar li a,
.sidebar li .soon {
  display: block; padding: 9px 12px; border-radius: 9px;
  color: var(--side-ink); font-size: 14px; font-weight: 500;
  text-decoration: none; border: 1px solid transparent;
}
.sidebar li a:hover { background: rgba(255, 255, 255, .07); }
/* Boxed, not just filled: at a glance the outline is what reads as "you are
   here" down a column of similar words. */
.sidebar li a.on {
  background: var(--side-on-bg); border-color: var(--side-on-line);
  font-weight: 600;
}
.sidebar li .soon { color: var(--side-dim); }

/* The company control belongs to the dark panel now, not to the page. */
.sidebar .company-current {
  background: rgba(255, 255, 255, .06); border-color: var(--side-line);
  color: var(--side-ink);
}
.sidebar .company-current:hover { border-color: var(--side-on-line); }
.sidebar .company-current em { color: var(--side-dim); }

/* --- Side panel: icons, and no group headings -----------------------------
   The headings said Upload / Library / Settings above lists of two things
   each, which is three words explaining five. The dashed rule that separated
   them does the same job without them, and the icons carry the recognition
   the headings were standing in for. */
.sidebar .side-label { display: none; }
.sidebar .nav-group {
  border-top: 1px dashed var(--side-line); padding-top: 10px; margin-top: 10px;
}
.sidebar .nav-group:first-of-type { border-top: 0; padding-top: 0; margin-top: 14px; }

.sidebar li a,
.sidebar li .soon {
  display: flex; align-items: center; gap: 10px;
  padding: 9px 12px; font-size: 15px;
}
/* Onsite's are small — they sit beside the word rather than competing
   with it. 19px read as a row of buttons. */
.nav-icon { width: 16px; height: 16px; flex: none; opacity: .8; }
.sidebar li a.on .nav-icon { opacity: 1; }
.sidebar li .soon .nav-icon { opacity: .5; }
.sidebar li .soon em { margin-left: auto; }

/* --- Side panel: the size the words are actually read at ------------------
   16px with an 18px icon. The labels are the thing being scanned, so they
   lead: the icon sits beside the word at a size that supports it rather than
   competing, and every label starts at the same x however wide its icon
   draws — a flexed row will otherwise let a narrower glyph pull its text
   left and the column stops looking like a column. */
.sidebar li a,
.sidebar li .soon {
  justify-content: flex-start; text-align: left;
  font-size: 16px; gap: 12px; padding: 10px 12px;
}
.sidebar li a span,
.sidebar li .soon > span { flex: 1; min-width: 0; }
.nav-icon { width: 18px; height: 18px; }

/* --- The three steps should not move under you ----------------------------
   Two `main` rules were in play: an early one with `margin: 0 auto` from
   before there was a sidebar, and the flex one that replaced it. The auto
   margin survived, so Upload and Result were centred in whatever space was
   left while Review filled the width — and stepping between the three slid
   the whole page sideways.

   Left edge fixed for all three. Review is still allowed to be wider, because
   a table of thirty columns has to be, but it now grows to the right rather
   than dragging everything with it. */
.body main { margin-left: 0; margin-right: 0; max-width: 1040px; }
.body main.wide { max-width: none; }
.body main > .topline { margin-left: 0; }

/* --- An empty line still has to be clickable ------------------------------
   A BOQ heading whose name never came through renders an empty cell, and an
   empty `-webkit-box` is zero pixels tall — so the one line the screen was
   shouting about ("Every line needs a name") was the one line nobody could
   click to fix. The row looked editable and was not.

   A placeholder gives it both a hit area and a sentence saying what to do.
   `::before` content is not part of `textContent`, so nothing here can be
   saved as the value by accident. */
.desc.edit:empty,
.desc.edit:has(> br:only-child) {
  min-height: 1.5em; -webkit-line-clamp: none; display: block;
}
.desc.edit:empty::before {
  content: "Add a name for this line";
  color: var(--bad); font-weight: 500; opacity: .85;
}
.desc.edit.editing:empty::before { content: ""; }

/* The same for anything short and editable that happens to be blank: a dash
   is a target, an empty box is not. */
.line .unit:empty::before,
.line .num:empty::before { content: "—"; color: var(--muted); }

/* --- The mapping panel: one question, one direction -----------------------
   It used to be two blocks — Onsite's fields each picking one of their
   columns, then their leftover columns each picking an Onsite field. Opposite
   questions in identical furniture, so the bold label reads as the subject and
   the subject flipped halfway down the page; and it read as being asked the
   same thing twice, as though the second block were a double-check on the
   first rather than the columns the first had not accounted for.

   One list now, laid out left to right in the direction it is read: their
   column, an arrow, what it becomes. The arrow is what makes the direction
   impossible to mistake, so it is drawn rather than implied. */
.map-toolbar { display: flex; align-items: center; justify-content: space-between;
  gap: 12px; flex-wrap: wrap; padding: 2px 0 8px; font-size: 12.5px; }
.map-toolbar .bad { color: var(--bad); }
.map-toolbar .only-left { display: inline-flex; align-items: center; gap: 6px;
  color: var(--muted); cursor: pointer; }

.map-list { display: flex; flex-direction: column; }
.map-line { display: grid; grid-template-columns: minmax(0, 1fr) 18px 240px;
  align-items: center; gap: 10px; padding: 6px 2px;
  border-bottom: 1px solid var(--line); }
.map-line:last-child { border-bottom: 0; }
.map-line.head { border-bottom: 1px solid var(--ink); padding-bottom: 5px;
  font-size: 11px; letter-spacing: .06em; text-transform: uppercase;
  color: var(--muted); position: sticky; top: 0; background: #fff; z-index: 2; }
.map-line .from { display: flex; flex-direction: column; gap: 1px;
  min-width: 0; font-size: 12.5px; }
.map-line .from b { font-weight: 600; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.map-line .from i { font-style: normal; font-size: 11.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.map-line .from i.faint { opacity: .6; }
.map-line .arrow { color: var(--muted); text-align: center; font-size: 13px; }
.map-line select { width: 100%; font: inherit; font-size: 12.5px;
  padding: 5px 7px; border: 1px solid var(--line); border-radius: 6px;
  background: #fff; }

/* Undecided is the state that holds the upload, so it is the loud one — and
   it stays loud on the row, not only on the dropdown, because the row is what
   you scan down. */
.map-line.undecided { background: var(--s-error-bg); }
.map-line.undecided .from b { color: var(--bad); }
.map-line.undecided select { border-color: var(--bad); }
.map-line.settled select { color: var(--ink); }

/* Hiding the settled ones on a sixty-column file. The header row is not a
   decision and stays. */
.map-list.only-undecided .map-line.settled { display: none; }

/* What this mapping leaves empty, in front of the button that saves it. Not a
   warning to dismiss — a statement of what the file does not carry. */
.note.left-off { background: var(--s-auto-bg); border-radius: 8px;
  padding: 9px 12px; margin: 12px 0 0; font-size: 12.5px; line-height: 1.5; }

/* A company this account may not upload into. Shown, not hidden — a company
   that simply is not in the list reads as the tool being broken. */
.companies .company-pick.off { cursor: not-allowed; opacity: .55;
  background: transparent; border-style: dashed; }
.companies .company-pick.off:hover { border-color: var(--line); }
.company-menu .menu-note { margin: 4px 0 0; padding: 8px 10px;
  border-top: 1px solid var(--line); font-size: 11.5px; line-height: 1.45;
  color: var(--muted); }

/* Jumping to a page by the rows it holds. */
.pager .page-jump { display: inline-flex; align-items: center; gap: 6px;
  margin: 0 2px; }
.pager .page-jump select { font: inherit; font-size: 12.5px; padding: 3px 6px;
  border: 1px solid var(--line); border-radius: 6px; background: #fff;
  color: var(--ink); max-width: 230px; }
.pager .page-jump button { font: inherit; font-size: 12.5px; padding: 3px 9px; }
.sr-only { position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap;
  border: 0; }

/* --- The mapping panel, second pass ---------------------------------------
   Onsite's field on the left and their column on the right: the row reads
   "Item Code ← A · Material name", which is the direction people check in —
   they know what Onsite wants and are looking for where it comes from.

   Everything above the list is one sticky block. On a sixty-column file the
   Save button sat a thousand pixels down, and so did the sentence saying what
   the mapping leaves empty; a statement you have to scroll back to is not one
   you read before pressing the button. */
#columns-modal .modal-card { padding: 0; }
#columns-modal .map-sticky { position: sticky; top: 0; z-index: 4;
  background: var(--card); padding: 18px 24px 0;
  border-radius: 12px 12px 0 0; border-bottom: 1px solid var(--line); }
#columns-modal .map-list { padding: 0 24px 20px; }
#columns-modal .modal-head { display: flex; align-items: flex-start;
  justify-content: space-between; gap: 20px; cursor: move;
  touch-action: none; }
#columns-modal .modal-head .head-text { min-width: 0; }
#columns-modal .modal-head h2 { margin: 0 0 6px; }
#columns-modal .modal-head .note { margin: 0; }
#columns-modal .modal-actions { margin: 0; flex: none; }
#columns-modal .map-line.head { position: static; }
#columns-modal .note.left-off { margin: 10px 0 0; }
#columns-modal .map-toolbar { padding-bottom: 6px; }
/* Dragged out of the way. The dialog is tall enough to cover the table it is
   describing, and shoving it aside beats closing and reopening it. */
#columns-modal .modal-card.moved { margin: 0; }

/* --- The BOQ outline: every Onsite column ---------------------------------
   The outline used to draw only the fields this file happened to fill, with
   Item code, GST Percent, HSN Code, Cost Code and Notes as tags beneath the
   description — and only once they had a value. So a BOQ missing its HSN
   codes had nowhere to type one, and a column Onsite wants was invisible on
   the screen where you fill it in.

   Every column now gets a track. The line grows past the width of the card and
   the outline scrolls sideways, which is the trade: a roomier description is
   worth less than a column you can use. */
.outline { overflow-x: auto; }
.line { grid-template-columns:
    56px 96px minmax(240px, 1fr) 104px 92px 108px 128px 118px
    var(--extra-cols, ); 
  min-width: var(--line-min, auto); }
.line > .extra-col { font-size: 12.5px; color: var(--ink); min-width: 0;
  overflow: hidden; text-overflow: ellipsis; }
.line.head > .extra-col { color: var(--ink); }
.line > .extra-col.edit { cursor: pointer; border-radius: 5px;
  padding: 1px 4px; margin: -1px -4px; }
.line > .extra-col.edit:hover { background: var(--bg);
  box-shadow: inset 0 0 0 1px var(--line); }
/* An empty column still has to be clickable, or drawing it achieves nothing. */
.line > .extra-col.edit:empty::before { content: "—"; color: var(--muted); }
.line > .extra-col.auto { background: var(--s-auto-bg); }
.line > .extra-col.error { background: var(--s-error-bg); color: var(--bad); }
/* The description and its overflow stop before the new columns rather than
   running under them, so the tags do not sit beneath a rate. */
.line > .extras, .line > .theirs-rows { grid-column: 3 / 9; }

/* GST beside the rate it is charged on, rather than out past the amount. One
   track fewer than before: their Amount folded into ours. */
.line { grid-template-columns:
    56px 96px var(--desc, minmax(240px, 1fr)) 104px 92px 108px 84px 128px
    var(--extra-cols, );
  min-width: var(--line-min, auto); }
.line > .extras, .line > .theirs-rows { grid-column: 3 / 9; }

/* A tree with no money in it. Tasks have no rate, no GST and no amount, so
   those three tracks are not drawn — and the grid has to stop expecting them
   or every column past Qty sits one place to the left of its heading. */
.outline.unpriced .line { grid-template-columns:
    56px 96px var(--desc, minmax(240px, 1fr)) 92px 104px
    var(--extra-cols, );
  min-width: var(--line-min, auto); }
.outline.unpriced .line > .extras,
.outline.unpriced .line > .theirs-rows { grid-column: 3 / 6; }

/* --- The BOQ outline: one treatment per role ------------------------------
   The row had grown four font sizes and three colours across it — 10.5px grey
   caps for UNIT/QTY/RATE, 12px dark for Item, 13.5px for Amount, 12.5px for
   the columns added later — so it read as several tables stitched together
   rather than one. Values are values; headings are headings. */
.line > .serial, .line > .desc, .line > .unit, .line > .num,
.line > .amount, .line > .extra-col { font-size: 13px; }
.line > .serial { color: var(--ink); }
.line > .extra-col { color: var(--ink); }

/* Every heading identical, and on one line, so they share a baseline however
   the tracks are sized. */
.line.head > .serial, .line.head > .desc, .line.head > .unit,
.line.head > .num, .line.head > .amount, .line.head > .extra-col {
  font-size: 10.5px; font-weight: 600; color: var(--muted);
  text-transform: uppercase; letter-spacing: .05em; align-self: center;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.line.head { align-items: center; }
.line.head > .serial[data-tip], .line.head > .amount[data-tip] { cursor: help; }

/* A cell with something wrong with it. The problem is stated on the value it
   is about, not under the item name — clicking opens the panel with the
   message and the ways out. */
.line .fixable { cursor: pointer; position: relative; }
/* Opens the same picker, but nothing is wrong, so it gets no mark. */
.line .picks { cursor: pointer; }

/* --- Adding a party from the fix panel ----------------------------------- */
.make-here { margin-top: 16px; padding-top: 14px; border-top: 1px solid var(--line); }
.make-here-head { font-size: 12.5px; color: var(--muted); margin-bottom: 10px; }
.make-here .make-new { display: flex; flex-wrap: wrap; gap: 10px; align-items: flex-end; }
.make-here .make-new label { display: flex; flex-direction: column; gap: 4px;
  font-size: 12px; color: var(--muted); }
.make-here .make-new select { padding: 7px 9px; }
.make-here .make-go { white-space: nowrap; }
.make-here .more { margin-top: 10px; }
.make-here .more summary { font-size: 12.5px; color: var(--muted); cursor: pointer; }
.make-here .more-fields { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 10px; margin-top: 10px; }

/* --- Usage, for super users ---------------------------------------------- */
.usage-filters { display: flex; flex-wrap: wrap; gap: 18px; margin: 14px 0 0; }
.usage-filters .group { display: flex; flex-wrap: wrap; gap: 6px; }
.usage-filters .chip { font-size: 12.5px; padding: 4px 10px; border-radius: 999px;
  border: 1px solid var(--line); color: var(--muted); text-decoration: none; }
.usage-filters .chip.on { background: var(--ink); border-color: var(--ink);
  color: #fff; }
.usage-totals { display: flex; flex-wrap: wrap; gap: 28px; margin: 20px 0 6px;
  padding: 14px 0; border-top: 1px solid var(--line);
  border-bottom: 1px solid var(--line); }
.usage-totals div { display: flex; flex-direction: column; gap: 2px; }
.usage-totals dt { font-size: 12px; color: var(--muted); }
.usage-totals dd { margin: 0; font-size: 20px; font-weight: 600;
  font-variant-numeric: tabular-nums; }
table.usage { width: 100%; border-collapse: collapse; margin-top: 14px;
  font-size: 13px; }
table.usage th { text-align: left; font-weight: 600; color: var(--muted);
  font-size: 12px; padding: 6px 12px 6px 0;
  border-bottom: 1px solid var(--line); white-space: nowrap; }
table.usage td { padding: 8px 12px 8px 0; vertical-align: top;
  border-bottom: 1px solid var(--line); }
table.usage .num { text-align: right; font-variant-numeric: tabular-nums;
  white-space: nowrap; }
table.usage .num.low { color: var(--bad); }
table.usage .mono { font-variant-numeric: tabular-nums; color: var(--muted); }
table.usage .packs { color: var(--muted); font-size: 12px; }
table.usage em.sub { display: block; font-style: normal; font-size: 12px;
  color: var(--muted); }

/* --- The blank-file offer on step 1 ---------------------------------------
   Folded shut. Most people arrive with a file already and the mapping stage
   reads it; opening with a template would teach them they have to rebuild it,
   which is the work this tool removes. */
/* The per-row menu on the uploads list. A <details> rather than a scripted
   popover: it opens, closes and is reachable from the keyboard on its own, and
   a menu that only works once a script has run is a menu that sometimes does
   not. */
.row-actions { white-space: nowrap; }
.rowmenu { display: inline-block; position: relative; margin-left: 10px; }
.rowmenu > summary { cursor: pointer; list-style: none; color: var(--muted);
  padding: 0 6px; border-radius: 5px; line-height: 1.4; user-select: none; }
.rowmenu > summary::-webkit-details-marker { display: none; }
.rowmenu > summary::marker { content: ""; }
.rowmenu > summary:hover { background: var(--line); color: inherit; }
.rowmenu[open] > summary { background: var(--line); color: inherit; }
.rowmenu-panel { position: absolute; right: 0; top: 100%; z-index: 20;
  margin-top: 3px; padding: 4px; background: var(--card);
  border: 1px solid var(--line); border-radius: 7px;
  box-shadow: 0 3px 10px rgba(0, 0, 0, .10); }
/* Quiet until you mean it. A solid red block for one row of housekeeping read
   as a warning about something that had already gone wrong, and at that size
   it was the loudest thing on a screen whose real subject is the list. Grey
   with red lettering says the same thing at the weight it deserves. */
.rowmenu-panel button { width: auto; white-space: nowrap;
  background: var(--bg); color: var(--bad); border: 1px solid var(--line);
  font-size: 12.5px; font-weight: 600; padding: 4px 12px; border-radius: 5px; }
.rowmenu-panel button:hover { background: var(--bad-bg);
  border-color: #f2cecb; opacity: 1; }

details.template { margin: 18px 0 0; border-top: 1px solid var(--line);
  padding-top: 14px; }
details.template > summary { cursor: pointer; font-weight: 600;
  font-size: 13.5px; color: var(--ink); }
details.template > summary::marker { color: var(--muted); }
details.template .why { color: var(--muted); font-size: 12.5px;
  line-height: 1.5; max-width: 62ch; margin: 8px 0; }
table.template-cols { width: 100%; border-collapse: collapse;
  margin-top: 10px; font-size: 12.5px; }
table.template-cols th { text-align: left; font-weight: 600;
  color: var(--muted); border-bottom: 1px solid var(--line);
  padding: 5px 10px 5px 0; }
table.template-cols td { padding: 5px 10px 5px 0; vertical-align: top;
  border-bottom: 1px solid var(--line); }
table.template-cols td.req { color: var(--bad); font-weight: 600; }
/* An Onsite id, shown only where nothing else can answer "where did it go". */
code.ref { font-size: 12px; color: var(--muted); word-break: break-all; }
/* What the file said, beneath what we put in its place. Drawn from an
   attribute so it is not a child node: these cells are edited in place, and
   anything inside them would be read back as part of the value.

   `order` rather than `::after`, which the fixable dot already owns — and the
   caption has to sit under the value, not above it. */
.line > .extra-col[data-was] { display: flex; flex-direction: column;
  justify-content: center; gap: 1px; }
.line > .extra-col[data-was]::before { content: "was " attr(data-was);
  order: 2; font-size: 11px; line-height: 1.25; color: var(--muted);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
/* The list under this is the catalogue, not a ranking. Saying so where the
   list is, rather than in a note below it that is read afterwards if at all. */
.choices-note { margin: 0 0 8px; font-size: 12.5px; color: var(--muted);
  line-height: 1.45; }
.line .v.error, .line > .extra-col.error, .line > .desc.error {
  background: var(--s-error-bg); color: var(--bad); border-radius: 5px; }
.line .fixable::after { content: ""; position: absolute; right: 2px; top: 2px;
  width: 5px; height: 5px; border-radius: 50%; background: var(--bad); }
.line > .desc.fixable::after { right: -2px; }

/* The mapping row under the BOQ's headings — the same control the flat grid
   puts under each column, so correcting one column no longer means opening a
   dialog over the table you were reading it from. */
.line.head.map-row { padding-top: 0; padding-bottom: 8px;
  border-bottom: 1px solid var(--line); align-items: start; }
.line.head.map-row .map-pick { width: 100%; min-width: 0; font-size: 11.5px;
  padding: 3px 4px; }
.line.head.map-row > div { min-width: 0; }

/* --- Names your file has and Onsite does not ------------------------------
   Grouped by the name as written: twelve payments to one unknown party is one
   decision. Onsite's near-matches come first and "create" comes last, because
   people do not make duplicate parties out of laziness — they make them
   because nothing showed them the 92% match.

   Read top to bottom in the order the decision is made: what the file says,
   what Onsite already has, then the way to add one — and Done at the end,
   where you are when you have finished, rather than at the top where it was
   the first thing under the sentence explaining the panel. */
#make-modal .modal-card { padding: 0; display: flex; flex-direction: column; }
.make-top { padding: 20px 24px 14px; border-bottom: 1px solid var(--line); }
.make-top h2 { margin: 0 0 6px; }
.make-top .note { margin: 0; }

.make-list { padding: 0 24px; }
.make-one { padding: 16px 0; border-bottom: 1px solid var(--line); }
.make-one:last-child { border-bottom: 0; }

/* The name as their file writes it, and how much of the file is waiting on it. */
.make-head { display: flex; align-items: baseline; gap: 10px; }
.make-head b { font-size: 15px; color: var(--ink); }
.make-head .count { font-size: 12px; color: var(--muted); }

/* The two ways out, each named and each given the same weight, so the choice
   reads as a choice rather than as a form with a link beside it. */
.make-way { margin-top: 10px; }
.make-way .lead { display: block; font-size: 11px; letter-spacing: .05em;
  text-transform: uppercase; color: var(--muted); margin-bottom: 6px; }
.make-none { margin: 10px 0 0; font-size: 12.5px; color: var(--muted); }

.make-matches { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; }
.make-matches form { display: inline; }
.make-matches .match { background: #fff; border: 1px solid var(--line);
  border-radius: 7px; padding: 5px 11px; font-size: 12.5px; color: var(--ink);
  cursor: pointer; }
.make-matches .match:hover { border-color: var(--accent); color: var(--accent); }
.make-matches .match em { font-style: normal; color: var(--muted);
  font-size: 11px; margin-left: 7px; }

/* The create row: everything on one line, the button ending it, and the
   optional fields folded away underneath rather than wedged in the middle. */
.make-new { display: flex; align-items: center; flex-wrap: wrap; gap: 10px;
  font-size: 12.5px; }
.make-new > label { display: inline-flex; align-items: center; gap: 7px;
  color: var(--muted); white-space: nowrap; }
.make-new select, .make-new input { font: inherit; font-size: 12.5px;
  padding: 5px 8px; border: 1px solid var(--line); border-radius: 6px;
  background: #fff; }
.make-new select { min-width: 170px; }
.make-go { font-size: 12.5px; padding: 6px 14px; margin: 0; }
.make-new .more { flex-basis: 100%; font-size: 12px; }
.make-new .more summary { cursor: pointer; color: var(--accent);
  list-style: none; }
.make-new .more summary::before { content: "\25b8 "; }
.make-new .more[open] summary::before { content: "\25be "; }
.make-new .more-fields { display: flex; flex-wrap: wrap; gap: 10px;
  margin-top: 8px; }
.make-new .more-fields label { display: inline-flex; align-items: center;
  gap: 6px; color: var(--muted); white-space: nowrap; }
.make-new .more-fields input { width: 150px; }

.make-foot { display: flex; justify-content: flex-end;
  padding: 14px 24px 18px; border-top: 1px solid var(--line); }

/* "None of these." The panel pre-selects its best candidate, so a 94% guess
   applied itself with no way back out — you could pick a different record but
   not say the value is nobody's. Set apart from the candidates, because it is
   not one of them. */
.choices .none-of-these { border-top: 1px dashed var(--line); margin-top: 6px;
  padding-top: 6px; }
.choices .none-of-these .name { color: var(--muted); }
.choices .none-of-these input:checked ~ .name { color: var(--ink); }
.choices .none-of-these .tag { background: var(--s-auto-bg); color: var(--ink); }

/* The country beside the number, not inside it. One box meant a Dubai user
   typed +971… and it was sent as an Indian number. */
.phone { display: flex; gap: 8px; align-items: stretch; }
/* Narrow now that it reads "🇦🇪 +971" rather than the country spelled out —
   the number is the thing being typed and should have the room. */
.phone select { flex: 0 0 auto; width: 7.5em; font: inherit; font-size: 14px;
  padding: 9px 8px; border: 1px solid var(--line); border-radius: 8px;
  background: #fff; color: var(--ink); }
.phone input { flex: 1 1 auto; min-width: 0; margin: 0; }

/* Working in a company you are not a member of. Loud on purpose and on every
   screen: the realistic failure is not malice, it is forgetting whose data is
   on the screen. */
.on-behalf { display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  margin: 0 0 16px; padding: 10px 14px; border-radius: 8px;
  background: var(--s-auto-bg); border: 1px solid #e6d8a8;
  font-size: 13px; color: var(--ink); }
.on-behalf a { margin-left: auto; }
.open-by-id { margin-top: 22px; padding-top: 18px;
  border-top: 1px solid var(--line); }
.open-by-id h2 { font-size: 15px; margin: 0 0 6px; }
.open-by-id p { margin: 0 0 10px; font-size: 12.5px; line-height: 1.5; }
.open-by-id input { width: 100%; max-width: 420px; font: inherit;
  padding: 8px 10px; border: 1px solid var(--line); border-radius: 8px; }
.open-by-id button { margin-top: 10px; }

/* Opening a company by id, in the switcher — which is where companies are
   actually changed. The picker page carries the same form, but nobody returns
   to it once they are inside a company. */
.company-menu .menu-open-by-id { padding: 10px; border-top: 1px solid var(--line);
  display: flex; flex-direction: column; gap: 6px; }
.company-menu .menu-open-by-id label { font-size: 11px; color: var(--muted);
  text-transform: uppercase; letter-spacing: .05em; }
.company-menu .menu-open-by-id input { font: inherit; font-size: 12.5px;
  padding: 6px 8px; border: 1px solid var(--line); border-radius: 6px;
  background: #fff; }
.company-menu .menu-open-by-id button { margin: 0; font-size: 12.5px;
  padding: 6px 10px; }

/* --- Bank reconciliation --------------------------------------------------
   Two sides and the number between them, above the rows they are about: "is
   this statement right?" is answered by the arithmetic, and the table below is
   only how you act on the answer. */
.recon-card { padding: 18px 20px; }
.recon-head { display: flex; align-items: baseline; justify-content: space-between;
  gap: 14px; flex-wrap: wrap; margin-bottom: 14px; }
.recon-head h2 { margin: 0; font-size: 16px; }
.recon-head h2 em { font-style: normal; font-weight: 400; font-size: 12.5px;
  color: var(--muted); margin-left: 8px; }
.recon-head h2 em.agrees { color: var(--good); }
.recon-head .verdict { font-size: 13px; font-weight: 600; padding: 4px 12px;
  border-radius: 999px; }
.recon-head .verdict.good { background: var(--good-bg); color: var(--good); }
.recon-head .verdict.bad { background: var(--s-error-bg); color: var(--bad); }

.recon-sides { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.recon-sides .side { border: 1px solid var(--line); border-radius: 9px;
  padding: 12px 14px; }
.recon-sides h3 { margin: 0 0 8px; font-size: 12px; text-transform: uppercase;
  letter-spacing: .05em; color: var(--muted); }
.recon-sides h3 span { text-transform: none; letter-spacing: 0; font-weight: 400; }
.recon-sides dl { display: grid; grid-template-columns: 1fr auto; gap: 4px 10px;
  margin: 0; font-size: 13px; }
.recon-sides dt { color: var(--muted); }
.recon-sides dt.bad { color: var(--bad); }
.recon-sides dt.warn { color: var(--s-auto-ink); }
.recon-sides dd { margin: 0; text-align: right; font-variant-numeric: tabular-nums;
  color: var(--ink); }

/* What a statement's columns do, said once above the table they head. */
.note.reads-from { margin: 0 0 12px; font-size: 12.5px; }
.note.reads-from em { font-style: normal; background: var(--code-bg, #eef2f6);
  padding: 0 4px; border-radius: 3px; }
/* Said under a heading that is filled in rather than read: what from. Kept
   quiet — it is reassurance, not an instruction. */
table.grid th em.derived { display: block; font-style: normal; font-weight: 400;
  font-size: 10.5px; line-height: 1.35; color: var(--muted);
  text-transform: none; letter-spacing: 0; margin-top: 2px;
  max-width: 210px; white-space: normal; }

/* The two piles carried rather than corrected. Counted in the sides above and
   listed here, folded away: a number on its own cannot be checked, but on a
   clean month nobody opens them either. */
.recon-list { margin-top: 12px; border: 1px solid var(--line);
  border-radius: 9px; padding: 9px 12px; }
.recon-list + .recon-list { margin-top: 8px; }
.recon-list summary { cursor: pointer; font-size: 12.5px; color: var(--ink); }
.recon-list summary em { font-style: normal; float: right; color: var(--muted);
  font-variant-numeric: tabular-nums; }
.recon-list .note { margin: 8px 0 4px; font-size: 11.5px; }
.recon-entries { width: 100%; border-collapse: collapse; font-size: 12.5px;
  font-variant-numeric: tabular-nums; }
.recon-entries td { padding: 4px 6px; border-top: 1px solid var(--line);
  vertical-align: top; }
.recon-entries td.when { color: var(--muted); white-space: nowrap; width: 1%; }
.recon-entries td em { font-style: normal; color: var(--muted);
  margin-left: 6px; font-size: 11.5px; }
.recon-entries td.num { text-align: right; white-space: nowrap; }
.recon-entries td.way { color: var(--muted); width: 1%; padding-left: 10px; }

/* The proof, in the shape a reconciliation statement has always taken. */
.recon-proof { width: 100%; margin-top: 14px; border-collapse: collapse;
  font-size: 13px; font-variant-numeric: tabular-nums; }
.recon-proof td { padding: 5px 0; color: var(--muted); }
.recon-proof td:last-child { text-align: right; color: var(--ink); }
.recon-proof tr.sum td { border-top: 1px solid var(--line); font-weight: 600;
  color: var(--ink); }
.recon-proof tr.good td:last-child { color: var(--good); font-weight: 600; }
.recon-proof tr.bad td:last-child { color: var(--bad); font-weight: 600; }

/* A row nothing was done to, that somebody should still look at — a tax
   number another party already has, a name used twice. Distinct from "auto
   updated", which says we changed something: one asks you to check our work,
   the other to check yours. */
.filters a.on[href*="filter=warnings"] { background: var(--s-auto-bg);
  color: var(--s-auto-ink); border-color: #e6d8a8; }
tr.row-warn td.cell, .line.row-warn { background: #fffdf5; }
.status.warn { background: var(--s-auto-bg); color: var(--s-auto-ink); }

/* A statement line the matcher could not settle. Left unanswered it uploads
   as new and makes a second copy of a payment Onsite already has, so the
   question is asked plainly and an answer is one click. */
.recon-unsure { margin-top: 16px; border-top: 1px solid var(--line);
  padding-top: 14px; }
.recon-unsure h3 { margin: 0 0 10px; font-size: 12px; text-transform: uppercase;
  letter-spacing: .05em; color: var(--bad); }
.unsure-line { display: flex; align-items: flex-start; gap: 14px;
  flex-wrap: wrap; padding: 8px 0; border-bottom: 1px solid var(--line); }
.unsure-line:last-child { border-bottom: 0; }
.unsure-line .what { min-width: 190px; font-size: 12.5px; }
.unsure-line .what b { display: block; font-variant-numeric: tabular-nums; }
.unsure-line .what span { color: var(--muted); font-size: 11.5px; }
.unsure-line .picks { display: flex; gap: 6px; flex-wrap: wrap; }
.unsure-line .picks form { display: inline; }
.unsure-line .pick { background: #fff; border: 1px solid var(--line);
  border-radius: 7px; padding: 5px 11px; font-size: 12.5px; color: var(--ink);
  cursor: pointer; text-align: left; }
.unsure-line .pick:hover { border-color: var(--accent); color: var(--accent); }
.unsure-line .pick em { display: block; font-style: normal; font-size: 11px;
  color: var(--muted); }
.unsure-line .pick.none { color: var(--muted); border-style: dashed; }

/* --- Which sheet of the workbook ------------------------------------------
   A workbook's sheet names are frequently no help — one real diesel file here
   has 22 of them called `123`, `4216`, `SHUKLA` — so each option leads with
   its own column headings and row count. The name is the label; the headings
   are what the choice is actually made on. */
.sheet-pick .sheet-list { list-style: none; margin: 18px 0 0; padding: 0;
  display: flex; flex-direction: column; gap: 8px; }
.sheet-pick .sheet-list label { display: grid; gap: 2px 10px; cursor: pointer;
  grid-template-columns: auto 1fr auto; align-items: baseline;
  padding: 11px 14px; border: 1px solid var(--line); border-radius: 8px;
  background: var(--card); }
.sheet-pick .sheet-list label:hover { border-color: var(--accent); }
.sheet-pick .sheet-list input { margin: 0; }
.sheet-pick .sheet-name { font-weight: 600; }
.sheet-pick .sheet-rows { color: var(--muted); font-size: 12.5px;
  font-variant-numeric: tabular-nums; }
.sheet-pick .sheet-cols { grid-column: 2 / -1; color: var(--muted);
  font-size: 12.5px; overflow: hidden; text-overflow: ellipsis;
  white-space: nowrap; }
.sheet-pick .sheet-list input:checked + .sheet-name { color: var(--accent); }
.sheet-pick .sheet-list label:has(input:checked) { border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent); }
.sheet-pick .sheet-actions { display: flex; align-items: center; gap: 16px;
  margin-top: 18px; }

/* The same fact on the review screen: which sheet this is, and the way back
   to the picker. Reads as a chip beside Columns and Units because it is the
   same kind of thing — something decided about the file that can be redecided. */
.sheet-chip { text-decoration: none; }
.sheet-chip b { font-weight: 600; max-width: 190px; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; display: inline-block;
  vertical-align: bottom; }

/* --- Roles & Access -------------------------------------------------------
   Onsite's own Roles screen is 391 checkboxes across 26 sections, which is why
   every real company has one role called Admin and everybody on it. These two
   screens are the other way in, so they lead with the job and the consequence
   rather than with a grid. */
.role-picks{list-style:none;margin:20px 0 0;padding:0;display:grid;gap:10px;
  grid-template-columns:repeat(auto-fit,minmax(260px,1fr))}
.role-picks a{display:flex;flex-direction:column;gap:5px;height:100%;
  padding:14px 16px;border:1px solid var(--line);border-radius:9px;
  background:var(--card);text-decoration:none;color:inherit}
.role-picks a:hover{border-color:var(--accent)}
.role-name{font-weight:600;font-size:15.5px}
.role-sum{color:var(--muted);font-size:13.5px;line-height:1.5}
.role-meta{margin-top:auto;padding-top:6px;color:var(--muted);font-size:12.5px;
  display:flex;gap:7px;align-items:center;flex-wrap:wrap}
.role-cols{display:grid;gap:26px;grid-template-columns:repeat(auto-fit,minmax(260px,1fr));
  margin-top:20px}
.role-cols h2{font-size:13px;letter-spacing:.06em;text-transform:uppercase;margin:0 0 9px}
.role-can,.role-cannot{list-style:none;margin:0;padding:0;display:flex;
  flex-direction:column;gap:6px;font-size:14px}
.role-can li{display:flex;gap:8px;align-items:baseline;flex-wrap:wrap}
.role-cannot li{color:var(--muted)}
.risk-head{font-size:11.5px;letter-spacing:.07em;text-transform:uppercase;
  margin:14px 0 5px;font-weight:600}
.risk-head:first-of-type{margin-top:0}
em.risk-money,em.risk-approval,em.risk-people,em.risk-destructive,em.risk-settings{
  font-style:normal;font-size:11px;font-weight:600;letter-spacing:.04em;
  text-transform:uppercase;padding:1px 6px;border-radius:4px}
.risk-money,em.risk-money{color:var(--warn)}
em.risk-money{background:var(--warn-bg)}
.risk-approval,em.risk-approval{color:#6b3fa0}
em.risk-approval{background:#f4eefb}
.risk-people,em.risk-people{color:var(--good)}
em.risk-people{background:var(--good-bg)}
.risk-destructive,em.risk-destructive{color:var(--bad)}
em.risk-destructive{background:var(--bad-bg)}
.risk-settings,em.risk-settings{color:var(--muted)}
em.risk-settings{background:var(--line)}
.role-whose{margin-top:22px;padding-top:16px;border-top:1px solid var(--line)}
.role-whose h2{font-size:13px;letter-spacing:.06em;text-transform:uppercase;margin:0 0 8px}
.role-whose ul{margin:0;padding-left:20px;font-size:14px;display:flex;
  flex-direction:column;gap:5px}
.role-make{display:flex;gap:10px;align-items:center;flex-wrap:wrap;margin-top:24px;
  padding-top:18px;border-top:1px solid var(--line)}
.role-make label{font-size:13px;color:var(--muted)}
.role-make input{padding:8px 11px;border:1px solid var(--line);border-radius:7px;
  font:inherit;font-size:14.5px;min-width:200px}
.role-say{display:flex;flex-direction:column;gap:8px;margin-top:20px;
  padding:16px;border:1px solid var(--line);border-radius:9px;background:var(--bg)}
.role-say label{font-size:13px;color:var(--muted)}
.role-say textarea{font:inherit;font-size:14.5px;padding:10px 12px;resize:vertical;
  border:1px solid var(--line);border-radius:7px;background:var(--card);color:var(--ink)}
.role-say button{align-self:flex-start}
.role-cant{margin-top:22px;padding-top:16px;border-top:1px solid var(--line)}
.role-cant h2{font-size:13px;letter-spacing:.06em;text-transform:uppercase;margin:0 0 9px}
.role-cant ul{margin:0;padding-left:20px;display:flex;flex-direction:column;gap:8px;
  font-size:14px}
.role-cant li span{display:block;color:var(--muted);font-size:13.5px;margin-top:2px}
.role-people{margin:18px 0 6px;padding:12px 15px;border-radius:8px;font-size:15px;
  background:var(--good-bg);color:var(--good);border:1px solid #cfe6d9}
.role-people.bad{background:var(--warn-bg);color:var(--warn);border-color:#f0dfb6}
.role-make select{padding:8px 11px;border:1px solid var(--line);border-radius:7px;
  font:inherit;font-size:14.5px;background:var(--card);color:var(--ink)}

/* --- Long lists, in parts ------------------------------------------------
   Nothing long renders flat. Each group carries its own count and the risk
   classes inside it, so a shut group still says whether it is worth opening. */
.count{display:inline-block;min-width:20px;padding:1px 6px;border-radius:20px;
  background:var(--line);color:var(--muted);font-size:11.5px;font-weight:600;
  text-align:center;font-variant-numeric:tabular-nums}
.folds{display:flex;flex-direction:column;gap:6px;margin-top:10px}
.role-fold{border:1px solid var(--line);border-radius:8px;background:var(--card)}
.role-fold>summary{cursor:pointer;padding:9px 13px;font-weight:600;
  font-size:14.5px;display:flex;align-items:center;gap:8px;flex-wrap:wrap;
  list-style:none}
.role-fold>summary::-webkit-details-marker{display:none}
.role-fold>summary::before{content:"›";color:var(--muted);font-weight:400;
  display:inline-block;transition:transform .12s}
.role-fold[open]>summary::before{transform:rotate(90deg)}
.role-fold>summary:hover{background:var(--bg)}
.role-fold>ul,.role-fold>.folds{padding:2px 14px 12px 28px;margin:0}
.role-fold .role-can li,.role-fold .role-cannot li{font-size:14px}
@media (prefers-reduced-motion:reduce){.role-fold>summary::before{transition:none}}

/* The verdict: five words that answer "is this dangerous". */
.verdict{list-style:none;display:flex;flex-wrap:wrap;gap:8px;margin:18px 0 6px;
  padding:0}
.verdict li{display:flex;flex-direction:column;gap:1px;padding:8px 13px;
  border:1px solid var(--line);border-radius:8px;background:var(--card);min-width:118px}
.verdict .v-word{font-size:11px;letter-spacing:.06em;text-transform:uppercase;
  color:var(--muted);font-weight:600}
.verdict .v-ans{font-weight:600;font-size:14.5px}
.verdict li.no .v-ans{color:var(--muted);font-weight:400}
.verdict li.yes{border-width:1px}
.verdict li.yes.risk-money{background:var(--warn-bg);border-color:#f0dfb6}
.verdict li.yes.risk-money .v-ans,.verdict li.yes.risk-money .v-word{color:var(--warn)}
.verdict li.yes.risk-approval{background:#f4eefb;border-color:#e0d0f0}
.verdict li.yes.risk-approval .v-ans,.verdict li.yes.risk-approval .v-word{color:#6b3fa0}
.verdict li.yes.risk-people{background:var(--good-bg);border-color:#cfe6d9}
.verdict li.yes.risk-people .v-ans,.verdict li.yes.risk-people .v-word{color:var(--good)}
.verdict li.yes.risk-destructive{background:var(--bad-bg);border-color:#f3cfcd}
.verdict li.yes.risk-destructive .v-ans,.verdict li.yes.risk-destructive .v-word{color:var(--bad)}
.verdict li.yes.risk-settings{background:var(--line);border-color:var(--line)}

.whose-line{margin-top:14px;font-size:14px;color:var(--muted)}
.role-list{list-style:none;margin:10px 0 0;padding:0;display:flex;
  flex-direction:column;gap:2px}
.role-list a{display:flex;flex-direction:column;gap:2px;padding:10px 13px;
  border-radius:7px;text-decoration:none;color:inherit}
.role-list a:hover{background:var(--bg)}
.role-list .role-name{font-weight:600;font-size:14.5px}
.role-say.big textarea{font-size:15.5px;padding:12px 14px}
.role-say-foot{display:flex;gap:12px;align-items:center;flex-wrap:wrap}
.or-pick{margin-top:16px;display:flex;gap:18px;flex-wrap:wrap;font-size:14px}
.sofar{margin:6px 0 22px;padding:14px 16px;background:var(--bg);
  border-radius:9px;border:1px solid var(--line)}
.sofar h2{font-size:12px;letter-spacing:.07em;text-transform:uppercase;
  color:var(--muted);margin:0 0 8px}
.asks{display:flex;flex-direction:column;gap:16px}
fieldset.ask{border:1px solid var(--line);border-radius:9px;padding:14px 16px;
  margin:0}
fieldset.ask legend{padding:0 6px;font-weight:600;font-size:15px}
.ask-opts{display:flex;gap:8px;flex-wrap:wrap;margin-top:6px}
.ask-opts label{display:flex;align-items:center;gap:7px;padding:8px 14px;
  border:1px solid var(--line);border-radius:20px;cursor:pointer;font-size:14px}
.ask-opts label:hover{border-color:var(--accent)}
.ask-opts label:has(input:checked){background:var(--ink);border-color:var(--ink);
  color:var(--card);font-weight:600}
.ask-opts input{margin:0}
.match{border:1px solid var(--accent);border-radius:9px;padding:16px 18px;
  background:var(--card);display:flex;flex-direction:column;gap:9px;margin:6px 0 20px}
.match-head{display:flex;gap:10px;align-items:baseline;flex-wrap:wrap;font-size:16px}
.match-do{display:flex;gap:12px;align-items:center;flex-wrap:wrap;margin-top:4px}
.note-warn{background:var(--warn-bg);color:var(--warn);padding:10px 14px;
  border-radius:8px;font-size:14px;margin:14px 0}

/* One weight of emphasis per screen. Group headings are bold; the rows under
   them are not — when every capability was bold, none of them stood out. */
.role-can li,.role-cannot li{font-weight:400}
.whose-line .whose-mod{font-weight:600;color:var(--ink)}
.quick-jobs{display:flex;flex-wrap:wrap;gap:7px;align-items:center;margin-top:14px;
  font-size:13.5px}
.quick-jobs .chip{display:inline-block;padding:5px 12px;border:1px solid var(--line);
  border-radius:20px;text-decoration:none;color:var(--ink);background:var(--card)}
.quick-jobs .chip:hover{border-color:var(--accent)}
.quick-jobs .chip.more{color:var(--muted);border-style:dashed}

/* What a measurement file does to the BOQ it is for. Above the rows, because
   the question is not "did you read my sheet right?" but "what happens to my
   BOQ?" — and the answer is a quantity, which is money. */
.measure-card{padding:18px 20px;margin-bottom:14px}
.measure-card h2{margin:0 0 8px;font-size:16px}
.measure-headline{margin:0 0 12px;font-size:14px}
.measure-how{display:flex;gap:16px;align-items:center;flex-wrap:wrap;
  font-size:13.5px;padding:10px 12px;border:1px solid var(--line);
  border-radius:9px;margin-bottom:12px}
.measure-how-label{color:var(--muted)}
.measure-how label{display:flex;gap:6px;align-items:center;margin:0}
.measure-detail summary{cursor:pointer;font-size:13.5px;color:var(--accent)}
.measure-table{width:100%;margin-top:10px}
.measure-table td,.measure-table th{padding:7px 10px;font-size:13.5px}
.measure-table .num{text-align:right;font-variant-numeric:tabular-nums}
.measure-table .serial{font-variant-numeric:tabular-nums;white-space:nowrap}
/* The old number, struck through beside the new one: the change is the point,
   and a lone new number says nothing about what it replaced. */
.measure-table .was{color:var(--muted);text-decoration:line-through;
  margin-right:6px}
.measure-table tr.moves strong{color:var(--ink)}
.measure-note{color:var(--muted);font-size:12px;margin-left:6px}
/* A deduction is not a smaller measurement — it is one taken off. Onsite
   shows those in red, and the difference is invisible in a total alone. */
.measure-deduct{color:var(--bad);font-size:12px;margin-left:6px}

/* Getting the BOQ before editing it: the first half of the update screen. */
.boq-get{border:1px solid var(--line);border-radius:10px;padding:14px 16px;
  margin-bottom:18px;background:var(--card)}
.boq-get h2,.boq-step-two{font-size:15px;margin:0 0 10px}
.boq-step-two{margin-top:4px}
.boq-pick{display:flex;gap:12px;align-items:flex-end;flex-wrap:wrap;
  margin-bottom:10px}
.boq-pick label{display:flex;flex-direction:column;gap:4px;font-size:13px;
  color:var(--muted)}
.boq-pick select{min-width:240px}

/* The BOQ on screen, editable.

   Built out of the review grid's own parts rather than beside them: same
   header band, same row rhythm, same quiet cells that show an edge only when
   the pointer is on them. A screen full of boxed inputs reads as a form to
   fill in; this is a table you correct, which is what it is. */
.edit-table td { padding: 0; vertical-align: middle; }
.edit-table td.serial { color: var(--muted); font-variant-numeric: tabular-nums;
  padding-left: calc(14px + var(--d, 0) * 15px); white-space: nowrap;
  width: 1%; font-size: 12.5px; }
/* The lines under a heading, heavier than the heading itself — the same way
   the BOQ's own review screen reads. */
.edit-table tr:not(.section-row) td:nth-child(2) input { font-weight: 500; }
.edit-table input {
  width: 100%; padding: 7px 10px; font: inherit; font-size: 13.5px;
  color: var(--ink); background: none; border: 1px solid transparent;
  border-radius: 6px; }
.edit-table input:hover { border-color: var(--line); background: var(--card); }
.edit-table input:focus { outline: none; border-color: var(--accent);
  background: var(--card); box-shadow: 0 0 0 3px rgba(31,94,255,.12); }
.edit-table input.num { text-align: right; font-variant-numeric: tabular-nums; }
/* A heading over a column of right-aligned numbers belongs over the digits,
   not off at the far side of the column. */
.edit-table th.num, .mb-table th.num { text-align: right; padding-right: 16px; }
.edit-table input[readonly] { color: var(--muted); cursor: not-allowed; }
.edit-table input[readonly]:hover { border-color: transparent; background: none; }
.edit-table col.c-narrow { width: 90px; }
.edit-table col.c-tiny { width: 70px; }
/* The item column took everything left over, which on a wide screen is most
   of the table — and a description is not more readable for being 700px
   wide. It takes two thirds of the free space; notes take the rest. */
.edit-table col.c-serial { width: 64px; }
.edit-table col.c-item { width: 40%; }
.edit-table col.c-notes { width: 20%; }
.edit-table td input { text-overflow: ellipsis; }

/* A section heading: the same tint the tree uses for one, and nothing to edit
   but its own text — Onsite works a section's totals out from the lines under
   it. */
.edit-table tr.section-row td { background: #f7f8fa; }
.edit-table tr.section-row input { font-weight: 600; }
.edit-table tr.section-row .heading-note { color: var(--muted); font-size: 11px;
  text-transform: uppercase; letter-spacing: .05em; padding-left: 10px; }

/* The measurements under a line. Folded away by default: on a BOQ of a
   hundred lines the open ones are the point, and the rest are noise. */
.edit-table tr.measure-under td { border-top: 0; padding: 0 0 0 14px; }
.edit-table tr.measure-under details { padding: 0 0 8px; }
.edit-table summary { display: inline-flex; align-items: center; gap: 6px;
  cursor: pointer; font-size: 12px; color: var(--accent); padding: 2px 0 4px;
  list-style: none; }
.edit-table summary::-webkit-details-marker { display: none; }
.edit-table summary::before { content: "▸"; font-size: 10px; }
.edit-table details[open] > summary::before { content: "▾"; }
.edit-table summary .count { background: var(--bg); color: var(--muted);
  border: 1px solid var(--line); border-radius: 10px; padding: 0 6px;
  font-size: 11px; }
.edit-table summary .by-count { color: var(--muted); }

.mb-table { width: 100%; table-layout: fixed; border-collapse: separate;
  border-spacing: 0; margin: 2px 0 4px; background: var(--bg);
  border: 1px solid var(--line); border-radius: 8px; }
.mb-table th { background: none; border: 0; padding: 6px 10px 2px;
  font-size: 10.5px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); font-weight: 500; text-align: left; }
.mb-table th.num, .mb-table td.num { text-align: right; }
.mb-table td { padding: 1px 4px 4px; border: 0; }
.mb-table input { background: var(--card); border-color: var(--line);
  padding: 5px 8px; font-size: 13px; }
.mb-table input:hover { border-color: #cdd3db; }
/* The boxes sit next to the description, not out at the right-hand edge: a
   measurement is read across, and hunting for the Nos box past 600px of empty
   table is not reading. The description takes a fixed share and a last,
   empty column soaks up whatever is left over. */
.mb-table col.c-desc { width: 300px; }
.mb-table col.c-dim { width: 84px; }
.mb-table col.c-deduct { width: 96px; }
.mb-table .desc input { width: 100%; }
.deduct-box { display: inline-flex; gap: 5px; align-items: center;
  font-size: 11.5px; color: var(--muted); margin: 0; white-space: nowrap; }
.deduct-box input { width: auto; padding: 0; }
.deduct-box input:hover { border-color: transparent; background: none; }
.measure-hint { font-size: 11.5px; color: var(--muted); margin: 2px 0 0; }

/* Above the table, and sticky: on a hundred-line page the button was a
   scroll away from wherever the last edit was made. */
.edit-actions { display: flex; gap: 14px; align-items: center; flex-wrap: wrap;
  padding: 12px 18px; border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 3; background: var(--card);
  border-radius: 10px 10px 0 0; }
.edit-pager { font-size: 12.5px; margin-left: auto; }
/* What the measurements come to, under the column it totals. Onsite's own
   screen shows one, and without it you add your own rows up to see what the
   line becomes. */
.mb-foot td { border-top: 1px solid var(--line); padding-top: 6px;
  font-size: 12.5px; color: var(--muted); }
.mb-foot .label { text-align: right; padding-right: 10px; }
.mb-foot .unit { text-transform: none; }
.mb-foot .sum { color: var(--ink); font-variant-numeric: tabular-nums;
  font-size: 13px; padding-right: 8px; }
/* A Qty the tool worked out, against one somebody typed over it. */
.mb-table input.qty[data-auto="1"] { color: var(--muted); }

/* Which BOQ this is, and nothing else: the screen explains itself. */
.edit-title { font-size: 18px; margin: 0 0 12px; }

/* Two readings of one number. Both are offered whole — the number, its unit
   and where it came from — because the choice is between two accounts of the
   line, not between two strings. */
.panel-note { font-size: 13px; margin: 0 0 10px; }
.qty-choice .panel-actions { flex-direction: column; align-items: stretch;
  gap: 8px; }
.qty-choice button { text-align: left; line-height: 1.35; }
.qty-choice button em { display: block; font-style: normal; font-size: 11.5px;
  opacity: .75; font-weight: 400; }
.qty-all { display: flex; gap: 7px; align-items: center; font-size: 12.5px;
  color: var(--muted); margin-bottom: 10px; }

/* One BOQ, two ways at it. Two panels rather than a pair of radio dots: the
   difference between adding lines and changing them is worth a sentence. */
.which-way { display: flex; gap: 10px; flex-wrap: wrap; margin: 4px 0 14px; }
.which-way label { display: flex; align-items: center;
  border: 1px solid var(--line); border-radius: 9px; padding: 9px 16px;
  cursor: pointer; background: var(--card); margin: 0; }
.which-way label.on { border-color: var(--accent);
  box-shadow: inset 0 0 0 1px var(--accent); }
.which-way input { position: absolute; opacity: 0; pointer-events: none; }
.which-way .what { font-weight: 600; font-size: 13.5px; }

/* A BOQ read as a BOQ. The same shapes its own review screen uses: a heading
   tinted and in normal weight, the lines under it heavier, and the serial
   stepped in so the outline is visible without opening anything. */
tr.line-section > td { background: #f7f8fa; }
tr.line-section td.cell[data-field="name"] .txt { font-weight: 400; }
tr.line-item td.cell[data-field="name"] .txt { font-weight: 600; color: #0f5132; }
td.cell[data-field="serial"] .txt { padding-left: calc(12px + var(--d, 0) * 15px);
  font-variant-numeric: tabular-nums; }
/* Material purchase: a held bill stays on the list, quieter, so a missing
   bill is something you can see rather than something you have to notice. */
.measure-table tr.held > td{color:var(--muted)}
.measure-table details summary{cursor:pointer}
.measure-table details table{margin-top:6px;text-align:left}
.measure-table details td{white-space:nowrap}

/* --- Budget ------------------------------------------------------------------
   The Budget screens were designed as one piece (mockups/budget-section.html)
   and are kept that way: every rule below sits under `.bud`, the wrapper those
   three templates draw inside, so none of it can reach an upload screen and
   nothing above can quietly restyle a budget line. The first three rules undo
   the tool's global button, which is a filled primary by default; in here a
   button is whatever its class says it is. */
.bud button { font: inherit; margin: 0; padding: 0; border: 0; border-radius: 0;
              background: none; color: inherit; opacity: 1; cursor: pointer; }
.bud button:hover { opacity: 1; }
.bud button:disabled { opacity: .5; cursor: default; }
.bud a { color:var(--accent); }
.bud a:hover { color:var(--ink); }
.bud [hidden] { display:none !important; }
.bud h1 { font-size:22px; margin:0 0 6px; }
.bud h2 { font-size:16px; margin:0 0 8px; }
.bud h3.band { font-size:13px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin:22px 0 10px; }
.bud .muted { color:var(--muted); }
.bud .small { font-size:13px; }
.bud .lead { font-size:15px; color:#3f4651; margin:4px 0 0; max-width:76ch; }
.bud .card { background:var(--card); border:1px solid var(--line); border-radius:10px; padding:20px 22px; margin-bottom:14px; }
.bud .steps { display:flex; gap:8px; list-style:none; padding:0; margin:0; font-size:13px; flex-wrap:wrap; }
.bud .steps li { padding:4px 12px; border-radius:999px; background:#fff; border:1px solid var(--line); color:var(--muted); }
.bud .steps li.now { background:var(--ink); border-color:var(--ink); color:#fff; }
.bud .topline { display:flex; align-items:center; justify-content:space-between; gap:16px; flex-wrap:wrap; margin:0 0 14px; }
.bud .acts { display:flex; gap:10px; align-items:center; flex-wrap:wrap; }
.bud .btn { font:inherit; font-size:14px; font-weight:550; padding:9px 16px; border-radius:7px; border:1px solid #d5d9df; background:#fff; color:var(--ink); cursor:pointer; text-decoration:none; display:inline-block; }
.bud .btn:hover { border-color:var(--ink); color:var(--ink); }
.bud .btn.pri { background:var(--ink); border-color:var(--ink); color:#fff; }
.bud .btn.pri:hover { opacity:.9; color:#fff; }
.bud .btn.sm { font-size:13px; padding:6px 11px; }
.bud .btn.big { font-size:15.5px; padding:11px 20px; }
.bud .btn.block { display:block; text-align:center; margin-top:8px; width:100%; }
.bud .btn:disabled { opacity:.45; cursor:default; }
.bud .tallies { display:flex; gap:10px; flex-wrap:wrap; margin:14px 0 0; }
.bud .tally { flex:1 1 130px; padding:11px 14px; border:1px solid var(--line); border-radius:8px; background:#fff; }
.bud .tally strong { display:block; font-size:22px; line-height:1.15; }
.bud .tally span { font-size:12.5px; color:var(--muted); }
.bud .t-ready { background:var(--good-bg); border-color:#cbe7d7; }
.bud .t-conf { background:var(--warn-bg); border-color:#efe0b4; }
.bud .t-need { background:var(--bad-bg); border-color:#f3cfcd; }
.bud .read { display:flex; gap:10px 18px; flex-wrap:wrap; align-items:center; padding:10px 14px; border-radius:8px; background:var(--bg); font-size:13.5px; margin-top:14px; }
.bud .chips { display:flex; gap:8px; flex-wrap:wrap; align-items:center; margin:0 0 12px; }
.bud .chip { font:inherit; font-size:13px; padding:5px 12px; border-radius:999px; border:1px solid #d5d9df; background:#fff; color:var(--ink); cursor:pointer; }
.bud .chip.on { background:var(--ink); border-color:var(--ink); color:#fff; }
.bud .search { font:inherit; font-size:13.5px; padding:7px 11px; border:1px solid #d5d9df; border-radius:7px; min-width:220px; margin-left:auto; background:#fff; }
.bud .sr { position:absolute; left:-9999px; }
.bud .scroll { overflow-x:auto; }
.bud table { border-collapse:collapse; width:100%; font-size:13.5px; }
.bud table.wide { min-width:880px; }
.bud th { text-align:left; font-size:12px; font-weight:600; color:var(--muted); padding:8px 10px; border-bottom:1px solid var(--line); white-space:nowrap; }
.bud td { padding:10px; border-bottom:1px solid #eef0f3; vertical-align:top; }
.bud td small { display:block; color:var(--muted); font-size:12.5px; margin-top:2px; }
.bud .num { text-align:right; font-variant-numeric:tabular-nums; white-space:nowrap; }
.bud .plain th, .bud .plain td { padding-left:0; }
.bud .plain tr:last-child td { border-bottom:0; }
.bud .gridwrap { overflow:visible; }
.bud .bgrid { table-layout:fixed; }
.bud .bgrid th.g-no { width:62px; }
.bud .bgrid th.g-qty { width:116px; }
.bud .bgrid th.g-rate { width:104px; }
.bud .bgrid th.g-amt { width:124px; }
.bud .bgrid th.g-bud { width:136px; }
.bud .bgrid th.g-items { width:112px; }
/* What the tool worked out, as against what the BOQ says: its own colour. */
.bud { --gen:#6a3ec9; }
.bud .bgrid th.gen, .bud .bgrid td.gen { color:var(--gen); }
.bud .bgrid td.gen { font-weight:550; }
.bud .bgrid th.g-rev { width:150px; }
.bud .bgrid th { background:var(--bg); position:sticky; top:0; z-index:6; box-shadow:inset 0 -1px 0 var(--line); }
.bud .bgrid thead th:first-child { border-top-left-radius:10px; }
.bud .bgrid thead th:last-child { border-top-right-radius:10px; }
.bud .bgrid td { border-bottom:1px solid var(--line); }
.bud .bgrid tr.sec td { background:#fafbfc; font-weight:650; font-size:13px; color:#3f4651; padding:7px 10px; }
.bud .bgrid tr.has-detail td:first-child { white-space:nowrap; }
.bud .bgrid tr.has-detail { cursor:pointer; }
.bud .bgrid tr.has-detail:hover td { background:#f8faff; }
.bud .bgrid tr.open td { background:#e6edfb; border-bottom:0; }
.bud .bgrid .item { max-width:380px; }
.bud .caret { display:inline-block; width:14px; font-size:17px; line-height:1; font-weight:700; color:var(--accent); transition:transform .12s; }
.bud tr.open .caret { transform:rotate(90deg); }
.bud .detail { background:#f0f4fc; border-bottom:2px solid #c3d0ee; padding:12px 18px 16px 46px; }
.bud .dhead { display:flex; align-items:center; gap:10px; flex-wrap:wrap; margin-bottom:8px; }
.bud .dhead h4 { font-size:12.5px; margin:0; text-transform:uppercase; letter-spacing:.05em; color:#3f4a63; }
.bud .dhead .grow { flex:1; }
/* What a line's recipe left open, and how it was settled. A row of quiet
   buttons under the header: read at a glance, changed in one click. */
.bud .choices { display:flex; flex-wrap:wrap; gap:6px; margin:-2px 0 10px; }
.bud .choice-wrap { position:relative; }
.bud .choice { display:inline-flex; align-items:baseline; gap:6px; font:inherit; font-size:12.5px; padding:4px 9px; border-radius:6px; border:1px solid #d5dbe8; background:#fff; color:var(--ink); cursor:pointer; }
.bud .choice:hover, .bud .choice[aria-expanded="true"] { border-color:var(--accent); }
.bud .choice small { font-size:10.5px; font-weight:650; text-transform:uppercase; letter-spacing:.05em; color:var(--warn); }
.bud .choice b { font-weight:600; }
.bud .choice span { color:var(--muted); font-size:12px; }
.bud .choice i { font-style:normal; color:var(--muted); font-size:10px; }
.bud .choice.mine { background:#eef3ff; border-color:#c9d6ff; }
.bud .pop.choose { left:0; top:calc(100% + 4px); width:max-content; min-width:240px; max-width:340px; }
.bud .pop.choose .pop-q { font-size:12.5px; padding:4px 8px 8px; border-bottom:1px solid #eef0f3; margin-bottom:4px; color:var(--ink); }
.bud .pop.choose .pick b { font-weight:500; }
.bud .pop.choose .pick.on b { font-weight:650; }
.bud .ibtn { width:28px; height:28px; padding:0; border-radius:50%; border:1px solid #b9c6e6; background:#fff; color:var(--norm); display:inline-flex; align-items:center; justify-content:center; cursor:pointer; }
.bud .ibtn:hover { border-color:var(--accent); color:var(--accent); }
.bud .btn.quiet { background:transparent; border-color:transparent; color:var(--muted); }
.bud .btn.quiet:hover { border-color:#b9c6e6; background:#fff; color:var(--ink); }
.bud .btn.accept { border-color:#0a7c42; color:#0a6a3a; background:#fff; font-weight:600; }
.bud .btn.accept:hover { background:var(--good-bg); border-color:#0a7c42; color:#0a6a3a; }
.bud .btn.accepted { background:var(--good-bg); border-color:#cbe7d7; color:var(--good); font-weight:600; display:inline-flex; align-items:center; gap:5px; }
.bud .btn.accepted:hover { border-color:#0a7c42; color:var(--good); }
.bud td.review { white-space:nowrap; }
.bud .bgrid tr.is-ok td { background:#f4faf6; }
.bud .bgrid tr.is-ok.open td { background:#e6edfb; }
.bud .mtab .ratefrom { color:var(--muted); }
.bud #prog { background:#dfe3ea; }
.bud .mtab { width:100%; font-size:13.5px; table-layout:fixed; }
.bud .mtab th.c-m { width:30%; }
.bud .mtab th.c-q { width:158px; }
.bud .mtab th.c-x { text-align:right; padding-top:2px; padding-bottom:2px; }
.bud .choices .grow { flex:1; }
.bud .mtab th.c-r { width:112px; }
.bud .mtab th.c-a { width:104px; }
.bud .mtab th.c-x { width:38px; }
.bud .mtab th { background:none; border-bottom:1px solid #c9d4ec; padding:6px 12px 6px 0; color:#4a556e; }
.bud .mtab td { background:none; border-bottom:1px solid #dbe3f4; padding:5px 12px 5px 0; vertical-align:middle; }
.bud .mtab tr.total td { border-bottom:0; padding-top:9px; }
.bud .mtab tr.addrow td { border-bottom:1px solid #c9d4ec; }
.bud .qin { font:inherit; font-size:13.5px; width:78px; padding:4px 6px; border:1px solid transparent; border-radius:6px; text-align:right; font-variant-numeric:tabular-nums; background:transparent; color:var(--ink); -moz-appearance:textfield; }
.bud .qin::-webkit-outer-spin-button, .bud .qin::-webkit-inner-spin-button { -webkit-appearance:none; margin:0; }
.bud .qin:hover { border-color:#aebde0; background:#fff; }
.bud .qin:focus { border-color:var(--accent); background:#fff; outline:none; box-shadow:0 0 0 2px rgba(31,94,255,.18); }
.bud .mtab .amt, .bud .mtab .unit, .bud .mtab .cur { color:var(--muted); }
.bud .mtab .unit, .bud .mtab .cur { font-size:12.5px; }
.bud .swap { font:inherit; font-size:13.5px; text-align:left; color:var(--ink); background:none; border:1px solid transparent; border-radius:6px; padding:4px 6px; margin-left:-7px; cursor:pointer; }
.bud .swap:hover { border-color:#aebde0; background:#fff; }
.bud .tools { white-space:nowrap; text-align:right; width:1%; }
.bud .tool { width:28px; height:28px; padding:0; border:1px solid transparent; background:none; color:#7f8aa3; cursor:pointer; border-radius:6px; display:inline-flex; align-items:center; justify-content:center; vertical-align:middle; }
.bud .tool:hover { background:#fff; border-color:#aebde0; color:var(--ink); }
.bud .tool.rm:hover { color:var(--bad); border-color:#f3cfcd; }
.bud .addbtn { font:inherit; font-size:13px; font-weight:600; color:var(--accent); background:none; border:0; padding:5px 0; cursor:pointer; }
.bud .addbtn:hover { color:var(--ink); }
.bud .mwrap { overflow:visible; }
.bud .mtab td.mname { position:relative; }
.bud .mtab .how { color:#3f4a63; font-size:13px; padding-left:14px; }
.bud .mtab th.how { color:#4a556e; font-size:12px; }
.bud .pop { position:absolute; left:-7px; top:calc(100% - 3px); z-index:30; width:100%; min-width:280px; max-width:360px; background:#fff; border:1px solid #aebde0; border-radius:8px; box-shadow:0 10px 28px rgba(22,25,29,.18); padding:8px; }
.bud .pick-q { font:inherit; font-size:13.5px; width:100%; padding:7px 10px; border:1px solid #d5d9df; border-radius:6px; }
.bud .pick-q:focus { outline:none; border-color:var(--accent); box-shadow:0 0 0 2px rgba(31,94,255,.18); }
.bud .pick-list { max-height:236px; overflow:auto; margin-top:6px; }
.bud .pick { display:flex; align-items:baseline; justify-content:space-between; gap:10px; width:100%; font:inherit; font-size:13px; text-align:left; padding:6px 8px; border:0; border-radius:6px; background:none; cursor:pointer; color:var(--ink); }
.bud .pick span { color:var(--muted); font-size:12px; white-space:nowrap; }
.bud .pick:hover { background:#f3f6ff; }
.bud .pick.new { color:var(--accent); border-top:1px solid #eef0f3; border-radius:0 0 6px 6px; margin-top:4px; padding-top:8px; }
/* The start screen has a `.pick` of its own further down; inside the material
   list its spacing and wrapping are not wanted. */
.bud .pop .pick { margin-top:0; flex-wrap:nowrap; align-items:baseline; gap:10px; }
.bud .pop .pick.new { margin-top:4px; }
.bud .pop.many { min-width:300px; }
.bud .pop.many .pick { justify-content:flex-start; gap:8px; }
.bud .pop.many .pick b { flex:1 1 auto; min-width:0; font-weight:500; }
.bud .pop .cb { flex:none; align-self:center; width:15px; height:15px; border:1.5px solid #9aa3b2; border-radius:4px; background:#fff; font-style:normal; font-size:11px; line-height:12px; text-align:center; color:#fff; }
.bud .pop .pick.on { background:#eef3ff; }
.bud .pop .pick.on .cb, .bud .pop .cb.on { background:var(--accent); border-color:var(--accent); }
.bud .pop .pick.on .cb::after, .bud .pop .cb.on::after { content:"\2713"; }
.bud .pop .pick.has { cursor:default; color:var(--muted); }
.bud .pop .pick.has:hover { background:none; }
.bud .pop .pick.has .cb.on { background:#b9c0cc; border-color:#b9c0cc; }
.bud .pick-sel { display:flex; flex-wrap:wrap; gap:4px; margin-top:6px; }
.bud .pick-sel:empty { display:none; }
.bud .chipx { font:inherit; font-size:12px; max-width:100%; padding:2px 8px; border-radius:999px; border:1px solid #c9d6ff; background:#eef3ff; color:var(--ink); cursor:pointer; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; }
.bud .chipx:hover { border-color:var(--accent); }
.bud .pick-foot { display:flex; justify-content:flex-end; margin-top:6px; padding-top:8px; border-top:1px solid #eef0f3; }
.bud .dialog.wide { width:min(840px, 100%); }
.bud .dlg-body.calc h3 { font-size:12px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin:16px 0 6px; }
.bud .reflist { margin:0; padding-left:18px; font-size:14px; }
.bud .reflist li { margin-bottom:3px; }
.bud .fld { display:flex; align-items:center; justify-content:space-between; gap:12px; padding:6px 0; font-size:13.5px; }
.bud .sel { font:inherit; font-size:13.5px; padding:6px 9px; border:1px solid #d5d9df; border-radius:6px; background:#fff; }
.bud .sel.big { font-size:14px; padding:9px 11px; min-width:300px; }
.bud .in { font:inherit; font-size:14px; padding:6px 9px; border:1px solid #d5d9df; border-radius:6px; width:92px; }
.bud .note-s { font-size:12.5px; color:var(--muted); margin:8px 0 0; }
.bud .foot { position:sticky; bottom:0; display:flex; gap:14px; align-items:center; justify-content:space-between; flex-wrap:wrap; margin-top:16px; padding:12px 16px; background:#fff; border:1px solid var(--line); border-radius:10px; box-shadow:0 -2px 12px rgba(22,25,29,.06); }
.bud .b { display:inline-block; font-size:11.5px; font-weight:600; padding:2px 8px; border-radius:999px; white-space:nowrap; }
.bud .b-norm { background:var(--norm-bg); color:var(--norm); }
.bud .b-ds { background:#f1eefb; color:#4b3a8f; }
.bud .b-sup, .bud .b-grey, .bud .b-rule, .bud .s-none { background:#eef1f4; color:#3f4651; }
.bud .b-ai { background:#fff0e0; color:#8a4b00; border:1px solid #f2d3a8; }
.bud .s-ready, .bud .b-ok { background:var(--good-bg); color:var(--good); }
.bud .s-conf, .bud .b-pend, .bud .b-warn { background:var(--warn-bg); color:var(--warn); }
.bud .s-need, .bud .b-bad { background:var(--bad-bg); color:var(--bad); }
.bud .lbl { display:block; font-size:13px; font-weight:600; margin:0 0 5px; }
.bud .boqs { list-style:none; margin:12px 0 0; padding:0; display:flex; flex-direction:column; gap:6px; }
.bud .boqs label { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:10px 12px; border:1px solid var(--line); border-radius:8px; font-size:14px; cursor:pointer; }
.bud .boqs .meta { margin-left:auto; font-size:12.5px; color:var(--muted); }
.bud .qs { display:grid; grid-template-columns:repeat(2, minmax(0, 1fr)); gap:12px 18px; margin-top:12px; }
.bud .q { border:1px solid var(--line); border-radius:8px; padding:12px 14px; }
.bud .pills { display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }
.bud .pills label { display:inline-flex; align-items:center; gap:6px; padding:6px 11px; border:1px solid #d5d9df; border-radius:999px; font-size:13px; cursor:pointer; }
.bud label.on { border-color:var(--accent) !important; background:#f3f6ff; }
.bud .pills label.on { color:#1b3f9e; font-weight:600; }
.bud .from { display:inline-block; font-size:11px; font-weight:600; padding:1px 7px; border-radius:999px; background:var(--norm-bg); color:var(--norm); margin-left:6px; }
.bud .from.ask { background:var(--warn-bg); color:var(--warn); }
.bud .three { display:grid; grid-template-columns:repeat(3, minmax(0, 1fr)); gap:14px; margin-top:12px; }
.bud .three > div { border:1px solid var(--line); border-radius:8px; padding:14px; }
.bud .three b { display:block; margin-bottom:4px; }
.bud .three span.t { font-size:13.5px; color:#3f4651; }
.bud .n { display:inline-flex; width:24px; height:24px; border-radius:50%; background:var(--ink); color:#fff; font-size:12.5px; font-weight:700; align-items:center; justify-content:center; flex:none; }
.bud .three .n { margin-bottom:8px; }
.bud .between { display:flex; gap:14px; align-items:center; justify-content:space-between; flex-wrap:wrap; }
.bud h2.step { display:flex; align-items:center; gap:10px; font-size:15.5px; margin-bottom:10px; }
.bud .quote { padding:10px 14px; border-radius:8px; background:var(--bg); font-size:15px; margin:0 0 10px; }
.bud .ai { border:1px solid #f2d3a8; background:#fffaf3; border-radius:8px; padding:12px 14px; font-size:13.5px; margin-top:12px; }
.bud .work { list-style:none; margin:0; padding:0; counter-reset:w; }
.bud .work li { counter-increment:w; display:flex; gap:12px; padding:9px 0; border-bottom:1px solid #eef0f3; font-size:14px; }
.bud .work li:last-child { border-bottom:0; }
.bud .work li::before { content:counter(w); flex:none; width:20px; color:var(--muted); font-variant-numeric:tabular-nums; }
.bud .eq { font-family:ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size:12.5px; background:var(--bg); border-radius:6px; padding:1px 6px; }
.bud .checks { list-style:none; margin:0; padding:0; }
.bud .checks li:last-child { border-bottom:0; }
.bud .bigfig { font-size:26px; font-weight:650; line-height:1.1; font-variant-numeric:tabular-nums; }
.bud .note-warn { padding:10px 14px; border-radius:8px; background:var(--warn-bg); color:#6b4800; font-size:13.5px; margin:10px 0 0; }
.bud .note-info { padding:10px 14px; border-radius:8px; background:var(--norm-bg); color:var(--norm); font-size:14px; margin:0 0 14px; }
.bud textarea { width:100%; font:inherit; font-size:13.5px; padding:8px 10px; border:1px solid #d5d9df; border-radius:7px; margin-top:4px; }
.bud .kind { display:inline-block; font-size:11px; font-weight:650; text-transform:uppercase; letter-spacing:.06em; padding:2px 9px; border-radius:999px; margin-bottom:8px; }
.bud .k-need { background:var(--bad-bg); color:var(--bad); }
.bud .k-conf { background:var(--warn-bg); color:var(--warn); }
.bud .where { font-size:13px; color:var(--muted); margin:0 0 8px; }
.bud .opts { display:flex; flex-direction:column; gap:6px; }
.bud .opts > label { display:flex; align-items:flex-start; gap:10px; padding:10px 12px; border:1px solid var(--line); border-radius:8px; font-size:14px; cursor:pointer; }
.bud .opts input[type=radio] { margin-top:4px; }
.bud .opts small { display:block; color:var(--muted); font-size:12.5px; }
.bud .opts .eff { margin-left:auto; font-size:12.5px; color:var(--muted); white-space:nowrap; font-variant-numeric:tabular-nums; text-align:right; }
.bud .keep { display:flex; gap:8px; align-items:flex-start; font-size:12.5px; color:var(--muted); margin:10px 0 0; }
.bud .share { height:8px; background:#eceef1; border-radius:999px; overflow:hidden; min-width:120px; }
.bud .share span { display:block; height:100%; background:var(--norm); }
.bud .checks li { display:flex; gap:10px; align-items:flex-start; padding:8px 0; border-bottom:1px solid #eef0f3; font-size:14px; }
.bud .tick { flex:none; width:18px; height:18px; border-radius:50%; background:var(--good-bg); color:var(--good); display:inline-flex; align-items:center; justify-content:center; font-size:12px; font-weight:700; margin-top:2px; }
.bud .tick.w { background:var(--warn-bg); color:var(--warn); }
.bud code { background:#f0f1f3; padding:1px 6px; border-radius:4px; font-size:13px; }
.bud .done { padding:14px 16px; border-radius:8px; background:var(--good-bg); color:var(--good); font-size:14.5px; margin-top:14px; }
.bud .pick { display:flex; gap:14px; flex-wrap:wrap; align-items:flex-end; margin-top:10px; }
.bud .pick > div { flex:1 1 240px; min-width:0; }
.bud .pick .sel.big { width:100%; min-width:0; }
.bud .answers { display:flex; gap:6px 4px; flex-wrap:wrap; align-items:center; margin-top:12px; font-size:13.5px; }
.bud .answers b { margin-right:6px; }
.bud .ans { display:inline-block; font-size:12px; padding:2px 9px; border-radius:999px; background:#fff; border:1px solid var(--line); color:#3f4651; margin:0 4px 4px 0; }
.bud .ans.skip { background:var(--warn-bg); border-color:#efe0b4; color:var(--warn); }
.bud .ans.typed { background:#e6f4f1; border-color:#bfe3dc; color:#0b5d55; }
.bud .used { margin:0; }
.bud .b-you { background:#e6f4f1; color:#0b5d55; }
.bud .bgrid tr.has-detail:focus { outline:2px solid var(--accent); outline-offset:-2px; }
.bud .rhead { display:flex; gap:14px; align-items:flex-end; justify-content:space-between; flex-wrap:wrap; margin:0 0 14px; }
.bud .based { margin:0; font-size:14px; color:#3f4651; display:flex; align-items:center; gap:10px; }
.bud .totalbar { position:fixed; left:218px; right:0; bottom:0; z-index:20; display:flex; align-items:center; gap:10px 36px; flex-wrap:wrap; padding:10px 24px; background:#fff; border-top:1px solid var(--line); box-shadow:0 -4px 16px rgba(22,25,29,.08); }
.bud .totalbar .fig span { display:block; font-size:12px; color:var(--muted); }
.bud .totalbar .fig strong { font-size:19px; line-height:1.25; font-variant-numeric:tabular-nums; }
.bud .totalbar .fig em { font-style:normal; font-size:11.5px; color:var(--muted); margin-left:6px; }
.bud .totalbar .grow { flex:1; }
.bud .alist h3 { font-size:12px; text-transform:uppercase; letter-spacing:.06em; color:var(--muted); margin:14px 0 4px; }
.bud .arow { display:flex; align-items:center; gap:12px; width:100%; font:inherit; font-size:14px; text-align:left; padding:8px 10px; background:none; border:0; border-bottom:1px solid #eef0f3; cursor:pointer; color:var(--ink); border-radius:6px; }
.bud .arow:hover { background:#f3f6ff; }
.bud .arow .aq { flex:1 1 55%; }
.bud .arow .aval { flex:1 1 35%; font-weight:600; }
.bud .arow .aval.none { font-weight:400; color:var(--muted); }
.bud .arow .achg { color:var(--accent); font-size:13px; }
.bud .modal { position:fixed; inset:0; z-index:50; display:flex; align-items:center; justify-content:center; padding:20px; background:rgba(15,21,48,.55); }
.bud .dialog { background:#fff; border-radius:14px; width:min(720px, 100%); max-height:92vh; display:flex; flex-direction:column; box-shadow:0 24px 70px rgba(0,0,0,.35); outline:none; }
.bud .dlg-head { display:flex; align-items:flex-start; justify-content:space-between; gap:12px; padding:16px 22px 10px; }
.bud .dlg-group { font-size:11.5px; font-weight:650; text-transform:uppercase; letter-spacing:.07em; color:var(--norm); }
.bud .dlg-count { font-size:13px; color:var(--muted); margin-top:2px; }
.bud .x { font:inherit; font-size:22px; line-height:1; width:34px; height:34px; border-radius:8px; border:1px solid var(--line); background:#fff; color:var(--muted); cursor:pointer; }
.bud .x:hover { color:var(--ink); border-color:var(--ink); }
.bud .progress { height:4px; background:#eceef1; margin:0 22px; border-radius:999px; overflow:hidden; }
.bud .progress span { display:block; height:100%; width:0; background:var(--accent); transition:width .2s; }
.bud .dlg-body { padding:18px 22px 6px; overflow:auto; flex:1 1 auto; min-height:0; }
/* The questions: one size from the first question to the last, with the
   question scrolling inside it. A box that grew and shrank with each question
   moved the buttons under the pointer. */
.bud #modal .dialog { height:min(720px, 92vh); }
/* A scroll bar that can be seen: on a Mac the usual one hides until the list
   is already moving, and then nothing says there is more below. */
.bud #modal .dlg-body { overflow-y:auto; }
.bud #modal .dlg-body::-webkit-scrollbar { width:10px; }
.bud #modal .dlg-body::-webkit-scrollbar-thumb { background:#c3c9d3; border-radius:999px; border:2px solid #fff; }
.bud #modal .dlg-body::-webkit-scrollbar-track { background:transparent; }
.bud .opts > .more-opts { align-self:flex-start; padding-left:2px; }
.bud .dlg-body h2 { font-size:20px; margin:0 0 8px; line-height:1.3; }
.bud .applies { font-size:13px; color:var(--norm); background:var(--norm-bg); display:inline-block; padding:3px 10px; border-radius:6px; margin:0 0 8px; }
.bud .whyq { font-size:14px; color:#3f4651; margin:0 0 14px; }
.bud .dlg-foot { display:flex; align-items:center; gap:10px; flex-wrap:wrap; padding:14px 22px 18px; border-top:1px solid var(--line); margin-top:12px; }
.bud .dlg-foot:empty { display:none; }
.bud .dlg-foot .grow { flex:1; }
.bud .btn.link { border-color:transparent; background:none; color:var(--accent); padding-left:6px; padding-right:6px; }
.bud .btn.link:hover { border-color:transparent; color:var(--ink); }
.bud .reading { text-align:center; padding:34px 0 30px; }
.bud .spinner { display:inline-block; width:28px; height:28px; border-radius:50%; border:3px solid #dfe5f5; border-top-color:var(--accent); animation:spin .8s linear infinite; margin-bottom:10px; }
.bud @keyframes spin { to { transform:rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .bud .spinner { animation:none; }
  .bud .progress span { transition:none; }
}
.bud .readstats { display:flex; gap:10px; flex-wrap:wrap; margin:10px 0 12px; }
.bud .readstats div { flex:1 1 150px; border:1px solid var(--line); border-radius:8px; padding:10px 14px; }
.bud .readstats strong { display:block; font-size:22px; line-height:1.15; }
.bud .readstats span { font-size:12.5px; color:var(--muted); }
.bud .groups { margin:0; padding-left:18px; font-size:14px; }
.bud .ynrow, .bud .opts > .fillrow { display:flex; align-items:center; justify-content:space-between; gap:12px; flex-wrap:wrap; padding:10px 12px; border:1px solid var(--line); border-radius:8px; font-size:14px; }
.bud .ynrow small { display:block; font-size:12.5px; color:var(--muted); margin-top:3px; }
.bud .yn { display:inline-flex; gap:6px; }
.bud .ynrow .then { flex:1 1 100%; }
.bud .in.wide { width:100%; padding:8px 10px; }
.bud .opts > label.other { align-items:center; }
.bud .opts > label .grow { flex:1; min-width:160px; }
.bud .dlg-body textarea { font-size:14.5px; padding:10px 12px; }
.bud .yn label { display:inline-flex; align-items:center; gap:6px; padding:6px 12px; border:1px solid #d5d9df; border-radius:999px; font-size:13px; cursor:pointer; }
@media (max-width:820px) {
  .bud .qs, .bud .three { grid-template-columns:minmax(0,1fr); }
  .bud .sel.big { min-width:0; width:100%; }
  .bud .search { margin-left:0; width:100%; }
  .bud .totalbar { left:0; }
  .bud .gridwrap { overflow-x:auto; }
  .bud .bgrid { min-width:900px; }
  .bud .mwrap { overflow-x:auto; }
  .bud .mtab { min-width:760px; }
  .bud .bgrid th { position:static; }
}
/* A class that sets `display` beats the browser's own `[hidden]`, so each one
   that can carry the attribute says so itself — the rule this file keeps. */
.bud .btn[hidden], .bud .caret[hidden], .bud .tick[hidden], .bud .bar[hidden] { display: none; }
.bud.narrow { max-width: 980px; }
/* The mockup stood beside a 218px sidebar; the tool's is 232px. And a heading
   in a budget table is a label to read, not the small capitals the upload
   grids use. */
.bud .totalbar { left: 232px; }
@media (max-width: 820px) { .bud .totalbar { left: 0; } }
.bud th { text-transform: none; letter-spacing: 0; }
.bud .startrow .lbl { display: block; margin: 0 0 5px; }
.bud .steps { margin-bottom: 14px; }
.bud .startrow { display: flex; gap: 14px 18px; flex-wrap: wrap; align-items: flex-end; }
.bud .startrow form { display: flex; gap: 12px; align-items: flex-end; flex: 1 1 320px; min-width: 0; }
.bud .startrow .fld { flex: 1 1 200px; min-width: 0; }
.bud .startrow select { width: 100%; }
.bud .btn.off { opacity: .5; cursor: default; }
.bud .flash { margin: 0 0 12px; }
.bud .actions { display: flex; gap: 10px; align-items: center; flex-wrap: wrap; margin-top: 18px; }
.bud .actions .grow { flex: 1; }
.bud .results { list-style: none; margin: 14px 0 0; padding: 0; font-size: 13.5px; }
.bud .results li { padding: 8px 0; border-top: 1px solid #eef0f3; }
.bud .results li.bad small { display: block; color: var(--bad); margin-top: 2px; }

/* The Learning page (templates/learning.html), from the same mockup. */
.bud .bar { display:flex; height:8px; border-radius:999px; overflow:hidden; flex:1 1 220px; min-width:160px; }
.bud .bar.tall { height:12px; flex:none; width:100%; margin:4px 0 10px; }
.bud .legend { display:flex; gap:8px 18px; flex-wrap:wrap; font-size:13.5px; color:#3f4651; }
.bud .legend i { display:inline-block; width:10px; height:10px; border-radius:2px; margin-right:6px; }
.bud .notes { list-style:none; margin:0; padding:0; }
.bud .notes li:last-child { border-bottom:0; }
.bud .notes small { display:block; color:var(--muted); font-size:12.5px; }
.bud .team { font-size:11px; font-weight:650; text-transform:uppercase; letter-spacing:.05em; padding:3px 9px; border-radius:999px; background:#eef1f4; color:#3f4651; vertical-align:middle; margin-left:8px; }
.bud .ltabs { display:flex; gap:2px; flex-wrap:wrap; margin-top:16px; border-top:1px solid #eef0f3; }
.bud .ltab { font:inherit; font-size:14px; font-weight:600; color:var(--muted); background:none; border:0; border-bottom:3px solid transparent; padding:11px 14px 9px; cursor:pointer; display:inline-flex; align-items:center; gap:7px; }
.bud .ltab:hover { color:var(--ink); }
.bud .ltab.on { color:var(--ink); border-bottom-color:var(--accent); }
.bud .ltab:focus-visible { outline:2px solid var(--accent); outline-offset:-2px; border-radius:6px 6px 0 0; }
.bud .ltab b { font-size:11.5px; font-weight:650; padding:1px 7px; border-radius:999px; background:var(--good-bg); color:var(--good); font-variant-numeric:tabular-nums; }
.bud table.learn th.next, .bud table.learn td.next { width:190px; padding-left:18px; }
.bud .btn.draft { border-color:#cbe7d7; color:var(--good); }
.bud .btn.draft:hover { background:var(--good-bg); border-color:#0a7c42; }
.bud .words { display:flex; gap:8px; flex-wrap:wrap; }
.bud .word { font-size:13px; padding:4px 10px; border-radius:999px; background:var(--bg); border:1px solid var(--line); }
.bud .word b { font-variant-numeric:tabular-nums; margin-left:6px; color:var(--muted); font-weight:600; }
.bud .notes { font-size:14px; }
.bud .notes li { padding:9px 0; border-bottom:1px solid #eef0f3; }
.bud .loop { display:flex; gap:10px; flex-wrap:wrap; align-items:center; font-size:13.5px; margin-top:6px; }
.bud .loop span { padding:6px 12px; border-radius:8px; background:var(--bg); border:1px solid var(--line); }
.bud .loop i { font-style:normal; color:var(--muted); }
.bud .ltab, .bud a.chip, .bud a.btn { text-decoration: none; }
.bud .verify { display: flex; gap: 10px 12px; align-items: center; flex-wrap: wrap; margin-top: 14px; padding-top: 14px; border-top: 1px solid #eef0f3; }
.bud .verify .why { font-size: 13.5px; color: #3f4651; flex: 1 1 320px; min-width: 0; }
.bud .verify .grow { flex: 1 1 0; }
.bud .verify label { font-size: 13.5px; font-weight: 600; color: #3f4651; margin: 0; }
.bud .verify input[type="number"] { width: 68px; font: inherit; font-size: 14px; padding: 7px 8px; border: 1px solid #d5d9df; border-radius: 7px; background: #fff; text-align: right; font-variant-numeric: tabular-nums; }
.bud table.learn form { display: inline; }
.bud table.learn td.next small { display: block; color: var(--muted); font-size: 12.5px; margin-top: 3px; }
.bud .rulegrid { display: grid; grid-template-columns: minmax(0, 1fr) minmax(0, 1fr); gap: 14px; }
@media (max-width: 820px) { .bud .rulegrid { grid-template-columns: minmax(0, 1fr); } }
.bud .rulegrid label { display: block; font-size: 12.5px; font-weight: 600; color: #3f4651; margin: 0 0 5px; }
.bud .rulegrid input[type="text"], .bud .rulegrid select { width: 100%; font: inherit; font-size: 14px; padding: 8px 10px; border: 1px solid #d5d9df; border-radius: 7px; background: #fff; }
.bud .examples { list-style: none; margin: 6px 0 0; padding: 0; font-size: 13.5px; }
.bud .examples li { padding: 5px 0; border-top: 1px solid #eef0f3; }
.bud .examples small { color: var(--muted); margin-left: 6px; }
