/*
 * om.css — the house style, taken from the Admin system.
 *
 * The Admin app is the requirements source for how stock is listed and how
 * filters behave, and its look is part of that: warm greys rather than blue,
 * near-black as the accent so the status colours are the only saturated things
 * on the page, chips instead of dropdowns for anything a trader picks often,
 * and a dense table that fits real stock on one screen.
 *
 * Status colour is load-bearing here, not decoration. Green means available,
 * amber means out on memo, red means sold, purple means complete. Those four
 * meanings are the same everywhere in the system.
 */

:root {
  --bg: #FAFAF9;
  --surface: #ffffff;
  --surface2: #F5F5F4;
  --surface3: #E7E5E4;
  --border: #E7E5E4;
  --border2: #D6D3D1;
  --accent: #1C1917;
  --accent-dim: rgba(28,25,23,0.07);
  /* Text sitting ON the accent. Not "white": in dark mode the accent is nearly
     white itself, and anything hard-coded here disappears into it. */
  --on-accent: #ffffff;
  --flash-ok-ink: #14532d;
  --flash-err-ink: #7f1d1d;
  color-scheme: light;
  --text: #0C0A09;
  --text2: #44403C;
  --text3: #78716C;

  --available: #15803D;  --available-bg: #F0FDF4;
  --memo:      #854D0E;  --memo-bg:      #FEFCE8;
  --sold:      #DC2626;  --sold-bg:      #FEF2F2;
  --complete:  #6D28D9;  --complete-bg:  #F5F3FF;

  /*
   * A traffic light, in pastel.
   *
   * The status colours above are for a screen where colour is the signal and
   * there is one coloured thing per row. A ledger is not that: every row has a
   * confidence, an amount and a state, and four saturated hues over fifty rows
   * stopped reading as meaning and started reading as noise (Shay, 2026-08-28).
   *
   * Three lights, soft grounds, ink dark enough to read on them. Green trust
   * it, amber check it, red somebody guessed.
   */
  --calm-go-bg:   #ECF6EF;  --calm-go-ink:   #35694A;
  --calm-hmm-bg:  #FBF4E1;  --calm-hmm-ink:  #7A6120;
  --calm-stop-bg: #FAECEC;  --calm-stop-ink: #994646;

  --good: #15803D;
  --bad: #DC2626;
  --radius: 8px;
  --radius-sm: 6px;
  --shadow: 0 1px 2px rgba(28,25,23,0.05);
  --shadow-lg: 0 8px 30px rgba(28,25,23,0.12);
  --mono: 'SF Mono', ui-monospace, Menlo, Consolas, monospace;
}

* { box-sizing: border-box; margin: 0; padding: 0; -webkit-font-smoothing: antialiased; }

body {
  background: var(--bg); color: var(--text);
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Text', 'Segoe UI', Roboto, sans-serif;
  font-size: 14px;
}

/* ── Header and navigation ─────────────────────────────────────────── */

header {
  background: var(--surface); border-bottom: 1px solid var(--border);
  padding: 0 24px; position: sticky; top: 0; z-index: 60;
}
.brand { padding: 14px 0 8px; display: flex; align-items: baseline; gap: 12px; }
.brand-title {
  font-family: -apple-system, BlinkMacSystemFont, 'SF Pro Display', sans-serif;
  font-size: 20px; font-weight: 600; letter-spacing: -0.4px; color: var(--text);
}
.brand-eyebrow {
  font-size: 11px; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text3); font-weight: 500;
}
nav { display: flex; gap: 2px; }
nav > a, nav .menu > span {
  display: block; padding: 9px 14px; text-decoration: none; color: var(--text2);
  font-size: 13px; border-bottom: 2px solid transparent; cursor: pointer; white-space: nowrap;
}
nav > a:hover, nav .menu:hover > span { color: var(--text); }
nav > a.on, nav .menu.on > span { color: var(--text); border-bottom-color: var(--accent); font-weight: 600; }
nav .menu { position: relative; }
nav .menu > span::after { content: ' ▾'; color: var(--text3); font-size: 10px; }
nav .drop {
  display: none; position: absolute; top: 100%; left: 0; min-width: 214px; z-index: 70;
  background: var(--surface); border: 1px solid var(--border); border-radius: 0 8px 8px 8px;
  box-shadow: var(--shadow-lg); padding: 5px 0;
}
/* Hover for the mouse, focus-within for the keyboard, and .open for a click,
 * which the script puts on and takes off. */
nav .menu:hover .drop, nav .menu:focus-within .drop, nav .menu.open .drop { display: block; }
nav .drop a { display: block; padding: 8px 16px; text-decoration: none; color: var(--text2); font-size: 13px; }
nav .drop a:hover { background: var(--surface2); color: var(--text); }
nav .drop a.on { color: var(--text); font-weight: 600; }
nav .drop .sep { height: 1px; background: var(--border); margin: 5px 0; }

main { padding: 20px 24px 8px; }
footer { padding: 10px 24px 40px; color: var(--text3); font-size: 12px; }

h2 { font-size: 16px; font-weight: 600; letter-spacing: -0.2px; margin: 24px 0 8px; }
h2:first-child { margin-top: 0; }
.note { color: var(--text3); font-size: 12px; margin: 0 0 14px; line-height: 1.6; max-width: 900px; }

/* ── Panels, cards, tables ─────────────────────────────────────────── */

.panel {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); overflow: auto; margin-bottom: 10px;
}
.panel.attached { border-radius: 0 0 var(--radius) var(--radius); border-top: none; }

table { border-collapse: collapse; width: 100%; font-size: 13px; }
th {
  text-align: left; font-size: 11px; font-weight: 600; letter-spacing: 0.04em;
  text-transform: uppercase; color: var(--text3); background: var(--surface2);
  padding: 9px 12px; border-bottom: 1px solid var(--border); white-space: nowrap;
}

/*
 * Frozen while the rows go past — but only a real table head.
 *
 * Every th used to be sticky, which was harmless while the offset was zero and
 * broke the debtors page the moment it was not: those tables put their header
 * cells in an ordinary row with no thead around them, so the offset pushed the
 * header a hundred and fifty pixels down the table and over the first rows.
 *
 * A header cell is only worth freezing when it is declared as one. And inside
 * a panel it freezes to the panel, which scrolls on its own, so the page
 * header's height is not the offset there.
 */
thead th { position: sticky; top: var(--header-h, 0px); z-index: 5; }
.panel thead th { top: 0; }
td { padding: 8px 12px; border-bottom: 1px solid var(--border); color: var(--text2); }
tbody tr:last-child td, tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--surface2); }

.num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.mono { font-family: var(--mono); font-size: 12px; }
.muted { color: var(--text3); }
.tot td { border-top: 2px solid var(--border2); font-weight: 600; background: var(--surface2); }
.ok { color: var(--good); } .no { color: var(--bad); }

.cards { display: flex; gap: 8px; flex-wrap: wrap; margin-bottom: 10px; }
.card {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px; min-width: 154px;
}
.card .v { font-size: 22px; font-weight: 600; font-variant-numeric: tabular-nums; letter-spacing: -0.5px; }
.card .k { font-size: 11px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.05em; margin-top: 2px; }

/* ── Bank accounts ─────────────────────────────────────────────────── */

/*
 * One card per account, two numbers on each.
 *
 * The bank's own number is the big one, because that is the question somebody
 * has when they open the page: how much is in there. What the books say about
 * the same account sits under it, and the difference between the two is not an
 * error, it is the lines nobody has explained yet.
 */
.accounts { display: flex; gap: 10px; flex-wrap: wrap; margin-bottom: 14px; }
.account {
  display: block; text-decoration: none; color: inherit;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 14px 16px 12px; min-width: 236px;
}
.account:hover { border-color: var(--border2); }
.account.on { border-color: var(--accent); box-shadow: var(--shadow); }
.account .who { font-size: 13px; font-weight: 600; color: var(--text); }
.account .who .tail {
  font-family: var(--mono); font-size: 11px; color: var(--text3); font-weight: 400;
  border: 1px solid var(--border); border-radius: 20px; padding: 1px 7px; margin-left: 6px;
}
.account .v {
  font-size: 24px; font-weight: 600; font-variant-numeric: tabular-nums;
  letter-spacing: -0.5px; margin-top: 8px;
}
.account .k {
  font-size: 11px; color: var(--text3); text-transform: uppercase;
  letter-spacing: 0.05em; margin-top: 2px;
}
.account .line {
  display: flex; justify-content: space-between; gap: 18px;
  font-size: 12px; color: var(--text3); margin-top: 5px;
}
.account .k + .line { border-top: 1px solid var(--border); padding-top: 9px; margin-top: 10px; }
.account .line .mono { color: var(--text2); }
/* Amber, not red. Money waiting to be explained is work, not a fault. */
.account .line.gap, .account .line.gap .mono { color: var(--calm-hmm-ink); }
.account .bank-name { font-size: 11px; color: var(--text3); margin-top: 9px; }

/* ── Reports ───────────────────────────────────────────────────────── */

/*
 * A statement is a column of figures, not a spreadsheet.
 *
 * These tables were stretching the full width of the screen, which put the
 * account name at the far left and its number at the far right with a hand's
 * width of nothing between them, and made the eye travel for every line. A
 * profit and loss has three columns and wants to be narrow, the way it is on
 * paper. The figures then have room to be read rather than squinted at.
 */
.statement { max-width: 780px; }
/* A few more columns needs a little more room, and still nothing like the
   full width of a screen. */
.statement.figures { max-width: 940px; }
.statement table { font-size: 14px; }
.statement td { padding: 9px 14px; }
.statement .num { font-size: 16px; letter-spacing: -0.2px; }
.statement .mono { font-size: 13px; }
.statement .tot td { font-size: 15px; }
.statement .tot .num { font-size: 17px; font-weight: 600; }

/* A section heading inside a statement: a label, not a row of data. */
.statement .tot td[colspan="3"] {
  font-size: 11px; text-transform: uppercase; letter-spacing: 0.06em;
  color: var(--text3); font-weight: 600; padding-top: 14px;
}

/*
 * The wide ones earn their width: a stone has a parcel, a size, a grade, an
 * owner and an age, and none of that folds. What they can have is money big
 * enough to read at a glance, since that is what anybody scans for.
 */
.figures table { font-size: 13px; }
.figures .num { font-size: 15px; letter-spacing: -0.2px; }
.figures .num.mono { font-size: 15px; }
.figures .tot .num { font-weight: 600; }

/* The headline numbers on a report carry the answer. Let them say it. */
.report-cards .card { min-width: 190px; padding: 16px 18px; }
.report-cards .card .v { font-size: 28px; letter-spacing: -0.8px; }
.report-cards .card .k { margin-top: 4px; }

/* Two lists of the same shape sit above one another and have to line up, which
   an auto layout will not do: each table sizes its own columns to its own
   content, so the money ends up in two different places. */
.doclist table { table-layout: fixed; }
.doclist td, .doclist th { overflow: hidden; text-overflow: ellipsis; }
.doclist .num { font-size: 15px; letter-spacing: -0.2px; }

/* Filing an arrival happens in the row it arrived on, so the form is furniture
   rather than a page of its own. */
form.filer { display: flex; gap: 6px; align-items: center; margin: 0; }
form.filer select { max-width: 300px; font-size: 12px; padding: 4px 6px; }
form.filer button { font-size: 12px; padding: 4px 10px; }

/* Terms that do not divide. Folded away, because most documents are paid once
   and a form that shows eight empty rows to everybody is a form nobody reads. */
details.terms { margin: 0 0 4px; }
details.terms > summary {
  cursor: pointer; font-size: 12px; color: var(--text3);
  padding: 6px 0; list-style: none; user-select: none;
}
details.terms > summary::-webkit-details-marker { display: none; }
details.terms > summary::before { content: "+ "; font-weight: 600; }
details.terms[open] > summary::before { content: "2 "; }
details.terms > summary:hover { color: var(--text2); }
details.terms .terms-grid { display: flex; flex-direction: column; gap: 4px; padding: 6px 0 2px; }
details.terms .terms-grid .f > label { min-height: 14px; }


/* ── Status tags ───────────────────────────────────────────────────── */

.tag {
  display: inline-block; padding: 2px 8px; border-radius: 20px; font-size: 11px;
  font-weight: 500; background: var(--surface2); color: var(--text2); border: 1px solid var(--border);
  white-space: nowrap;
}
.tag.owned, .tag.available { background: var(--available-bg); color: var(--available); border-color: #BBF7D0; }
.tag.memo, .tag.on_memo_out, .tag.prov { background: var(--memo-bg); color: var(--memo); border-color: #FDE68A; }
/* Held on memo is its own situation, not a shade of "out": the stone is here
   and sellable, it just is not ours. */
.tag.memo_in { background: var(--complete-bg); color: var(--complete); border-color: #DDD6FE; }
.tag.sold { background: var(--sold-bg); color: var(--sold); border-color: #FECACA; }
.tag.complete { background: var(--complete-bg); color: var(--complete); border-color: #DDD6FE; }
/* A vendor's listing. Amber, like the External chip that finds it. */
.tag.external { background: var(--memo-bg); color: var(--memo); border-color: #FDE68A; }
/* Seen by administrators only. Ink on paper, so it reads as "not for everyone"
   rather than as a state of the stone. */
.tag.hidden { background: var(--text); color: var(--surface); border-color: var(--text); }

/* ── Whose stone it is ─────────────────────────────────────────────────
   A dot on the parcel name. The list holds our own stock and the vendors'
   listings side by side, and every other thing on a row reads the same either
   way, so this is the one mark that has to be found without being read. The
   four colours are the Status chips' four, deliberately: the row and the filter
   row answer the same question and should not use two vocabularies.

   A ring rather than a disc for somebody else's stone. Filled means we have it,
   open means we do not — which survives a photocopy and does not depend on
   telling green from amber. */
.own-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-right: 6px; vertical-align: middle;
  background: var(--available); border: 1px solid var(--available);
}
.own-dot.memo { background: var(--complete); border-color: var(--complete); }
.own-dot.sold { background: var(--sold); border-color: var(--sold); }
.own-dot.ext  { background: transparent; border: 2px solid var(--memo); }

/* ── Forms ─────────────────────────────────────────────────────────── */

form.card-form {
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px; margin-bottom: 16px;
}
.row { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end; margin-bottom: 12px; }
.row:last-child { margin-bottom: 0; }
.f { display: flex; flex-direction: column; gap: 4px; }
.f label { font-size: 10px; color: var(--text3); text-transform: uppercase; letter-spacing: 0.05em; font-weight: 600; }

/* A row aligns on its bottom edge, which is right until one field carries a
   hint underneath: that field grows taller, and because the bottoms have to
   meet, its input is pushed up out of line with its neighbours. Where a row
   has hints in it, align on the top instead and hold every label to one line,
   so the inputs start together and the hints hang below wherever they land. */
.row:has(> .f > .hint) { align-items: flex-start; }
.row:has(> .f > .hint) > .f > label { min-height: 14px; line-height: 14px; }

input, select, textarea {
  font: inherit; font-size: 13px; padding: 7px 10px; border: 1.5px solid var(--border2);
  border-radius: var(--radius-sm); background: var(--surface); color: var(--text); min-width: 90px;
  outline: none; transition: border-color 0.15s, box-shadow 0.15s;
}
input:focus, select:focus, textarea:focus {
  border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim);
}
input.n { text-align: right; font-variant-numeric: tabular-nums; }

/* A control that does not apply right now. Left on the page rather than
   removed from it, because a field that appears and disappears is a field
   nobody trusts — but dimmed, so it does not read as something to fill in. */
input[disabled], select[disabled], textarea[disabled] {
  background: var(--surface2); color: var(--text3); border-color: var(--border); cursor: not-allowed;
}
.f.dim > label, .f.dim > .hint { opacity: 0.6; }

button {
  font: inherit; font-size: 13px; font-weight: 500; padding: 8px 18px;
  border-radius: var(--radius-sm); cursor: pointer;
  background: var(--accent); color: var(--on-accent); border: 1px solid var(--accent);
  transition: opacity 0.15s;
}
button:hover { opacity: 0.88; }
button.plain {
  background: var(--surface); color: var(--text2); border-color: var(--border2);
  font-weight: 400; padding: 6px 14px;
}
button.plain:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }

.flash {
  padding: 11px 14px; border-radius: var(--radius); margin-bottom: 16px; font-size: 13px;
  border: 1px solid; line-height: 1.55;
}
.ok-flash  { background: var(--available-bg); border-color: #BBF7D0; color: var(--flash-ok-ink); }
.err-flash { background: var(--sold-bg); border-color: #FECACA; color: var(--flash-err-ink); }
.hint { font-size: 12px; color: var(--text3); margin-top: 8px; line-height: 1.55; }

/* ── Quick filters: chip rows ──────────────────────────────────────── */

.quick-filters {
  background: var(--surface); border: 1px solid var(--border);
  border-radius: var(--radius); margin-bottom: 10px;
}
.qf-split { display: flex; align-items: stretch; }
/*
 * Folding is a fade, and nothing else.
 *
 * It used to animate max-height, padding, width and border-width over 280ms,
 * which means a full page layout on every frame of it — and below this box sits
 * a table of several hundred rows, so every one of those frames relaid the grid
 * out as well. On our own stock it was merely a bit soft; with a vendor's
 * listings loaded it was the heaviest thing on the screen.
 *
 * Opacity is composited and touches no geometry. The height still changes, but
 * in one step rather than seventeen, so the table moves once and the chips fade
 * while it does. Quicker to watch and a fraction of the work.
 */
.qf-rows { flex: 1; min-width: 0; padding: 4px 14px 10px; }
.qf-group-section {
  display: flex; align-items: center; gap: 14px;
  padding: 7px 0; border-top: 1px solid var(--border);
  /* Folds away on scroll: the height goes at once, the chips fade over it.
     Nothing that costs a layout is animated — see the note above .qf-rows. */
  overflow: hidden;
  transition: opacity .12s linear;
}
.qf-group-section:first-child { border-top: none; }
.qf-section-label {
  font-size: 10px; font-weight: 600; letter-spacing: 0.06em; text-transform: uppercase;
  color: var(--text3); flex-shrink: 0; width: 86px;
}
.qf-group { flex: 1; display: flex; gap: 4px; flex-wrap: wrap; }

.qf-btn {
  padding: 5px 11px; background: var(--surface2); border: 1px solid var(--border2);
  color: var(--text2); font: inherit; font-size: 12.5px; font-weight: 500;
  cursor: pointer; border-radius: var(--radius-sm);
  transition: background-color .12s, border-color .12s, color .12s;
  white-space: nowrap; user-select: none;
}
.qf-btn:hover { background: var(--surface3); color: var(--text); }
.qf-btn.active { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }

/* How many that vendor is listing, on the button that finds them. Quieter than
   the name, because it is the answer to a second question. */
.qf-btn .qf-n { opacity: .6; font-variant-numeric: tabular-nums; margin-left: 5px; font-size: 10.5px; }
.qf-btn.active .qf-n { opacity: .75; }

/* A range reads as one run: solid ends, tinted middle. */
.qf-btn.in-range { background: var(--accent-dim); border-color: var(--accent); color: var(--accent); font-weight: 600; }
.qf-btn.range-start, .qf-btn.range-end { background: var(--accent); border-color: var(--accent); color: var(--on-accent); font-weight: 600; }

.qf-btn.active-green  { background: var(--available-bg); border-color: var(--available); color: var(--available); font-weight: 600; }
.qf-btn.active-amber  { background: var(--memo-bg); border-color: var(--memo); color: var(--memo); font-weight: 600; }
.qf-btn.active-red    { background: var(--sold-bg); border-color: var(--sold); color: var(--sold); font-weight: 600; }
.qf-btn.active-purple { background: var(--complete-bg); border-color: var(--complete); color: var(--complete); font-weight: 600; }

.qf-paste-col {
  width: 176px; min-width: 176px; padding: 10px 14px;
  border-left: 1px solid var(--border); display: flex; flex-direction: column;
  overflow: hidden;
  transition: opacity .12s linear;
}
.qf-paste-col textarea {
  font-family: var(--mono); font-size: 11.5px; line-height: 1.6; width: 100%;
  height: 104px; resize: none; padding: 7px 9px;
}
.qf-foot {
  display: flex; align-items: center; gap: 10px; flex-wrap: wrap;
  padding: 9px 14px; border-top: 1px solid var(--border); background: var(--surface2);
  border-radius: 0 0 var(--radius) var(--radius);
}
.clear-btn {
  background: none; border: 1px solid var(--border2); border-radius: 20px;
  padding: 4px 13px; font-size: 11.5px; color: var(--text3); cursor: pointer;
  transition: background-color .15s, border-color .15s, color .15s;
}
/*
 * A filter is on. Red, and breathing slowly, because the failure it guards
 * against is a filtered list read as the whole stock (Shay, 2026-10-06). Lit
 * only when the view differs from the opening one, so the pulse means something.
 */
.clear-btn.lit {
  border-color: var(--sold); color: var(--sold); background: var(--sold-bg); font-weight: 600;
  animation: filter-on 2.4s ease-in-out infinite;
}
@keyframes filter-on {
  0%, 100% { box-shadow: 0 0 0 0 transparent; }
  50%      { box-shadow: 0 0 0 4px color-mix(in srgb, var(--sold) 22%, transparent); }
}
/* The same warning beside the stock count, which stays on screen when the
   chips have folded away. */
.filtered-tag {
  margin-left: 8px; padding: 1px 9px; border-radius: 20px; cursor: pointer;
  border: 1px solid var(--sold); background: var(--sold-bg); color: var(--sold);
  font-size: 11px; font-weight: 600; line-height: 1.5;
  animation: filter-on 2.4s ease-in-out infinite;
}
.filtered-tag:hover, .clear-btn.lit:hover { background: var(--sold); color: var(--surface); }
/* OM and these books disagree about this stone: an error to correct, not a
   fact to read past. Empty where they agree, so red is all there is to see. */
.house-wrong { color: var(--sold); font-weight: 600; cursor: help; }
.clear-btn.lit .filter-count { background: var(--sold); color: var(--surface); }
.clear-btn.lit:hover .filter-count { background: var(--surface); color: var(--sold); }
@media (prefers-reduced-motion: reduce) {
  .clear-btn.lit, .filtered-tag { animation: none; }
}
.filter-count {
  display: inline-block; margin-left: 6px; background: currentColor; color: var(--bg);
  border-radius: 10px; padding: 0 6px; font-size: 10px; font-weight: 700; min-width: 14px; text-align: center;
}
.saved-filter-chip {
  display: inline-flex; align-items: center; gap: 5px; background: var(--surface);
  border: 1px solid var(--border2); border-radius: 20px; padding: 4px 10px 4px 12px;
  font-size: 11.5px; color: var(--text2); cursor: pointer; transition: border-color 0.15s;
}
.saved-filter-chip:hover { border-color: var(--accent); color: var(--accent); }
.saved-filter-chip .sf-del { opacity: 0.4; font-size: 11px; }
.saved-filter-chip .sf-del:hover { opacity: 1; color: var(--sold); }
.save-filter-btn {
  background: none; border: 1px dashed var(--border2); border-radius: 20px;
  padding: 4px 12px; font-size: 11.5px; color: var(--text3); cursor: pointer;
  transition: border-color .15s, color .15s;
}
.save-filter-btn:hover { border-color: var(--accent); color: var(--accent); }

/* ── Table toolbar ─────────────────────────────────────────────────── */

.table-toolbar {
  display: flex; gap: 10px; align-items: center; padding: 9px 14px; flex-wrap: wrap;
  background: var(--surface2); border: 1px solid var(--border); border-bottom: none;
  border-radius: var(--radius) var(--radius) 0 0;
}
.toolbar-stat { font-size: 12px; color: var(--text3); }
.toolbar-stat b { color: var(--text); font-weight: 600; font-variant-numeric: tabular-nums; }
.toolbar-stat .sel-count { color: var(--accent); font-weight: 600; }
.toolbar-actions { margin-left: auto; display: flex; gap: 6px; align-items: center; }
.toolbar-btn {
  font: inherit; font-size: 11.5px; padding: 5px 12px; background: transparent; color: var(--text2);
  border: 1px solid var(--border2); border-radius: var(--radius-sm); cursor: pointer;
  transition: background-color .15s, border-color .15s, color .15s; white-space: nowrap;
}
.toolbar-btn:hover { border-color: var(--accent); color: var(--accent); }
.toolbar-btn.primary { background: var(--accent-dim); color: var(--accent); border-color: var(--accent); font-weight: 600; }
.toolbar-btn:disabled { opacity: 0.4; cursor: default; }
.toolbar-btn:disabled:hover { border-color: var(--border2); color: var(--text2); }

/* ── The grid ──────────────────────────────────────────────────────── */

table.grid { font-size: 11.5px; }
table.grid thead th {
  background: var(--surface2); padding: 7px 7px; font-size: 10.5px; font-weight: 600;
  color: var(--text3); border-bottom: 1px solid var(--border); text-align: left; white-space: nowrap;
}
table.grid thead th.num, table.grid tbody td.num { text-align: right; }
table.grid thead th.mid, table.grid tbody td.mid { text-align: center; }
table.grid tbody td {
  padding: 5px 7px; border-bottom: 0.5px solid var(--border); white-space: nowrap; color: var(--text2);
}
table.grid tbody tr { cursor: default; }

/* Sticky identity columns. They need an opaque background or the columns
   scrolling underneath show through as text over text. */
table.grid th.stick, table.grid td.stick { position: sticky; background: var(--surface); z-index: 4; }
table.grid thead th.stick { z-index: 6; background: var(--surface2); }
/* The left offsets are set by om.js from what actually rendered, so hiding a
   pinned column or a long parcel name cannot leave the pinned block misaligned. */
table.grid .c-sel    { width: 32px; min-width: 32px; text-align: center; padding: 5px 4px; }
/* Wide enough for the dot as well as the fourteen characters: a name that wraps
   is a name that cannot be read down the column. */
table.grid .c-parcel { width: 138px; min-width: 138px; color: var(--text); font-weight: 500;
                       font-family: var(--mono); font-size: 11px; white-space: nowrap; }
table.grid .c-ct     { width: 52px; min-width: 52px; text-align: right; }
table.grid .c-col    { width: 44px; min-width: 44px; text-align: center; }
table.grid .c-cla    { width: 54px; min-width: 54px; text-align: center; }
/* The rule down the right edge of the pinned block is written by pinColumns()
   in om.js, into a stylesheet of its own: which column is last, and where each
   one sits, is only known once the widths have been measured. */

table.grid tbody tr.selected td { background: var(--accent-dim) !important; }
table.grid tbody tr.selected td.stick {
  background: linear-gradient(var(--accent-dim), var(--accent-dim)), var(--surface) !important;
}
table.grid tbody tr:hover td { background: var(--surface2); }
table.grid tbody tr:hover td.stick {
  background: linear-gradient(var(--surface2), var(--surface2)), var(--surface);
}

.sel-circle {
  display: inline-block; width: 13px; height: 13px; border: 1.2px solid var(--border2);
    /* Three hundred of these on screen at once: paint only, never geometry. */
  border-radius: 50%; cursor: pointer; transition: background-color .12s, border-color .12s;
  vertical-align: middle;
}
.sel-circle:hover { border-color: var(--accent); }
.sel-circle.on { background: var(--accent); border-color: var(--accent); }

/* ── Column header filter dropdown ─────────────────────────────────── */

.col-filter-btn {
  display: inline-flex; align-items: center; gap: 3px; background: none; border: none;
  color: inherit; font: inherit; cursor: pointer; padding: 0;
  text-transform: uppercase; letter-spacing: 0.04em; font-size: 10.5px; font-weight: 600;
}
.col-filter-btn:hover { color: var(--accent); }
.col-filter-btn .cf-arrow { font-size: 8px; opacity: 0.45; margin-left: 1px; }
.col-filter-btn.cf-active, .col-filter-btn.cf-sorted { color: var(--accent); }
.col-filter-btn.cf-active .cf-arrow { opacity: 1; }

.col-dropdown {
  position: fixed; z-index: 9999; background: var(--surface);
  border: 1px solid var(--border2); border-radius: 10px; padding: 8px 0;
  min-width: 190px; max-width: 268px; box-shadow: var(--shadow-lg); display: none;
}
.col-dropdown.open { display: block; }
.col-dropdown-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 2px 12px 8px; border-bottom: 1px solid var(--border); margin-bottom: 4px;
}
.col-dropdown-title {
  font-size: 10.5px; font-weight: 600; color: var(--text3);
  text-transform: uppercase; letter-spacing: 0.05em;
}
.col-dropdown-clear {
  font-size: 10.5px; color: var(--accent); cursor: pointer; background: none;
  border: none; padding: 0; font-family: inherit;
}
.cd-sort { display: flex; gap: 4px; padding: 0 12px 7px; }
.cd-sort button {
  flex: 1; font-size: 11px; padding: 4px 0; background: var(--surface2);
  border: 1px solid var(--border); color: var(--text2); border-radius: 5px; font-weight: 500;
}
.cd-sort button:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }
.cd-sort button.on { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
.cd-range { display: flex; gap: 6px; padding: 0 12px 8px; }
.cd-range input { width: 100%; min-width: 0; padding: 5px 7px; font-size: 12px; }
.col-dropdown-search {
  width: calc(100% - 24px); margin: 0 12px 6px; padding: 5px 8px; font-size: 12px; min-width: 0;
}
.col-dropdown-list { max-height: 232px; overflow-y: auto; scrollbar-width: thin; }
.col-dropdown-item {
  display: flex; align-items: center; gap: 8px; padding: 4px 12px; cursor: pointer;
  font-size: 12px; color: var(--text2); transition: background 0.1s;
}
.col-dropdown-item:hover { background: var(--surface2); }
.col-dropdown-item input[type=checkbox] {
  accent-color: var(--accent); width: 13px; height: 13px; cursor: pointer; min-width: 0; padding: 0;
}
.col-dropdown-item.on { color: var(--text); font-weight: 500; }
.col-dropdown-item .cd-n { margin-left: auto; color: var(--text3); font-size: 11px; font-variant-numeric: tabular-nums; }

.col-vis-panel {
  position: fixed; z-index: 9999; background: var(--surface); border: 1px solid var(--border2);
  border-radius: 10px; padding: 8px 0; max-height: 60vh; overflow-y: auto;
  min-width: 190px; box-shadow: var(--shadow-lg); display: none;
}
.col-vis-panel.open { display: block; }

.empty { padding: 44px 20px; text-align: center; color: var(--text3); font-size: 13px; }

@media (max-width: 900px) {
  .qf-split { flex-direction: column; }
  .qf-paste-col { width: 100%; min-width: 0; border-left: none; border-top: 1px solid var(--border); }
  .qf-section-label { width: 66px; font-size: 9.5px; }
  table.grid td.stick, table.grid th.stick { position: static; }
}

/* ── Inline editing in a document being priced ──────────────────────── */

/*
 * A box you can type in.
 *
 * The negative margin is what lines the columns up. The input padded itself by
 * six pixels inside a cell already padded by seven, so every figure sat six
 * pixels to the left of the heading above it — visible on every numeric column
 * and impossible to unsee once noticed. Pulling the box out by the same six
 * puts the text back on the cell's own edge while the focus ring still has
 * room to breathe.
 *
 * The hairline says the box is writable. A field that looks like text until you
 * click it is a field nobody clicks.
 */
table.grid input.cell {
  width: calc(100% + 12px); min-width: 0; margin: 0 -6px; padding: 3px 6px;
  font-size: 11.5px; text-align: right; font-variant-numeric: tabular-nums;
  border: 1px solid transparent; border-bottom: 1px solid var(--border2);
  border-radius: 4px; background: transparent;
  transition: border-color 0.12s ease, background 0.12s ease;
}
table.grid input.cell:hover {
  border-color: var(--border2); background: var(--surface);
}
table.grid input.cell:focus {
  border-color: var(--accent); background: var(--surface); box-shadow: 0 0 0 2px var(--accent-dim);
}
/* Nothing to type into: no invitation to. */
table.grid input.cell[readonly], table.grid input.cell:disabled {
  border-bottom-color: transparent;
}
table.grid input.cell.rap { color: var(--text3); }
table.grid td.c-sel input[type=checkbox] { accent-color: var(--sold); width: 12px; height: 12px; }
table.grid input.cell.unusable { color: var(--text3); font-style: italic; }
table.grid input.cell.unusable::placeholder { font-size: 10px; letter-spacing: .02em; }
/* A Rap price taken from a lighter band is still right, but it is not obvious
   from the number, so the cell says so on hover and marks itself. */
table.grid input.cell.rap.fellback { color: var(--memo); border-bottom: 1px dotted var(--memo); }

/* ── Dark mode ──────────────────────────────────────────────────────── */

/*
 * The same palette turned over: warm near-black surfaces, the accent inverted
 * to near-white so it still reads as "this one", and the four status colours
 * lifted to stay legible on a dark ground. They keep their meanings — green
 * available, amber memo, red sold, purple complete — because a colour that
 * means one thing by day and another by night means nothing.
 */
:root[data-theme="dark"] {
  --bg: #1C1917;
  --surface: #292524;
  --surface2: #322D2A;
  --surface3: #3F3A36;
  --border: #3A3532;
  --border2: #4E4741;
  --accent: #FAFAF9;
  --accent-dim: rgba(250,250,249,0.10);
  --text: #FAFAF9;
  --text2: #D6D3D1;
  --text3: #A8A29E;

  --available: #4ADE80;  --available-bg: #14301F;
  --memo:      #FCD34D;  --memo-bg:      #332A10;
  --sold:      #F87171;  --sold-bg:      #3A1A1A;
  --complete:  #C4B5FD;  --complete-bg:  #2A2340;

  /* The same three lights, dimmed rather than inverted. */
  --calm-go-bg:   #1B2E22;  --calm-go-ink:   #86D6A2;
  --calm-hmm-bg:  #302A18;  --calm-hmm-ink:  #DFC177;
  --calm-stop-bg: #33201F;  --calm-stop-ink: #DE9C9C;

  --good: #4ADE80;
  --bad: #F87171;
  --on-accent: #1C1917;
  --flash-ok-ink: #A7F3D0;
  --flash-err-ink: #FECACA;
  --shadow: 0 1px 2px rgba(0,0,0,.4);
  --shadow-lg: 0 8px 30px rgba(0,0,0,.55);

  /* Native controls — selects, date pickers, checkboxes, scrollbars — are drawn
     by the browser, and without this they stay white with black text. */
  color-scheme: dark;
}

/* The status tags set their own border colours, which have to turn over too. */
:root[data-theme="dark"] .tag.owned,
:root[data-theme="dark"] .tag.available { border-color: #1E5235; }
:root[data-theme="dark"] .tag.memo,
:root[data-theme="dark"] .tag.on_memo_out,
:root[data-theme="dark"] .tag.prov { border-color: #5A4715; }
:root[data-theme="dark"] .tag.sold { border-color: #5E2626; }
:root[data-theme="dark"] .tag.complete { border-color: #3E3468; }

/*
 * No blanket button rule here. There was one, and it painted dark ink onto
 * every outline button — which on a dark ground is invisible. The filled
 * buttons take their ink from --on-accent above; every other button already
 * names its own colour.
 */
:root[data-theme="dark"] .toolbar-btn.primary { color: var(--accent); }
:root[data-theme="dark"] .filter-count { color: var(--surface); }

/* Documents are printed, so they stay on white whatever the screen is doing. */
/* Documents are a separate page with their own stylesheet, so they are already
   unaffected by the theme — which is right, because they get printed. */

.theme-toggle {
  background: none; border: 1px solid var(--border2); border-radius: 20px;
  padding: 3px 12px; font-size: 11px; color: var(--text3); cursor: pointer;
  font-weight: 500; letter-spacing: .02em;
}
.theme-toggle:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }
.theme-on-dark { display: none; }
:root[data-theme="dark"] .theme-on-light { display: none; }
:root[data-theme="dark"] .theme-on-dark { display: inline; }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) .theme-on-light { display: none; }
  :root:not([data-theme="light"]) .theme-on-dark { display: inline; }
}

/* ── The selection, made hard to walk away from ─────────────────────── */

.selection-badge {
  display: inline-flex; align-items: center; gap: 7px;
  background: var(--memo-bg); border: 2px solid var(--memo); color: var(--memo);
  border-radius: 20px; padding: 3px 13px; font-size: 12px; font-weight: 600;
  text-decoration: none; letter-spacing: .01em;
}
.selection-badge:hover { filter: brightness(1.04); }
.selection-badge .dot {
  width: 7px; height: 7px; border-radius: 50%; background: var(--memo);
  animation: om-pulse 2.4s ease-in-out infinite;
}
@keyframes om-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .35; } }
@media (prefers-reduced-motion: reduce) { .selection-badge .dot { animation: none; } }

.nav-count {
  display: inline-block; background: var(--memo); color: var(--memo-bg);
  border-radius: 10px; padding: 0 6px; font-size: 10px; font-weight: 700;
  min-width: 15px; text-align: center; vertical-align: 1px;
}

/* Owed for too long. Amber is the ordinary state of this badge — it is an open
   obligation, not an alarm — and it turns red only once the oldest of them has
   been sitting for two months, which is long enough that somebody should have
   asked by now. */
.nav-count.late { background: var(--sold); color: var(--sold-bg); }

/* On the stock list, one pill. Framing the whole bar shouted, and a thing that
   shouts every time you tick a stone stops being read after the first day. */
.sel-pill {
  display: inline-block; background: var(--sold); color: #fff;
  border-radius: 20px; padding: 2px 11px; font-size: 11.5px; font-weight: 600;
  letter-spacing: .01em;
}

/* ── Task menu ──────────────────────────────────────────────────────── */

.task-menu { position: relative; display: inline-block; }
.task-menu > .toolbar-btn::after { content: ' ▾'; opacity: .6; }
.task-list {
  display: none; position: absolute; right: 0; top: calc(100% + 4px); z-index: 200;
  background: var(--surface); border: 1px solid var(--border2); border-radius: 8px;
  box-shadow: var(--shadow-lg); padding: 5px 0; min-width: 236px; text-align: left;
}
.task-menu.open .task-list { display: block; }
.task-list button, .task-list a {
  display: block; width: 100%; text-align: left; background: none; border: none;
  padding: 8px 14px; font: inherit; font-size: 12.5px; color: var(--text2);
  cursor: pointer; text-decoration: none; border-radius: 0;
}
.task-list button:hover, .task-list a:hover { background: var(--surface2); color: var(--text); opacity: 1; }
.task-list button:disabled { opacity: .38; cursor: default; }
.task-list button:disabled:hover { background: none; color: var(--text2); }
.task-list .sep { height: 1px; background: var(--border); margin: 5px 0; }
.task-list .head {
  padding: 4px 14px 5px; font-size: 10px; text-transform: uppercase;
  letter-spacing: .07em; color: var(--text3); font-weight: 600;
}

/* ── Conditional colour in the grid ─────────────────────────────────── */

/*
 * Carried over from the Admin system, meanings unchanged. Green is where a
 * stone should be, red is where it should not, amber is "look at this".
 */
table.grid td.p-oversize {
  background: var(--available-bg) !important; color: var(--available); font-weight: 600;
}
table.grid tbody tr.selected td.p-oversize,
table.grid tbody tr:hover td.p-oversize {
  background: linear-gradient(var(--accent-dim), var(--accent-dim)), var(--available-bg) !important;
}
table.grid td.p-triple span,
table.grid td.p-triple { color: var(--complete); font-weight: 600; }
table.grid td.p-fluo-strong { color: var(--sold); font-weight: 600; }
table.grid td.p-fluo-strong::after { content: ''; }
table.grid td.p-fluo-medium { color: var(--sold); }
table.grid td.p-good { color: var(--available); font-weight: 500; }
/* Clean on what was graded, with something nobody looked at. Green, because it
   is the good answer, but not the confident weight of a full one. */
table.grid td.p-good-soft { color: var(--available); opacity: .72; }
table.grid td.p-bad { color: var(--sold); font-weight: 500; }
table.grid td.p-bad-strong { color: var(--sold); font-weight: 700; }
table.grid td.p-warn { color: var(--memo); font-weight: 600; }
table.grid td.p-strong { color: var(--text); font-weight: 600; }
table.grid td .dash { color: var(--border2); }

/* A rule down the left edge where one block of columns ends and the next
   begins, so the price columns can be found without counting across. */
table.grid th.grp, table.grid td.grp { border-left: 1px solid var(--border2); }

table.grid td a { color: var(--accent); text-decoration: none; font-size: 11px; }
table.grid td a:hover { text-decoration: underline; }

/* The icon takes the same ink as the links beside it, and does not underline:
   an underline under a 16-pixel arrow reads as a smudge. */
table.grid td a.icon-btn:hover { text-decoration: none; }

/* An action that has to be a POST — it does something rather than going
   somewhere — but belongs beside the links in a row and should not shout. */
button.linkish {
  background: none; border: 0; padding: 0; margin: 0 0 0 10px;
  color: var(--accent); font-size: 11px; font-weight: 400; cursor: pointer;
}
button.linkish:hover { text-decoration: underline; opacity: 1; }
button.linkish[disabled] { color: var(--text3); cursor: default; text-decoration: none; }

.note-chip {
  display: inline-block; border-radius: 4px; padding: 1px 5px; font-size: 10px;
  margin-right: 3px; background: var(--surface2); color: var(--text2); white-space: nowrap;
}
.note-chip.bad { background: var(--sold-bg); color: var(--sold); }
.note-chip.good { background: var(--available-bg); color: var(--available); }

/* The single status, in the Admin's colours. Memo-in stock takes the fourth
   colour: it is here and sellable, but it is not ours. */
.tag.memo_out { background: var(--memo-bg); color: var(--memo); border-color: #FDE68A; }
.tag.returned, .tag.written_off, .tag.unavailable {
  background: var(--surface2); color: var(--text3); border-color: var(--border2);
}
:root[data-theme="dark"] .tag.memo_out { border-color: #5A4715; }

:root[data-theme="dark"] .tag.memo_in { border-color: #3E3468; }

/* ── A table read across, never down ────────────────────────────────── */

/*
 * One line per row, always. A row that grows to two because a vendor has a long
 * name makes the whole table stop being scannable, and these are read while
 * somebody is waiting on the phone. Anything too long is cut with an ellipsis
 * and kept in the title attribute.
 */
table.tight { table-layout: auto; }
table.tight th, table.tight td {
  height: 30px; padding: 3px 8px; white-space: nowrap;
  overflow: hidden; text-overflow: ellipsis; max-width: 220px;
}
table.tight td { vertical-align: middle; line-height: 1.2; }
table.tight input, table.tight select {
  padding: 3px 6px; font-size: 12px; height: 24px; min-width: 0;
}
table.tight input[type=checkbox] { height: auto; accent-color: var(--accent); }
table.tight .tag { padding: 1px 7px; font-size: 10px; }

/* ── A register rather than a grid ───────────────────────────────────────
   Six rows on a wide screen with room to spare. `tight` is sized for a
   hundred rows of stock and reads as cramped here, where every cell is two
   facts stacked and the columns are read across rather than scanned down. */
table.register th, table.register td {
  height: auto; padding: 10px 14px; max-width: none; vertical-align: top;
}
table.register td { line-height: 1.45; }
table.register thead th { white-space: nowrap; }
table.register td .hint { font-size: 11px; line-height: 1.4; }

/* The actions, kept apart. Two links touching read as one word — "Edit Columns"
   was exactly that — so they are spaced and the two that are always the same
   thing became icons. */
.row-actions { display: flex; align-items: center; gap: 14px; justify-content: flex-end; }
.row-actions form { margin: 0; }
.row-actions .icon-btn { margin-left: 0; }
.row-actions a.words { white-space: nowrap; }

/* ── Totals ─────────────────────────────────────────────────────────── */

/*
 * Pinned to the bottom of the panel. A total you have to scroll to the end of
 * two hundred rows to find is a total that gets worked out on a calculator.
 */
table.grid tfoot td {
  position: sticky; bottom: 0; z-index: 7;
  background: var(--surface2); border-top: 2px solid var(--border2);
  font-weight: 600; color: var(--text); white-space: nowrap;
}
table.grid tfoot td.stick { z-index: 8; }
table.grid tfoot tr.foot-sel td {
  background: var(--sold-bg); color: var(--sold); border-top: 1px solid var(--sold);
}
table.grid tfoot tr.foot-sel td b { color: var(--sold); }
table.grid tfoot tr.foot-all td b { color: var(--text); }

/* All and none inside a column filter. */
.cd-bulk { display: flex; gap: 5px; padding: 0 12px 7px; }
.cd-bulk button {
  flex: 1; font-size: 11px; padding: 4px 0; background: var(--surface2);
  border: 1px solid var(--border); color: var(--text2); border-radius: 5px; font-weight: 500;
}
.cd-bulk button:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }

/* ── Filters folded away ────────────────────────────────────────────── */

/*
 * Scrolling down folds the filters and gives the room to the stones; pointing
 * at what is left brings them back. The bar stays, so the count of what is
 * filtered never disappears and there is always something to point at.
 *
 * Folded means folded: what is left is one thin strip. The rows collapse but
 * their container's padding and their hairline borders do not go with them, and
 * a full-height bar underneath spent another forty pixels on a sentence — some
 * eighty in all, which is two rows of stones not shown for no reason.
 */
/* The first beat of folding away: gone to look at, still taking its room. The
   second beat takes the room, in one step, once this has finished. */
.quick-filters.fading .qf-group-section,
.quick-filters.fading .qf-paste-col { opacity: 0; pointer-events: none; }

.quick-filters.folded { margin-bottom: 6px; }
.quick-filters.folded .qf-rows { padding: 0; }
.quick-filters.folded .qf-group-section {
  max-height: 0; opacity: 0; pointer-events: none; border-top-width: 0;
}
.quick-filters.folded .qf-paste-col {
  max-height: 0; width: 0; min-width: 0; opacity: 0; pointer-events: none;
  /* Border-box clamps a zero width to the border, so the divider would survive
     as a hairline. It is not animated: one pixel does not need easing. */
  border-left-width: 0;
}
.quick-filters.folded .qf-foot {
  border-top: none; border-radius: var(--radius);
  padding: 3px 12px; gap: 8px; min-height: 0;
}
.quick-filters.folded .clear-btn { padding: 2px 11px; font-size: 11px; }
.quick-filters.folded .save-filter-btn,
.quick-filters.folded .saved-filter-chip { padding: 2px 9px; font-size: 11px; }

/*
 * Says what happened and how to undo it, and only while it is true. Not a
 * button on purpose: the folded bar itself is the target, and a whole strip is
 * easier to point at than a word at the end of one.
 */
.fold-hint {
  margin-left: auto; font-size: 11.5px; color: var(--text3);
  white-space: nowrap; user-select: none;
}
.quick-filters:not(.folded) .fold-hint { display: none; }

@media (prefers-reduced-motion: reduce) {
  .qf-group-section, .qf-paste-col { transition: none; }
}

/* ── One stone's life ───────────────────────────────────────────────── */

table.grid .c-hist { width: 24px; min-width: 24px; text-align: center; padding: 5px 2px; }
/*
 * The number on an allocation screen, and the line under it saying what the
 * document is for. The link opens the document in a panel over the list, so
 * looking it up does not cost the amounts already typed into the form.
 */
/*
 * The hidden attribute, meant.
 *
 * `hidden` is only `display: none` at the weakest specificity there is, so any
 * rule that gives an element a display — and the form rows here all do — wins
 * against it, and a block marked hidden sits there in plain sight. This is the
 * one place to say it once.
 */
[hidden] { display: none !important; }

a.doc-link { font-weight: 600; color: var(--text); text-decoration: none; border-bottom: 1px dotted var(--border2); }
a.doc-link:hover { color: var(--accent); border-bottom-color: var(--accent); }
.doc-says {
  font-size: 11px; color: var(--text3); margin-top: 2px;
  /* The grid keeps its cells on one line, which is right for a column of
     figures and wrong for a sentence: without this the sentence is cut off
     mid-word by the cell beside it. */
  white-space: normal; max-width: 300px; line-height: 1.35;
}

a.row-history {
  display: inline-block; width: 18px; line-height: 16px; border-radius: 4px;
  color: var(--text3); text-decoration: none; font-size: 13px; font-weight: 700;
}
a.row-history:hover { background: var(--accent-dim); color: var(--accent); text-decoration: none; }

.stone-overlay {
  position: fixed; inset: 0; z-index: 9998; background: rgba(0,0,0,.42);
  display: flex; align-items: flex-start; justify-content: center; padding: 5vh 16px;
  overflow: auto;
}
.stone-panel {
  position: relative; background: var(--surface); border: 1px solid var(--border2);
  border-radius: var(--radius); box-shadow: var(--shadow-lg);
  width: 100%; max-width: 1020px; padding: 18px 20px 20px;
}
.stone-close {
  position: absolute; top: 10px; right: 12px; background: none; border: none;
  color: var(--text3); font-size: 22px; line-height: 1; cursor: pointer; padding: 2px 6px;
}
.stone-close:hover { color: var(--text); opacity: 1; }

.stone-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 20px; }
.stone-parcel { font-family: var(--mono); font-size: 17px; font-weight: 600; color: var(--text); }
.stone-desc { font-size: 13px; color: var(--text2); margin-top: 3px; }
.stone-facts { display: flex; align-items: center; gap: 10px; font-size: 12px; white-space: nowrap; }

.stone-strip {
  display: flex; flex-wrap: wrap; gap: 8px 26px; margin: 14px 0 16px;
  padding: 11px 14px; background: var(--surface2); border-radius: var(--radius);
  font-size: 12.5px; color: var(--text);
}
.stone-strip .k {
  display: block; font-size: 10px; text-transform: uppercase; letter-spacing: .05em;
  color: var(--text3); font-weight: 600; margin-bottom: 1px;
}

/*
 * Green in, amber out, back to plain when it returns. The same four meanings
 * the rest of the system uses, so a life reads at a glance.
 */
table.stone-life td { border-bottom: 1px solid var(--border); }
table.stone-life .life-dot {
  display: inline-block; width: 7px; height: 7px; border-radius: 50%;
  margin-right: 7px; background: var(--border2); vertical-align: 1px;
}
tr.life-in   .life-dot { background: var(--available); }
tr.life-out  .life-dot { background: var(--memo); }
tr.life-back .life-dot { background: var(--complete); }
tr.life-move .life-dot { background: var(--border2); }

/* ── A stone that has left ──────────────────────────────────────────── */

/*
 * Dimmed rather than hidden. Looking up something you sold last month is an
 * ordinary thing to want, and a sold stone that looks identical to one on the
 * shelf is how somebody promises a customer a diamond that is already gone.
 */
table.grid tbody tr.gone td { color: var(--text3); }
table.grid tbody tr.gone td.c-parcel { color: var(--text3); font-weight: 400; }
table.grid tbody tr.gone .tag { opacity: .55; }
table.grid tbody tr.gone:hover td { color: var(--text2); }

.gone-mark {
  display: inline-block; font-size: 9px; font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; color: var(--sold);
  background: var(--sold-bg); border-radius: 3px; padding: 1px 3px; line-height: 1.3;
}

/* ---------------------------------------------------------- the bank ledger
 * A reason is only worth as much as the evidence under it, so the page says how
 * sure it is on every row rather than presenting all of them as equally settled.
 * The unsure ones are tinted, not shouted at: they are questions, not errors.
 */
table.grid td.nowrap, table.grid th.nowrap { white-space: nowrap; }

table.grid tr.unsure td { background: var(--memo-bg); }

.conf {
  display: inline-block;
  padding: 1px 7px;
  border-radius: 999px;
  font-size: 10px;
  font-weight: 600;
  letter-spacing: 0.02em;
  border: 1px solid transparent;
}
.conf-exact  { color: var(--available); background: var(--available-bg); border-color: var(--available); }
.conf-strong { color: var(--complete);  background: var(--complete-bg);  border-color: var(--complete); }
.conf-likely { color: var(--memo);      background: var(--memo-bg);      border-color: var(--memo); }
.conf-guess  { color: var(--sold);      background: var(--sold-bg);      border-color: var(--sold); }

/*
 * Once a person has matched the line, how sure the reading was stops being a
 * question. The chip still says what it said, because the record of the
 * reading is worth keeping, but it goes grey and gets out of the way: a
 * colour on a settled row is a signal pointing at nothing to do.
 */
.conf-settled {
  color: var(--text3) !important;
  background: var(--surface2) !important;
  border-color: transparent !important;
  opacity: 0.75;
}

/* The evidence is a sentence, not a code. It gets room to be one. */
table.grid td.evidence {
  white-space: normal;
  line-height: 1.35;
  color: var(--text2);
  font-size: 11px;
  min-width: 340px;
  max-width: 560px;
}

/* ── A calm grid ────────────────────────────────────────────────────
 *
 * For the ledger, which is fifty rows of the same shape read top to bottom
 * rather than a dozen rows scanned for the odd one out. What made it hectic
 * was that everything on it was coloured at once: the amount green or red on
 * every single row, a four-hue pill in the confidence column, a green pill
 * again under Matched, and the unsure rows tinted yellow underneath the lot.
 * Four signals competing means none of them is a signal (Shay, 2026-08-28).
 *
 * So: one coloured thing per row, in pastel, and everything else grey. The
 * rows are one line each, which is the other half of calm — the evidence
 * sentence used to wrap to three lines and no two rows were the same height,
 * so the eye had nothing straight to run down.
 */
table.grid.calm { font-size: 12.5px; }
table.grid.calm thead th { font-size: 11.5px; padding: 7px 9px; }
table.grid.calm tbody td {
  padding: 5px 9px;
  line-height: 1.6;
  /* Full ink, not the muted grey the dense grid uses. This one is read, not
   * scanned, and at fifty rows the second-tier grey was hard work in the dark
   * theme (Shay, 2026-08-28). The hierarchy is carried by the chips instead. */
  color: var(--text);
}

/* Not tinted. The chip in the row already says the reason is a reading. */
table.grid.calm tbody tr.unsure td { background: transparent; }

/* Only what went out. Money in is the ordinary case and does not need
 * announcing; a column where every cell is coloured says nothing at all. */
table.grid.calm td.n { font-variant-numeric: tabular-nums; color: var(--text); }
table.grid.calm td.p-good { color: var(--text); font-weight: 500; }
table.grid.calm td.p-bad  { color: var(--calm-stop-ink); font-weight: 500; }

/* One line each, and the whole sentence on hover. */
/* What the bank itself wrote on the line, beside whatever we made of it.
   Quieter, on the same line, and cut off by the cell rather than given a line
   of its own: rows that are all one line high is half of what makes this table
   readable, and the whole wording is on hover. */
.bank-said { color: var(--text3); }
.bank-said::before { content: '· '; }

table.grid.calm td.evidence {
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  /* Wider than it was, because the bank's own wording now shares the cell and
     on an unexplained line it is the only thing on the row that says anything. */
  max-width: 520px;
  min-width: 0;
  font-size: 12px;
  color: var(--text2);
}

/* The chip: a soft ground, no border, square-ish like everything else on the
 * page. A ring and a pill and a tint were three shapes saying one thing. */
table.grid.calm .conf {
  border: none;
  border-radius: 4px;
  padding: 1px 6px;
  font-weight: 500;
  letter-spacing: 0;
  background: var(--surface2);
  color: var(--text3);
}
table.grid.calm .conf-exact,
table.grid.calm .conf-strong { background: var(--calm-go-bg);   color: var(--calm-go-ink); }
table.grid.calm .conf-likely { background: var(--calm-hmm-bg);  color: var(--calm-hmm-ink); }
table.grid.calm .conf-guess  { background: var(--calm-stop-bg); color: var(--calm-stop-ink); }
table.grid.calm .conf-settled {
  background: var(--surface2);
  color: var(--text3);
  opacity: 0.75;
}

/* ── The pills in the first column ──────────────────────────────────
 *
 * What you can do to a line, at the front where the eye starts, in one shape.
 * Match disappears once the line is matched — not dimmed, gone (Shay). A
 * disabled control on most of the rows is a column of things you cannot press,
 * and absence says "dealt with" more plainly than any label could.
 *
 * Edit stays on every row. The reason is a reading and readings are wrong
 * sometimes: the wrong company picked out of a description, or a receipt found
 * later that the bank text never mentioned.
 */
table.grid.calm td.acts { white-space: nowrap; }
table.grid.calm .pill-btn {
  display: inline-block;
  padding: 2px 11px;
  margin-right: 4px;
  border-radius: 999px;
  font-size: 11.5px;
  font-weight: 500;
  line-height: 1.5;
  text-decoration: none;
  border: 1px solid var(--border2);
  color: var(--text2);
  background: var(--surface);
  transition: border-color 0.15s, color 0.15s;
}
table.grid.calm .pill-btn:hover { border-color: var(--accent); color: var(--accent); text-decoration: none; }

/* Still to do: the pill that asks to be pressed. */
table.grid.calm .pill-btn.act { background: var(--accent-dim); border-color: var(--accent); color: var(--accent); }

/* Done: the same pill, gone quiet. It is still there because a reading can
 * turn out to be wrong after it was matched, but it has stopped asking. */
table.grid.calm .pill-btn.dim {
  background: none;
  border-color: transparent;
  color: var(--text3);
}
table.grid.calm .pill-btn.dim:hover { border-color: var(--border2); color: var(--text2); }

/* ─────────────────────────────────────────────────────────────────────────────
   Address lookup.

   One box that answers as well as asks. The suggestion list is the only place
   in the app that floats over a form, so it borrows the panel's own surface and
   shadow rather than inventing a second look for the same idea.
   ───────────────────────────────────────────────────────────────────────────── */
.addr-find { position: relative; }
.addr-search { width: 260px; }
.addr-search.addr-busy { opacity: 0.6; }

.addr-drop {
  position: absolute; top: calc(100% + 4px); left: 0; z-index: 300;
  min-width: 320px; max-width: 460px; max-height: 300px; overflow-y: auto;
  background: var(--surface); border: 1px solid var(--border2);
  border-radius: var(--radius-sm); box-shadow: var(--shadow-lg); padding: 4px;
}
.addr-opt { padding: 7px 10px; border-radius: var(--radius-sm); cursor: pointer; }
.addr-opt:hover, .addr-opt.on { background: var(--surface2); }
.addr-opt-main { font-size: 13px; color: var(--text); }
.addr-opt-sub { font-size: 11px; color: var(--text3); margin-top: 1px; }

/* The panel the memo page opens when the customer chosen has no address on
   file. Amber, because it is the same "this is not finished" as a stone out on
   memo, not an error. */
.addr-wanted {
  border: 1px solid var(--memo); background: var(--memo-bg);
  border-radius: var(--radius); padding: 14px 16px; margin-bottom: 16px;
}
.addr-wanted .addr-why { font-size: 13px; color: var(--memo); margin-bottom: 10px; line-height: 1.5; }
.addr-wanted .addr-why b { font-weight: 600; }

/* Nothing found by a machine is anybody's address until this is ticked. It sits
   under the fields it is talking about, and it is a sentence rather than a
   label, because agreeing to it is a decision and not a formality. */
.addr-approve {
  margin-top: 10px; padding: 9px 12px;
  border: 1px solid var(--border2); border-left: 3px solid var(--accent);
  border-radius: var(--radius-sm); background: var(--surface2);
}
.addr-approve label {
  display: flex; align-items: baseline; gap: 9px; cursor: pointer;
  font-size: 13px; color: var(--text2); line-height: 1.5;
}
.addr-approve input[type=checkbox] { min-width: 0; margin: 0; position: relative; top: 2px; }
.addr-approve b { color: var(--text); font-weight: 600; }
.addr-found { color: var(--text); }

/* ------------------------------------------------------------- icon buttons
 * A download sign, drawn rather than typed. The glyph is white, which needs
 * something behind it: on the page's own background a white arrow is an empty
 * space, so it sits on the accent disc and stays legible in both themes.
 */
/*
 * Download: the glyph and nothing else.
 *
 * It was a filled disc with the arrow knocked out of it, which put a white
 * shape on a white ground in dark mode and a black one on black in light — a
 * plain circle either way. A bare arrow has no ground to disappear into: it is
 * ink on the page like the Edit link beside it, and it takes the link colour
 * the rest of the row uses.
 */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 20px;
  height: 20px;
  margin-left: 8px;
  background: none;
  border: 0;
  cursor: pointer;
  text-decoration: none;
  vertical-align: middle;
  transition: opacity 0.12s ease;
}
.icon-btn:hover { opacity: .6; }
.icon-btn:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 3px; }

/* A line above the grid while something is happening to it: the vendors'
   listings being fetched, or failing to. It exists only while it has something
   to say, because a bar that is always there is a bar nobody reads. */
.grid-note {
  margin: 0 0 8px; padding: 7px 12px; border-radius: var(--radius);
  background: var(--surface2); border: 1px solid var(--border);
  color: var(--text2); font-size: 12px;
}
/* How many of the matching rows are actually drawn. Quiet, because it is a fact
   about the screen and not about the stock. */
.grid-more {
  font-size: 11.5px; color: var(--text3); padding: 6px 2px 0; text-align: center;
}

.grid-note.bad { background: var(--sold-bg); border-color: var(--sold); color: var(--sold); }

/* Read out, never shown. The disc alone says download to anyone who can see it
 * and nothing at all to anyone who cannot. */
.visually-hidden {
  position: absolute;
  width: 1px; height: 1px;
  margin: -1px; padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  white-space: nowrap;
  border: 0;
}

/* A field with an answer rather than a choice. Sized like the inputs beside it
 * so a row does not jump when one of them stops being a picker. */
.static-field {
  min-height: 32px;
  padding: 7px 10px;
  border: 1px solid var(--border);
  border-radius: var(--radius-sm);
  background: var(--surface2);
  color: var(--text);
  font-weight: 500;
}
.static-field.muted { color: var(--text3); font-weight: 400; }
.static-field.p-bad { color: var(--sold); border-color: var(--sold); background: var(--sold-bg); }

/* A row that cannot take this money. Dimmed rather than hidden: seeing that an
 * invoice exists and belongs to somebody else is more use than an empty table. */
table.grid tr.dimmed td { opacity: 0.42; }
table.grid tr.dimmed input { pointer-events: none; }

/* A second opinion, not a refusal. Amber sits between the two so a line the
 * ledger will not take never looks the same as one worth checking. */
.flash.warn-flash {
  background: var(--memo-bg);
  border-color: var(--memo);
  color: var(--memo);
}

/* A line priced under what the stone cost. Marked as it is typed, because
 * finding it in a profit report a month later is too late to do anything. */
table.lines tr.under-cost td { background: var(--memo-bg); }
.under-cost-note { color: var(--memo); font-size: 10px; font-weight: 600; white-space: nowrap; }

/* The total at the foot of the column it totals. Ruled off above so the eye
 * stops there, and never striped like a line that could be edited. */
table.grid tfoot tr.line-total td {
  border-top: 2px solid var(--border2);
  background: var(--surface2);
  font-weight: 600;
  padding-top: 9px;
  padding-bottom: 9px;
}
table.grid tfoot tr.line-total td:empty { background: var(--surface2); }

/* ─────────────────────────────────────────── the document's own line table
 * Numbers here read from the left, not the right (Shay's, 2026-08-28).
 *
 * Right alignment is the usual choice for money because it lines decimal
 * points up, and it is the better one where columns are read down and
 * compared. A document being priced is not that: the boxes are mostly empty,
 * they are filled left to right, and a figure pinned to the far edge of an
 * empty box reads as the end of something rather than the start of it. The
 * heading, the box and the total all move together so the column still has
 * one edge.
 *
 * tabular-nums stays, so the digits keep the same width and the figures still
 * stack cleanly even from the left.
 */
#lineTable thead th.num,
#lineTable tbody td.num,
#lineTable tfoot td.num { text-align: left; }

#lineTable input.cell { text-align: left; }

/* Reading from the left, it is the left edges that have to meet: the heading's
 * and the total's sit on the cell's padding, so the box's text does too. */
#lineTable input.cell { margin-left: -6px; margin-right: -6px; }


/* ── A dropdown you can type at ─────────────────────────────────────
 *
 * See public/pick.js. The native select is still the control the form
 * submits; it is only out of sight. Everything here is the box in front of it.
 */
.pick { position: relative; display: inline-block; min-width: 90px; }

/* Present, submitted, and not in the way. Not display:none, because a control
 * that is display:none is one the browser will not read a value from in some
 * older engines, and this one carries the answer. */
.pick-native {
  position: absolute; left: 0; bottom: 0;
  width: 1px; height: 1px; opacity: 0; pointer-events: none; border: none; padding: 0;
  min-width: 0;
}

.pick-input { width: 100%; cursor: text; }
.pick.is-disabled .pick-input { cursor: not-allowed; }

.pick-list {
  display: none;
  position: absolute; z-index: 60; top: calc(100% + 4px); left: 0; right: 0;
  max-height: 280px; overflow-y: auto;
  background: var(--surface);
  border: 1.5px solid var(--border2);
  border-radius: var(--radius-sm);
  box-shadow: 0 8px 24px rgba(0,0,0,0.12);
  padding: 4px;
}
.pick.open .pick-list { display: block; }

.pick-opt {
  padding: 6px 9px;
  border-radius: var(--radius-sm);
  font-size: 13px;
  color: var(--text);
  cursor: pointer;
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* What you typed, where it matched. The only ink on the row. */
.pick-opt b { font-weight: 700; }
.pick-opt.on { background: var(--accent-dim); }
.pick-opt.is-chosen { font-weight: 600; }

.pick-none { padding: 8px 9px; font-size: 12px; color: var(--text3); }


/* ── Sorting, from the column's own menu ────────────────────────────
 *
 * In the menu rather than on the header, because clicking a header to sort and
 * clicking it to filter cannot both be the first thing a click does. The menu
 * is already where the column's questions are asked.
 */
.cd-sort { display: flex; gap: 6px; padding: 6px 8px 2px; }
.cd-sort-btn {
  flex: 1; padding: 5px 8px; font-size: 11.5px; font-weight: 500;
  background: var(--surface2); color: var(--text2);
  border: 1px solid var(--border); border-radius: var(--radius-sm); cursor: pointer;
}
.cd-sort-btn:hover { border-color: var(--border2); color: var(--text); }
.cd-sort-btn.on { background: var(--accent-dim); color: var(--text); border-color: var(--border2); }

/* ── What needs doing ───────────────────────────────────────────────── */

/*
 * The queue at the top of the front page, and the whole of /today.
 *
 * Three lights and no fourth. Red is late or broken, amber is coming, grey is
 * worth knowing — the same traffic light the bank ledger uses, in the same
 * pastel, because a second vocabulary of colour is a second thing to learn.
 *
 * The row is the link. A queue that names work and then leaves you to find the
 * screen it is done on is a list of complaints.
 */
.queue { padding: 0; }
.queue-item {
  display: flex; gap: 11px; align-items: flex-start; text-decoration: none;
  padding: 12px 14px; border-bottom: 1px solid var(--border); color: inherit;
}
.queue-item:last-child { border-bottom: 0; }
.queue-item:hover { background: var(--surface2); }
.queue-item .lamp {
  width: 8px; height: 8px; border-radius: 50%; flex: none; margin-top: 5px;
  background: var(--text3);
}
.queue-item.now .lamp { background: var(--calm-stop-ink); }
.queue-item.soon .lamp { background: var(--calm-hmm-ink); }
.queue-item.watch .lamp { background: var(--border2); }
.queue-what { flex: 1; min-width: 0; }
.queue-title { display: block; font-size: 13px; font-weight: 500; color: var(--text); }
.queue-item.now .queue-title { color: var(--calm-stop-ink); }
.queue-detail {
  display: block; font-size: 12px; color: var(--text3); line-height: 1.55; margin-top: 2px;
}
.queue-amount {
  font-family: var(--mono); font-size: 12.5px; color: var(--text2); white-space: nowrap;
  font-variant-numeric: tabular-nums; padding-top: 1px;
}
.queue-clear { padding: 18px 16px; color: var(--text3); font-size: 12.5px; line-height: 1.6; }

/*
 * The filing control, wide enough to read the answer in.
 *
 * "36260001 · YERUSHALM…" is not a choice anybody can check before pressing
 * File. The list is long enough that pick.js turns it into a typeahead, and a
 * typeahead whose box is a hundred pixels wide shows you the first word of what
 * you have chosen — which is the one part you already knew.
 *
 * So the control takes the width of its column and the button sits under it
 * rather than beside it, because in a table cell the two of them side by side
 * means neither has room.
 */
form.filer { flex-wrap: wrap; }
form.filer select,
form.filer .pick { flex: 1 1 100%; max-width: none; min-width: 0; }
form.filer .pick-input { font-size: 12px; padding: 4px 6px; }
form.filer button { flex: 0 0 auto; }

/* Options are three facts — number, party, money — and the party is the one
   that gets cut. Given room, the list shows all three. */
.pick-list .pick-opt { white-space: normal; line-height: 1.45; }

/*
 * A settlement, shown before it happens.
 *
 * Shay, on being taken to the settle list with nineteen of twenty-two rows
 * ticked for him: "i think it is hecktick. it better just pop up a window with
 * the diamonds it is about to assign." A list you have to check is work; a list
 * that is only what is about to happen is a thing you read once and agree with.
 *
 * Native <dialog>, so escape closes it and nothing underneath can be reached
 * while it is open.
 */
dialog.sheet {
  border: 1px solid var(--border2); border-radius: 10px; padding: 0;
  background: var(--surface); color: var(--text);
  width: min(1040px, 94vw); max-height: 88vh; overflow: auto;
  box-shadow: 0 24px 60px rgba(0,0,0,0.22);
}
dialog.sheet::backdrop { background: rgba(12,10,9,0.42); }

.sheet-top {
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  padding: 14px 18px; border-bottom: 1px solid var(--border);
  position: sticky; top: 0; background: var(--surface); z-index: 1;
}
.sheet-said {
  padding: 12px 18px; font-size: 12.5px; line-height: 1.65; color: var(--text2);
  border-bottom: 1px solid var(--border);
}
.sheet-said:last-of-type { border-bottom: 0; }
dialog.sheet table { width: 100%; border-collapse: collapse; }
dialog.sheet tfoot td { border-top: 2px solid var(--border2); }

.sheet-do {
  display: flex; align-items: center; gap: 18px;
  padding: 14px 18px; border-top: 1px solid var(--border); background: var(--surface2);
  position: sticky; bottom: 0;
}
.sheet-do form { margin: 0; }

/* A button that has to sit inside a sentence, because the sentence is what
   makes it safe to press. */
button.linky {
  background: none; border: 0; padding: 0; font: inherit; color: inherit;
  text-decoration: underline; cursor: pointer;
}

/*
 * The action a row is for, first and biggest in the column that holds the
 * actions. Shay, hunting for "Settle it" at the tail of a grey sentence: "the
 * settle button has to be more visible. it is the whole point."
 */
.filer.suggestion { margin-bottom: 10px; }
.filer.suggestion .toolbar-btn.primary {
  font-size: 12.5px; padding: 7px 14px; display: inline-block; text-decoration: none;
  white-space: normal; text-align: left; line-height: 1.35;
}
.filer.suggestion .hint { margin-top: 4px; }

/* ─────────────────────────────────────────────────────────────────────────────
   The inbox: one docket per document.

   It was a nine-column table. A scan the reader could not read left six of the
   nine columns showing a dash, and the tenth held three forms stacked to the
   height of a phone screen, with no way to tell which of them the row wanted.
   Shay: "we need to find a better display for this."

   The document is the unit, not the column. Each one is a docket: on the left,
   who sent it, the file, the five figures the reader looks for, and what those
   figures mean against the books, each finding a sentence with a light in
   front of it. On the right, the one thing to press for this document, and the
   other ways to file it folded underneath — open when there is no one thing.
   ───────────────────────────────────────────────────────────────────────────── */
.dockets { display: flex; flex-direction: column; gap: 10px; margin-bottom: 10px; }

.docket {
  display: grid; grid-template-columns: minmax(0, 1fr) 380px;
  background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
  box-shadow: var(--shadow);
}
.docket-read { padding: 14px 18px 16px; min-width: 0; }
.docket-do {
  padding: 14px 18px 16px; min-width: 0;
  border-left: 1px solid var(--border); background: var(--surface2);
  border-radius: 0 var(--radius) var(--radius) 0;
}
@media (max-width: 900px) {
  .docket { grid-template-columns: 1fr; }
  .docket-do {
    border-left: 0; border-top: 1px solid var(--border);
    border-radius: 0 0 var(--radius) var(--radius);
  }
}

/* Who it is from, and whether we know them. */
.docket-who { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.docket-who .name { font-size: 15px; font-weight: 600; color: var(--text); letter-spacing: -0.2px; }
.docket-who .when { margin-left: auto; font-family: var(--mono); font-size: 11.5px; color: var(--text3); white-space: nowrap; }

.chip {
  display: inline-block; font-size: 11px; font-weight: 500; line-height: 1.5;
  padding: 1px 8px; border-radius: 999px; white-space: nowrap; vertical-align: 1px;
}
.chip.go   { background: var(--calm-go-bg);   color: var(--calm-go-ink); }
.chip.hmm  { background: var(--calm-hmm-bg);  color: var(--calm-hmm-ink); }
.chip.stop { background: var(--calm-stop-bg); color: var(--calm-stop-ink); }
.chip.none { background: var(--surface3); color: var(--text2); }

/* The file itself, and the mail it came in. One line; the subject gives way. */
.docket-file { display: flex; gap: 8px; align-items: baseline; margin-top: 5px; min-width: 0; font-size: 13px; }
.docket-file a { color: var(--text); text-decoration: none; border-bottom: 1px solid var(--border2); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 60%; flex: 0 1 auto; }
.docket-file a:hover { border-color: var(--accent); }
.docket-file .sub { flex: 1 1 0; min-width: 0; font-size: 12px; color: var(--text3); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

/* The five figures the reader goes looking for. Always five cells, in the same
   order on every docket, so an empty one is seen to be empty rather than
   missing from the row. */
.facts {
  display: grid; grid-template-columns: repeat(5, minmax(0, 1fr)); margin: 12px 0 0;
  border: 1px solid var(--border); border-radius: var(--radius-sm); overflow: hidden;
}
.facts > div { padding: 7px 12px; border-right: 1px solid var(--border); min-width: 0; }
.facts > div:last-child { border-right: 0; }
.facts dt { font-size: 10.5px; font-weight: 600; letter-spacing: .05em; text-transform: uppercase; color: var(--text3); white-space: nowrap; }
.facts dt .no { font-weight: 600; }
.facts dd {
  margin: 2px 0 0; font-family: var(--mono); font-size: 12.5px; color: var(--text);
  font-variant-numeric: tabular-nums; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.facts dd.blank { color: var(--text3); opacity: .5; }
.facts dd .cur { font-size: 10.5px; color: var(--text3); margin-left: 3px; }
@media (max-width: 560px) {
  .facts { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .facts > div { border-bottom: 1px solid var(--border); }
  .facts > div:nth-child(2n) { border-right: 0; }
}

/* What the figures mean, one sentence each, a light in front of each. The
   light is never the only signal: the sentence says it as well. */
.findings { list-style: none; margin: 11px 0 0; padding: 0; display: flex; flex-direction: column; gap: 5px; }
.findings li { display: flex; gap: 9px; align-items: baseline; font-size: 12.5px; line-height: 1.5; color: var(--text2); }
.findings li::before {
  content: ''; flex: 0 0 8px; width: 8px; height: 8px; border-radius: 50%;
  background: var(--border2); transform: translateY(-1px);
}
.findings li.go::before   { background: var(--calm-go-ink); }
.findings li.hmm::before  { background: var(--calm-hmm-ink); }
.findings li.stop::before { background: var(--calm-stop-ink); }
.findings li.stop { color: var(--calm-stop-ink); }
.findings li b { font-weight: 600; color: var(--text); }
.findings li.stop b { color: inherit; }
.findings li .m { font-family: var(--mono); font-size: 12px; font-variant-numeric: tabular-nums; }

/* The one thing to press. Filled, full width, and the only filled button on
   the docket, so the eye lands on it before it reads anything else. */
.docket-do .lead { display: flex; flex-direction: column; gap: 8px; }
.docket-do .lead + .lead { margin-top: 14px; }
.docket-do .lead-btn {
  display: block; width: 100%; text-align: left; text-decoration: none;
  font-size: 13px; font-weight: 600; line-height: 1.35; padding: 10px 14px;
  background: var(--accent); color: var(--on-accent); border: 1px solid var(--accent);
  border-radius: var(--radius-sm); cursor: pointer; transition: opacity .15s;
  white-space: normal;
}
.docket-do .lead-btn:hover { opacity: .88; }
.docket-do .lead-btn small { display: block; font-weight: 400; font-size: 11.5px; opacity: .75; margin-top: 2px; }
.docket-do .lead .findings { margin-top: 0; }
.docket-do .lead form { margin: 0; }
.docket-do .lead .reason { width: 100%; }

/* The other ways to file it. Folded when the docket has a lead, because the
   list of every posting on the books is where you go to disagree; open when
   it has none, because then it is the only way in. */
.docket-more { margin: 0; }
.lead + .docket-more, .lead ~ .docket-more { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--border); }
.docket-more > summary {
  cursor: pointer; list-style: none; font-size: 12px; color: var(--text2); font-weight: 500;
  display: flex; align-items: center; gap: 6px; user-select: none;
}
.docket-more > summary::-webkit-details-marker { display: none; }
.docket-more > summary::before {
  content: ''; width: 6px; height: 6px; border-right: 1.5px solid currentColor; border-bottom: 1.5px solid currentColor;
  transform: rotate(-45deg) translateY(-1px); transition: transform .15s; opacity: .7;
}
.docket-more[open] > summary::before { transform: rotate(45deg) translateY(-2px); }
.docket-more > summary:hover { color: var(--accent); }
.docket-more > summary:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 3px; }
.docket-more.plain > summary { display: none; }
.docket-more .opt { margin-top: 10px; }
.docket-more .opt-k { font-size: 12px; color: var(--text2); margin-bottom: 5px; line-height: 1.5; }
.docket-more .opt form { margin: 0; }
.docket-more .opt .row2 { display: flex; gap: 6px; align-items: center; }
.docket-more .opt .row2 > input, .docket-more .opt .row2 > .pick, .docket-more .opt .row2 > select { flex: 1 1 auto; min-width: 0; }
.docket-more .opt .row2 > button { flex: 0 0 auto; }
.docket-more button.linkish { margin-left: 0; font-size: 12px; }
.docket-more .quiet { display: flex; gap: 16px; align-items: baseline; margin-top: 12px; font-size: 12px; color: var(--text3); flex-wrap: wrap; }
.docket-more .quiet form { margin: 0; display: inline; }

/*
 * The paper itself, in a window over the inbox.
 *
 * As tall as the screen allows and nearly as wide, because a PDF read in a
 * letterbox is a PDF nobody reads. The frame fills whatever the sticky top bar
 * leaves.
 */
dialog.sheet-paper { width: min(1200px, 96vw); height: 92vh; max-height: 92vh; display: none; flex-direction: column; }
dialog.sheet-paper[open] { display: flex; }
dialog.sheet-paper iframe { flex: 1 1 auto; width: 100%; border: 0; background: #525659; }
dialog.sheet-paper .sheet-top { flex: 0 0 auto; }
.docket-file .toolbar-btn.small { padding: 2px 8px; font-size: 12px; flex: 0 0 auto; }

/*
 * The old system beside the books, three rows a stone: what the books say,
 * what the old system says, and the difference. The difference row is the
 * one the eye should land on, so it is the only one that gets colour.
 */
/*
 * Not a .tight table: that class caps every cell at 220px and lets the
 * browser lay the columns out as it likes, which is how a 420px name column
 * came out at 215 and the side label sat at the far left of an empty gap.
 * Fixed layout, every column given its width, and the side label pushed up
 * against the first figure where it belongs.
 */
table.sheet { table-layout: fixed; width: 100%; }
table.sheet th, table.sheet td { padding: 4px 10px; font-size: 12.5px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
table.sheet td { vertical-align: top; }
table.sheet th.sheet-name, table.sheet td.sheet-name { width: 34%; border-right: 1px solid var(--border); white-space: normal; }
/* The side label — OMOS, Old system, Difference — is set right-aligned in a
   narrow column, so it reads as a label for the figures beside it rather than
   as a column of its own. */
table.sheet th.sheet-side, table.sheet td.sheet-side { width: 76px; padding-right: 2px; font-size: 11px; text-align: right; color: var(--text3); }
table.sheet th.sheet-act, table.sheet td.sheet-act { width: 148px; border-left: 1px solid var(--border); white-space: normal; }
/* Three rows of one height each, whatever the name cell holds: the reasons
   are clipped to the block and shown whole on hover. */
table.sheet tr.sheet-ours, table.sheet tr.sheet-theirs, table.sheet tr.sheet-diff { height: 32px; }
table.sheet .sheet-name .hint { margin-top: 3px; white-space: normal; line-height: 1.4; max-height: 74px; overflow: hidden; }
table.sheet .sheet-act form { display: flex; flex-direction: column; gap: 6px; margin: 0; }
table.sheet .sheet-act .toolbar-btn { width: 100%; text-align: center; }
table.sheet tr.sheet-ours td { border-bottom: 0; padding-bottom: 2px; }
table.sheet tr.sheet-theirs td { border-bottom: 0; padding-top: 2px; padding-bottom: 2px; }
table.sheet tr.sheet-diff td { padding-top: 2px; font-size: 12px; }
table.sheet tr.sheet-diff td, table.sheet td.sheet-name, table.sheet td.sheet-act { border-bottom: 2px solid var(--border2); }
table.sheet tr.sheet-diff td.no { font-weight: 600; }
/* The evidence row under a money block: small, one line per fact, and the
   heavy rule moves down to sit under it. */
table.sheet tr.sheet-diff.has-evidence td { border-bottom: 0; }
table.sheet tr.sheet-evidence td { font-size: 11.5px; color: var(--text2); line-height: 1.5; white-space: normal; padding-top: 2px; padding-bottom: 8px; border-bottom: 2px solid var(--border2); }
table.sheet tr.sheet-evidence td.sheet-side { padding-top: 4px; }

/*
 * The sheet's small print, readable.
 *
 * Shay: "the contrast of the smaller letters is just impossible to read."
 * The reasons under a name, the side labels and the evidence lines were set
 * in the hint colour at eleven pixels, which on the dark theme is grey on
 * grey. On this page they carry the argument, so they are set in the body's
 * second colour, a size up, and the side label a shade darker still.
 */
table.sheet .sheet-name .hint { font-size: 12.5px; color: var(--text2); max-height: 76px; }
table.sheet th.sheet-side, table.sheet td.sheet-side { font-size: 12px; color: var(--text2); }
table.sheet tr.sheet-evidence td { font-size: 12.5px; color: var(--text); }
table.sheet .sheet-act .hint { font-size: 12px; color: var(--text2); }
table.sheet tr.sheet-diff td { font-size: 12.5px; }

/* Bringing a note to a later invoice: one row of two boxes and a button. */
form.bring { margin: 8px 0 0; padding: 8px 10px; border: 1px dashed var(--border2); border-radius: 8px; }
form.bring .bring-row { display: flex; gap: 12px; align-items: flex-end; flex-wrap: wrap; }
form.bring label { font-size: 11px; color: var(--text3); display: flex; flex-direction: column; gap: 3px; }
form.bring .hint { margin-top: 6px; }

/* The expense bill as the lead action: six labelled boxes, then the button. */
.docket-do .expense-lead { display: flex; flex-direction: column; gap: 8px; }
.docket-do .expense-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 10px; }
.docket-do .expense-grid label { display: flex; flex-direction: column; gap: 3px; font-size: 11px; color: var(--text3); }
.docket-do .expense-grid label:nth-child(3) { grid-column: 1 / -1; }
.docket-do .expense-grid input, .docket-do .expense-grid select { width: 100%; font-size: 12.5px; padding: 5px 7px; }
.docket-do .expense-grid .mono { font-size: 12.5px; color: var(--text); padding: 5px 0; }

/* ── Every table sorts and filters (table-filter.js) ─────────────────
 *
 * The heading keeps its own look; the button inside it only borrows it. The
 * arrow is faint until the column is hovered, filtered or sorted, so a page of
 * lists does not turn into a page of controls.
 */
tr.cf-out, tr.cf-gone { display: none !important; }
.col-filter-btn.cf-auto {
  font-size: inherit; letter-spacing: inherit; font-weight: inherit; text-align: inherit;
}
.col-filter-btn.cf-auto .cf-arrow { opacity: 0.25; }
th:hover > .col-filter-btn.cf-auto .cf-arrow,
.col-filter-btn.cf-auto.cf-active .cf-arrow,
.col-filter-btn.cf-auto.cf-sorted .cf-arrow { opacity: 1; }
.col-filter-btn > span:first-child { order: 0; }
.col-filter-btn::after { order: 1; }
/* The arrow is its own target, so it gets a finger-sized patch to land on. */
.col-filter-btn .cf-arrow { order: 2; padding: 4px 5px; margin: -4px -5px -4px -2px; border-radius: 4px; }
.col-filter-btn .cf-arrow:hover { background: var(--surface); color: var(--accent); opacity: 1; }
th[data-sort-dir="up"] > .col-filter-btn::after { content: '\2191'; font-size: 10px; }
th[data-sort-dir="down"] > .col-filter-btn::after { content: '\2193'; font-size: 10px; }
.cd-more { padding: 6px 12px; font-size: 11px; color: var(--text3); }

/* A total that still counts every row, while some are hidden. */
table.cf-filtered .cf-fixed { opacity: 0.35; }
/* "Same as above" written out again once the rows are no longer together. */
td.cf-fill::before { content: attr(data-filter-value); color: var(--text3); }

.cf-bar {
  display: flex; gap: 14px; align-items: baseline; margin: 0 0 6px;
  font-size: 12px; color: var(--text3);
}
.cf-bar a { color: var(--accent); }

@media print {
  .cf-arrow, .cf-bar { display: none !important; }
  th[data-sort-dir] > .col-filter-btn::after { content: none; }
}

/* ── Zebra labels (labels.js) ──────────────────────────────────────────
   The previews sit on a grey like the liner the stickers come on, so the
   white of the sticker reads as the sticker. Pixelated, because a printer dot
   is a square and a smoothed preview promises a softer print than it gets. */
.labels-panel { max-width: 620px; }
.labels-previews {
  max-height: 340px; overflow: auto; border: 1px solid var(--border); border-radius: var(--radius);
  background: #e9e6e1; padding: 10px; display: flex; flex-direction: column; gap: 10px; align-items: center;
}
.labels-previews figure { margin: 0; text-align: center; }
.labels-previews img { width: 300px; background: #fff; border-radius: 4px; image-rendering: pixelated; display: block; }
.labels-previews figcaption { font-size: 11px; color: #57534E; margin-top: 4px; }
.labels-actions { display: flex; justify-content: flex-end; gap: 8px; margin-top: 16px; flex-wrap: wrap; }
.err-text { color: var(--sold); font-size: 12.5px; margin-top: 6px; }

/* The OM tag on a vendor listing (070): OriginMark material, the stones the
   retailer portal shows. Gold, like the OM sign on the portal itself. */
.tag.om-tag { background: #F6EFE2; color: #8A6A2E; border-color: #E2CFA6; font-weight: 600; }
:root[data-theme="dark"] .tag.om-tag { background: #3A2F1C; color: #E2C68A; border-color: #5C4A2A; }
