/* ============================================================================================
   THE INVOICE DOCUMENT — one stylesheet, every surface.

   Loaded by the app (editor, email preview, detail panel) AND by invoice.html (hosted client
   page, payment). They are separate documents on separate origins-of-code, so this file is the
   only thing that can keep them looking the same. It is a FILE and not a <style> block inside
   the prototype for exactly that reason: invoice.html has no app on the page and cannot import
   from it.

   THE TOKENS ARE THE CONTRACT. Column widths, the type scale and the rule colours are declared
   once here and consumed by every HTML renderer. The PDF cannot read CSS, so it consumes the
   same numbers from KSDOC_TOKENS in invoice-doc.js — change one, change both, and the audit
   compares them.

   Nothing in here knows about editing. The editor is this document with inputs in place of text;
   the input styling sits at the bottom, under .ksd-doc.is-editor, and changes nothing about
   position, width or weight. That is what makes "what you edit is what they get" true rather
   than aspirational.
   ============================================================================================ */

.ksd-doc{
  /* --- geometry ------------------------------------------------------------------------- */
  --ksd-pad: 34px;              /* paper padding */
  --ksd-desc-min: 0;            /* description column takes the remainder */
  /* 74px truncated "Sept 30, 2026" to "Sept 30, 202". A date is one of the values the handbook
     says must never truncate, and it is the column a reader checks first. */
  --ksd-date: 104px;
  --ksd-rate: 88px;
  --ksd-qty: 48px;
  --ksd-days: 48px;
  --ksd-total: 104px;
  --ksd-ot: 112px;
  --ksd-del: 22px;
  --ksd-row-py: 7px;
  --ksd-gap: 20px;              /* between blocks */

  /* --- type ----------------------------------------------------------------------------- */
  /* ONE STEP UP, ALL TOGETHER. 11.5px body was chosen for a dense on-screen panel and reads small
     on what the client actually receives. Every size moves by the same step so the hierarchy is
     unchanged \u2014 and KSDOC_TOKENS.type in invoice-doc.js carries the same numbers, because the
     PDF cannot read this file. */
  --ksd-fs: 13px;               /* body */
  --ksd-fs-sm: 11.5px;          /* captions, fees */
  --ksd-fs-lbl: 10px;           /* column + meta labels */
  --ksd-fs-title: 24px;         /* INVOICE */
  --ksd-fs-co: 17px;            /* company name */
  --ksd-fs-total: 17px;         /* the final figure */
  --ksd-fs-name: 15px;          /* the client's name, and the document number */
  --ksd-ls-lbl: .1em;

  /* --- colour --------------------------------------------------------------------------- */
  --ksd-ink: #1d2733;
  --ksd-dim: #66788c;
  --ksd-faint: #9aa8b5;
  --ksd-rule: #e7ecf1;
  --ksd-rule-soft: #f0f3f6;
  --ksd-paper: #ffffff;
  --ksd-panel: #f6f8fa;
  --ksd-acc: #ff3d63;           /* overridden per company */

  background: var(--ksd-paper);
  color: var(--ksd-ink);
  font-size: var(--ksd-fs);
  line-height: 1.5;
  padding: var(--ksd-pad);
  box-sizing: border-box;
}
.ksd-doc *{ box-sizing: border-box; }

/* ---- header ---------------------------------------------------------------------------- */
.ksd-top{ display:flex; justify-content:space-between; align-items:flex-start; gap:24px; }
.ksd-who{ min-width:0; }
.ksd-logo{ max-height:42px; max-width:190px; object-fit:contain; display:block; margin-bottom:8px; }
/* THE COMPANY IS NAMED IN THE SAME HAND AS THE CLIENT. This was 800 with the tracking pulled in,
   against the client's name at 700 with none, so the two names facing each other across the same
   document were visibly different type — the heavier cut reads as a different family rather than a
   heavier weight of the same one. The client's setting is the one that looked right, so it is the
   one both use. Size still separates them; weight no longer does. */
.ksd-co{ font-size:var(--ksd-fs-co); font-weight:700; margin:0; }
/* The logo is the company's name in its own type, so the heading beneath it was the name a second
   time. It is UNDRAWN, never removed: display:none would have taken it out of the text layer too,
   and external-invoice-truth-audit G3 caught exactly that — the company name disappeared from
   innerText, which is what a PDF text extraction, a document search, a copy-paste and a screen
   reader all read. Clipped instead, so it is absent to the eye and present to everything else. */
.ksd-co-hid{ position:absolute; width:1px; height:1px; margin:-1px; padding:0;
  overflow:hidden; clip:rect(0 0 0 0); clip-path:inset(50%); white-space:nowrap; border:0; }
.ksd-codet{ font-size:var(--ksd-fs-sm); color:var(--ksd-dim); line-height:1.6; margin-top:3px; white-space:pre-line; }
.ksd-idblock{ text-align:right; white-space:nowrap; }
.ksd-title{ font-size:var(--ksd-fs-title); font-weight:800; letter-spacing:.02em; color:var(--ksd-acc); line-height:1; }
.ksd-no{ font-size:var(--ksd-fs-name); font-weight:700; margin-top:5px; }
.ksd-pill{ display:inline-block; margin-top:7px; font-size:var(--ksd-fs-lbl); font-weight:800;
  letter-spacing:var(--ksd-ls-lbl); text-transform:uppercase; border:1px solid currentColor;
  border-radius:999px; padding:3px 9px; }
.ksd-hr{ height:2px; background:var(--ksd-acc); margin:14px 0 var(--ksd-gap); }

/* ---- customer + meta, side by side ------------------------------------------------------ */
.ksd-facts{ display:grid; grid-template-columns:minmax(0,1fr) minmax(0,auto); gap:24px;
  margin-bottom:var(--ksd-gap); }
.ksd-lbl{ font-size:var(--ksd-fs-lbl); font-weight:800; letter-spacing:var(--ksd-ls-lbl);
  text-transform:uppercase; color:var(--ksd-acc); }
.ksd-billto .n{ font-size:var(--ksd-fs-name); font-weight:700; margin-top:5px; }
.ksd-billto .d{ color:var(--ksd-dim); font-size:var(--ksd-fs-sm); line-height:1.6; }
/* IT SAT HIGH AND LOOSE. Four rows top-aligned against the BILLED TO *label* put the first value
   a line above the client's name, and 3px row gaps against the 1.6 line-height opposite made the
   two halves of the same band read as different documents. The rows now carry the body rhythm and
   the block starts on the name's line, so the two columns share a baseline instead of merely
   sharing a row of the grid. */
.ksd-meta{ display:grid; grid-template-columns:auto auto; gap:6px 22px; align-content:start;
  margin-top:calc(var(--ksd-fs-lbl) + 5px); }
.ksd-meta .k{ font-size:var(--ksd-fs-lbl); font-weight:800; letter-spacing:var(--ksd-ls-lbl);
  text-transform:uppercase; color:var(--ksd-dim); white-space:nowrap; align-self:baseline; }
/* NO NOWRAP HERE. A right-aligned nowrap value in an auto grid cell overflows LEFTWARDS when it
   is long, painting across its own label — a full address rendered "LOCATI1114 6th Avenue…" on the
   PDF and the blob preview (RC QA, 2026-08-15). Long values wrap onto further lines instead:
   never overlapping the label, never clipped, never truncated. Short values — dates, totals,
   numbers — still fit one line exactly as before. */
.ksd-meta .v{ text-align:right; min-width:0; overflow-wrap:anywhere; align-self:baseline;
  font-weight:600; }

/* ---- line items — THE shared grid ------------------------------------------------------- */
.ksd-sec{ margin-bottom:12px; }
/* margin:0 for the same reason the cell declares its own type: a page-level `table{margin-top}`
   would otherwise push this table down on that page alone. .ksd-sec owns the gap between
   sections. */
.ksd-tab{ width:100%; border-collapse:collapse; table-layout:fixed; margin:0; }
.ksd-tab th{ font-size:var(--ksd-fs-lbl); font-weight:800; letter-spacing:var(--ksd-ls-lbl);
  text-transform:uppercase; text-align:right; padding:5px 8px; color:#fff; background:var(--ksd-acc); }
.ksd-tab th:first-child{ text-align:left; }
/* font-size AND border-top ARE DECLARED, NOT INHERITED — and that is the whole point of them
   being here. This rule used to set neither, letting the cell inherit 11.5px from .ksd-doc and
   carry no top rule. An inherited value loses to ANY direct declaration regardless of
   specificity, so invoice.html's page-level `tbody td{font-size:13.5px;border-top:1px solid}`
   — a leftover from the hosted page's own pre-KSDoc design — won on the client's page and
   nowhere else. The result was a web page and a PDF that were different documents: 13.5px with
   a rule above every row on one, 11.5px with none on the other, and the client's own Download
   PDF (which prints the page) siding with the page. Measured across 14 fixtures, 2026-08-07.
   Declaring both closes the gap for good: a host page can no longer fill in what this file
   leaves unsaid. Row separation is the BOTTOM border below — one rule between rows, not two. */
.ksd-tab td{ padding:var(--ksd-row-py) 8px; font-size:var(--ksd-fs); border-top:0;
  border-bottom:1px solid var(--ksd-rule-soft); vertical-align:top; }
/* A DESCRIPTION MUST BREAK RATHER THAN DISAPPEAR. table-layout:fixed gives the description
   whatever is left after the numeric columns, and a token with no spaces in it — a long
   reference, a SKU, a URL — cannot wrap, so it overflowed its cell. On the hosted page
   .sheet{overflow:hidden} then CLIPPED it: the client was shown the first few characters of a
   line and had no way to reach the rest, with no scrollbar to suggest anything was missing. It
   clipped in the canonical PDF too (a 90-character reference measured 694px inside a 506px
   cell). Breaking mid-token is ugly; silently losing what the invoice says is not a choice.
   Certification, 2026-08-07. */
.ksd-tab td:first-child, .ksd-tab th:first-child{ overflow-wrap:anywhere; }
.ksd-tab td.n{ text-align:right; white-space:nowrap; }
/* Prices RIGHT-align, matching the editor — the last digits line up down the column. One
   decision, every surface (Stephen, 2026-08-03). */
.ksd-tab td.a{ text-align:right; white-space:nowrap; font-weight:600; font-variant-numeric:tabular-nums; }
.ksd-tab tr:last-child td{ border-bottom:0; }
/* Right padding 0: the value carries its own 8px, matching the table cell's padding, so the
   subtotal's last digit sits on the same pixel as the TOTAL column above it. Measured. */
.ksd-sub{ display:flex; justify-content:flex-end; align-items:baseline; gap:16px;
  border-top:1px solid var(--ksd-rule); padding:7px 0 0 8px; font-size:var(--ksd-fs); }
.ksd-sub .l{ color:var(--ksd-dim); }
.ksd-sub .v{ font-weight:700; min-width:var(--ksd-total); text-align:right; padding-right:8px;
  font-variant-numeric:tabular-nums; }
.ksd-sub.strong{ border-top:1.5px solid var(--ksd-acc); }
.ksd-sub.strong .l,.ksd-sub.strong .v{ font-weight:800; color:var(--ksd-ink); }
.ksd-onesub{ font-size:var(--ksd-fs-sm); color:var(--ksd-faint); }

/* ---- totals ---------------------------------------------------------------------------- */
.ksd-totals{ margin-top:var(--ksd-gap); margin-left:auto; width:min(300px,100%); }
.ksd-totals .row{ display:flex; justify-content:space-between; gap:16px; padding:5px 8px;
  color:var(--ksd-dim); }
.ksd-totals .row .v{ color:var(--ksd-ink); white-space:nowrap; }
.ksd-totals .grand{ border-top:2px solid var(--ksd-acc); margin-top:5px; padding-top:9px;
  font-size:var(--ksd-fs-total); font-weight:800; color:var(--ksd-ink); }
.ksd-totals .grand .v{ font-weight:800; }

/* ---- blocks ---------------------------------------------------------------------------- */
.ksd-block{ margin-top:var(--ksd-gap); }
.ksd-block > .ksd-lbl{ border-bottom:1.5px solid var(--ksd-acc); padding-bottom:5px; margin-bottom:9px; }
.ksd-pay{ background:var(--ksd-panel); border-radius:8px; padding:14px 16px; }
.ksd-paygrid{ display:flex; flex-wrap:wrap; gap:6px 28px; margin-bottom:9px; }
.ksd-paygrid > div{ display:flex; flex-direction:column; }
.ksd-paygrid .k{ font-size:var(--ksd-fs-lbl); font-weight:800; letter-spacing:var(--ksd-ls-lbl);
  text-transform:uppercase; color:var(--ksd-dim); }
.ksd-paygrid .v{ font-weight:700; }
/* A method is a block — heading, then the account that pays it — not a single baseline row, so
   the payee stays visibly attached to the method it belongs to. */
.ksd-meth > .pm{ padding:6px 0 7px; border-bottom:1px solid var(--ksd-rule); }
.ksd-meth > .pm:last-child{ border-bottom:0; }
.ksd-meth > .pm > .h{ display:flex; justify-content:space-between; gap:14px; align-items:baseline; }
.ksd-meth .l{ font-weight:600; }
.ksd-meth .f{ font-size:var(--ksd-fs-sm); color:var(--ksd-faint); white-space:nowrap; }
.ksd-meth .d{ margin-top:3px; font-size:var(--ksd-fs-sm); color:var(--ksd-dim); white-space:pre-line; }
/* THE CODE SITS BESIDE THE METHODS, NOT UNDER THE BLOCK. Laid out as a row so it reads as one
   more way to reach the same page rather than a banner of its own, and so the methods keep the
   width they had — a QR that pushed the bank details into a narrower column would be a code that
   cost the document something. It stays put when printed and never breaks across a page. */
.ksd-payrow{ display:flex; gap:18px; align-items:flex-start; }
.ksd-paymain{ flex:1; min-width:0; }
.ksd-paymain > .ksd-paygrid:last-child,
.ksd-paymain > .ksd-meth:last-child{ margin-bottom:0; }
/* The code no longer sits as a fixed-width column inside the payment block — it is a panel beside
   the pay panel, so layout comes from .ksd-qrbox and this keeps only what is true of the code
   itself. The class name stays because it is the contract two gates assert against. */
.ksd-qr{ text-align:center; break-inside:avoid; page-break-inside:avoid; }
.ksd-qr svg{ display:block; width:74px; height:74px; border:1px solid var(--ksd-rule); border-radius:4px; }
.ksd-qr .c{ margin-top:4px; font-size:var(--ksd-fs-lbl); font-weight:800;
  letter-spacing:var(--ksd-ls-lbl); text-transform:uppercase; color:var(--ksd-dim); line-height:1.25; }
@media print{ .ksd-qr{ break-inside:avoid; } }
.ksd-paydet{ font-size:var(--ksd-fs-sm); color:var(--ksd-dim); white-space:pre-line; margin-top:9px; }
/* The pay call-to-action. Printed as a real button shape with the URL under it, because a
   printed invoice is read on paper as often as on screen and a bare hyperlink is unreadable
   once it is ink. */
/* ══ THE PAY ROW ════════════════════════════════════════════════════════════════════════════════
   Two thirds and one third: the panel a person reads, and the code they point a phone at. Owner,
   2026-09-02: "make sure on every pdf it's centred like this, uniform... add the QR code in its own
   grey box next to that, so two thirds then the code."

   FLEX, NOT GRID, because this is printed. Grid's fractional tracks are honoured unevenly by print
   engines at page boundaries; a flex row with an explicit basis lands the same in the browser, in
   Chromium's PDF and on paper. Both children carry break-inside:avoid so the panel and its code can
   never be split across a page from each other. */
.ksd-payrow2{ display:flex; gap:12px; align-items:stretch; break-inside:avoid; page-break-inside:avoid; }
.ksd-payrow2 > *{ break-inside:avoid; page-break-inside:avoid; }
.ksd-paypanel{ flex:2 1 0; min-width:0; text-align:center;
  background:#f7f9fa; border:1px solid #e6ebef; border-radius:12px; padding:15px 18px; }
.ksd-payh{ font-size:var(--ksd-fs-lbl); letter-spacing:.11em; font-weight:800;
  color:var(--ksd-dim); margin-bottom:6px; }
.ksd-paypanel p{ margin:0 0 12px; font-size:var(--ksd-fs-sm); line-height:1.65; color:var(--ksd-body); }
.ksd-paynote{ margin-top:9px; font-size:var(--ksd-fs-lbl); color:var(--ksd-dim); }
/* The code's box matches the panel exactly — same ground, same border, same corner — so the two
   read as one control rather than a control and an ornament. */
.ksd-qrbox{ flex:1 1 0; min-width:0; display:flex; flex-direction:column;
  align-items:center; justify-content:center; gap:6px;
  background:#f7f9fa; border:1px solid #e6ebef; border-radius:12px; padding:12px; }
.ksd-qrbox svg{ display:block; width:88px; height:88px; background:#fff;
  border:1px solid var(--ksd-rule); border-radius:4px; }
.ksd-qrbox .c{ font-size:var(--ksd-fs-lbl); font-weight:800; letter-spacing:.09em;
  text-transform:uppercase; color:var(--ksd-dim); text-align:center; }
/* On paper the tinted ground costs ink and gains nothing; the border alone carries the grouping.
   Matches what invoice.html already does to its own .pay box at print. */
@media print{
  .ksd-paypanel, .ksd-qrbox{ background:#fff; border-color:#ccc; }
}
/* A narrow screen stacks them rather than crushing the code to unscannable width. */
@media (max-width:520px){
  .ksd-payrow2{ flex-direction:column; }
}
.ksd-paycta{ text-align:center; }
.ksd-paybtn{ display:inline-block; background:var(--ksd-acc); color:#fff !important; text-decoration:none;
  font-weight:800; font-size:var(--ksd-fs); padding:11px 26px; border-radius:8px; }
.ksd-payurl{ font-size:var(--ksd-fs-sm); color:var(--ksd-dim); margin-top:7px; word-break:break-all; }
.ksd-notes p{ margin:0 0 7px; }
.ksd-foot{ margin-top:calc(var(--ksd-gap) * 1.4); padding-top:12px; border-top:1px solid var(--ksd-rule);
  font-size:var(--ksd-fs-sm); color:var(--ksd-faint); text-align:center; }

/* ---- editor mode: inputs that occupy exactly the space the text would ------------------- */
.ksd-doc.is-editor .ksd-in{
  font:inherit; color:inherit; width:100%; background:transparent; border:0;
  border-bottom:1px dashed transparent; border-radius:0; padding:0; margin:0;
}
.ksd-doc.is-editor td.n .ksd-in,.ksd-doc.is-editor .ksd-meta .ksd-in{ text-align:right; }
.ksd-doc.is-editor .ksd-in:hover{ border-bottom-color:var(--ksd-rule); }
.ksd-doc.is-editor .ksd-in:focus{ outline:0; border-bottom-color:var(--ksd-acc);
  background:color-mix(in srgb, var(--ksd-acc) 6%, transparent); }
.ksd-doc.is-editor .ksd-in::placeholder{ color:var(--ksd-faint); }
/* A multi-line field occupies the same column as the text it replaces and grows downward — it
   must never be a resizable box that changes the row's width. */
.ksd-doc.is-editor textarea.ksd-in{ resize:vertical; min-height:1.5em; line-height:1.55;
  display:block; margin-top:4px; }

/* ---- editor-only furniture -----------------------------------------------------------------
   Everything below exists ONLY inside .is-editor, and every piece of it also carries
   .ksd-noprint, so there are two independent reasons none of it can reach a client's copy. The
   rule for all of it: it may add controls in the margins of the document, and it may not move a
   single column, weight or measure of the document itself. */

/* The delete column. Zero width unless cols.del asked for it, so the seven columns a client sees
   never shift when an owner starts editing. */
.ksd-tab .ksd-del{ padding:0; text-align:center; }
.ksd-doc.is-editor .ksd-x{ appearance:none;border:0;background:transparent;cursor:pointer;
  color:var(--ksd-faint); font:inherit; font-size:14px; line-height:1; padding:2px 4px; border-radius:4px; }
.ksd-doc.is-editor .ksd-x:hover{ color:#d13045; background:rgba(209,48,69,.08); }

/* Section controls, inside the header cell so no section's name can push a column out of line.
   White-on-accent, because that is the band they sit in. */
.ksd-doc.is-editor .ksd-secctl{ display:inline-flex; align-items:center; gap:6px; margin-left:10px;
  vertical-align:middle; }
.ksd-doc.is-editor .ksd-secctl button{ appearance:none; font:inherit; font-size:9px; font-weight:800;
  letter-spacing:.06em; text-transform:uppercase; cursor:pointer; color:#fff;
  background:rgba(255,255,255,.16); border:1px solid rgba(255,255,255,.34); border-radius:5px;
  padding:2px 6px; line-height:1.4; }
.ksd-doc.is-editor .ksd-secctl button:hover{ background:rgba(255,255,255,.3); }
.ksd-doc.is-editor .ksd-secctl button.on{ background:#fff; color:var(--ksd-acc); border-color:#fff; }
.ksd-doc.is-editor .ksd-secctl .ksd-add{ font-size:13px; padding:0 6px; letter-spacing:0; }

/* The "fill this from somewhere" row, under its own section's header. */
.ksd-doc.is-editor .ksd-prompt td{ border-bottom:1px solid var(--ksd-rule-soft);
  background:var(--ksd-panel); }
.ksd-doc.is-editor .ksd-prompt .b{ display:flex; align-items:center; gap:10px; flex-wrap:wrap;
  font-size:var(--ksd-fs-sm); color:var(--ksd-dim); padding:2px 0; }

/* The subtotal's editor-only aside ("Per day $740"), pushed left of the label. */
.ksd-sub .n{ margin-right:auto; font-size:var(--ksd-fs-sm); color:var(--ksd-faint); }

/* Small owner-only asides that hang off a line or a section without disturbing it. */
.ksd-doc.is-editor .ksd-hint{ display:block; font-size:var(--ksd-fs-sm); color:var(--ksd-faint);
  font-weight:400; letter-spacing:0; text-transform:none; margin-top:3px; }
/* ---- ONE SIZE FOR THE OWNER'S CONTROLS ------------------------------------------------------
   These grew one at a time and each picked its own geometry. Measured on the invoice editor:
   "+ details" 21.3px tall with a 5px radius, the ledger select 16px with a 10px radius, the date
   19px at a different font size again, and the value inputs 20.5px — four heights, two type sizes
   and three corner radii sitting on one row. That is what "one is big, one is small, it's all over
   the space" is.

   There are exactly two kinds of control in this document and they are now told apart on purpose:

     an ASIDE   something the owner adds or picks that the client never sees — "+ details", the
                ledger account. Dashed, quiet, and all the same size as each other.
     a VALUE    something that prints. It takes the geometry of the text it replaces, so the
                editor's grid keeps matching the client's copy — which is the whole reason the
                editor is the document rather than a form beside it.
   -------------------------------------------------------------------------------------------- */
.ksd-doc.is-editor .ksd-minibtn,
.ksd-doc.is-editor select.ksd-sel{
  appearance:none; -webkit-appearance:none; font:inherit; font-size:var(--ksd-fs-sm);
  font-weight:600; line-height:1; cursor:pointer; color:var(--ksd-dim); background:transparent;
  border:1px dashed var(--ksd-rule); border-radius:6px;
  height:22px; padding:0 8px; margin:3px 6px 0 0; max-width:100%; vertical-align:middle;
}
.ksd-doc.is-editor .ksd-minibtn:hover,
.ksd-doc.is-editor select.ksd-sel:hover{ color:var(--ksd-ink); border-color:var(--ksd-dim); }
.ksd-doc.is-editor select.ksd-sel{ padding-right:18px;   /* room for the caret glyph below */
  background-image:linear-gradient(45deg,transparent 50%,var(--ksd-faint) 50%),
                   linear-gradient(135deg,var(--ksd-faint) 50%,transparent 50%);
  background-position:calc(100% - 10px) 9px, calc(100% - 6px) 9px;
  background-size:4px 4px, 4px 4px; background-repeat:no-repeat; }
.ksd-doc.is-editor select.ksd-sel.set{ color:var(--ksd-ink); border-style:solid; }

/* THE DATE IS A VALUE, NOT AN ASIDE. It prints in the date column, so it wears the same clothes as
   every other printed cell — the .ksd-in treatment — rather than the dashed pill the asides use.
   Right-aligned like the numbers beside it, and no wider than its column. */
.ksd-doc.is-editor select.ksd-datesel{
  appearance:none; -webkit-appearance:none; font:inherit; font-size:var(--ksd-fs);
  color:var(--ksd-ink); font-weight:400; line-height:inherit;
  height:auto; width:100%; max-width:100%; margin:0; padding:0 12px 0 0;
  background:transparent; border:0; border-bottom:1px dashed transparent; border-radius:0;
  text-align:right; text-align-last:right;
  background-image:linear-gradient(45deg,transparent 50%,var(--ksd-faint) 50%),
                   linear-gradient(135deg,var(--ksd-faint) 50%,transparent 50%);
  background-position:calc(100% - 4px) .62em, calc(100% - 0px) .62em;
  background-size:4px 4px, 4px 4px; background-repeat:no-repeat; }
.ksd-doc.is-editor select.ksd-datesel:hover{ border-bottom-color:var(--ksd-rule); }
.ksd-doc.is-editor select.ksd-datesel:focus{ outline:0; border-bottom-color:var(--ksd-acc); }
.ksd-doc.is-editor select.ksd-datesel:not(.set){ color:var(--ksd-faint); }
.ksd-doc.is-editor input.ksd-dateinput{ width:100%; text-align:right; font-size:var(--ksd-fs); }

/* A section the owner has hidden. It is out of the document; this is the only place it is still
   mentioned, and the only way back. */
.ksd-doc.is-editor .ksd-hidden{ display:flex; align-items:center; gap:12px; flex-wrap:wrap;
  margin:0 0 10px; padding:8px 12px; border:1px dashed var(--ksd-rule); border-radius:8px;
  font-size:var(--ksd-fs-sm); color:var(--ksd-dim); }
.ksd-doc.is-editor .ksd-hidden .ksd-minibtn{ margin:0 0 0 auto; }

/* Ledger summary — the owner's accounting view of the same lines. */
.ksd-doc.is-editor .ksd-ledger > div{ display:flex; justify-content:space-between; gap:16px;
  padding:4px 8px; border-bottom:1px solid var(--ksd-rule-soft); font-size:var(--ksd-fs); }
.ksd-doc.is-editor .ksd-ledger > div:last-child{ border-bottom:0; }
.ksd-doc.is-editor .ksd-ledger .l{ color:var(--ksd-dim); }
.ksd-doc.is-editor .ksd-ledger .v{ font-weight:700; font-variant-numeric:tabular-nums; }

@media print{
  .ksd-doc{ padding:0; }
  .ksd-noprint{ display:none !important; }
  /* ---- THE SHARED PAGE RULES. One @page and one set of break rules, so the app's print frame
     and the hosted page's Download PDF paginate identically. These used to live inline in
     docPrintHTML only, which meant the client's own download had browser-default margins and
     could split a section from its subtotal or the PAYMENT heading from its bank details. ---- */
  .ksd-sec{ break-inside:avoid; }
  .ksd-tab tr{ break-inside:avoid; }
  .ksd-block{ break-inside:avoid; }
  .ksd-totals{ break-inside:avoid; }
  .ksd-tab thead{ display:table-header-group; }   /* repeat column headings across a page break */
}
/* NO @page HERE, DELIBERATELY. This stylesheet is loaded by the whole app, and an unscoped @page
   would change the margins of every other print path (gear list, call sheet, inventory). The
   14mm page margin lives in the two places that print ONLY this document: docPrintHTML's frame
   and invoice.html. Keep all three agreeing. */
/* PRINT THE COLOUR. Browsers drop background fills when printing, so the section header bands,
   the payment panel and the status pill came out as plain text on white — the printed invoice
   stopped looking like the invoice on screen at exactly the moment it mattered most. This is the
   whole difference between "the PDF looks like shit" and "the PDF is the client view".
   Set unconditionally, not only inside @media print: Chrome's headless PDF path and
   `window.print()` both honour it, and a screen render is unaffected by it. */
.ksd-doc, .ksd-doc *{ -webkit-print-color-adjust:exact; print-color-adjust:exact; }
@media (max-width:620px){
  .ksd-doc{ --ksd-pad:20px; }
  .ksd-facts{ grid-template-columns:minmax(0,1fr); }
  .ksd-meta{ grid-template-columns:auto 1fr; }
  .ksd-top{ flex-direction:column; }
  .ksd-idblock{ text-align:left; }
  /* ---- THE LINE-ITEM TABLE ON A PHONE — RESOLVED BY RESTACKING, 2026-08-08 ------------------
     THE PROBLEM, as measured during the 2026-08-07 certification: the numeric columns are absolute
     (RATE 88 + TOTAL 104 = 192px before QTY, DAYS or padding). A 360px phone offers 320px inside
     the page padding, so the description column measured 0-20px and an ordinary line wrapped one
     character at a time — "Arc hiv e dri ves , shi pp ed".

     THE TWO PROPORTIONING FIXES WERE TRIED THEN AND CORRECTLY REVERTED. Narrowing the money
     columns truncated "$1,234,567.89" into a 72px cell, and money is the one thing that must never
     truncate. table-layout:auto gave the three sections description columns of 48px, 69px and 52px
     and the shared column grid came apart. Neither is attempted again here.

     THE ANSWER IS NOT A PROPORTION, IT IS A DIFFERENT PRESENTATION. Handbook 07 §5.1 orders the
     remedies — restack, re-present, reorder, disclose, and only then truncate — and this is step
     one: below the threshold each row becomes a block, the description leads on its own full-width
     line, and the numbers follow beneath it as labelled pairs. Nothing is dropped, nothing is
     truncated, and there is no horizontal scroll (07 §5.4).

     07 §6 says a table becomes cards on a phone, and explicitly exempts "a document being READ
     rather than a list being PROCESSED, where the document's own layout governs". An invoice is
     that exemption, so this restacks the row rather than rebuilding it as a card component.

     EACH VALUE NAMES ITSELF. Once the row is a block the <thead> headings are no longer above
     their columns, so every cell carries its heading in data-l and prints it inline. A cell for a
     factor that does not price this section is EMPTY and is removed entirely — a labelled blank is
     noise, and there was never any information in it (07 §4: what may never change is which
     information EXISTS, and an inapplicable factor is not information).

     THE EDITOR IS DELIBERATELY EXCLUDED, via :not(.is-editor). Its rows carry inline controls and
     a delete affordance, and building a document on a phone is out of scope by design rather than
     by accident (07 §2.1). It keeps the tabular layout it already had.

     THE PRINT PATH IS UNAFFECTED. This is a screen media query at 620px; the canonical PDF renders
     at letter width, so it never matches. Verified by rendering both. */
  .ksd-doc:not(.is-editor) .ksd-tab{ table-layout:auto; }
  .ksd-doc:not(.is-editor) .ksd-tab colgroup{ display:none; }

  /* The section band keeps its name and its rule; only the column headings go, because each cell
     now announces itself. */
  .ksd-doc:not(.is-editor) .ksd-tab thead tr{ display:block; }
  .ksd-doc:not(.is-editor) .ksd-tab thead th{ display:none; }
  .ksd-doc:not(.is-editor) .ksd-tab thead th:first-child{ display:block; width:auto; }

  .ksd-doc:not(.is-editor) .ksd-tab tbody tr{
    display:block; padding:9px 0 10px; border-bottom:1px solid var(--ksd-rule-soft); }
  .ksd-doc:not(.is-editor) .ksd-tab tbody td{
    display:inline-block; width:auto; border:0; padding:1px 16px 1px 0; vertical-align:baseline; }
  /* The description leads, full width, and is the only thing allowed to wrap. */
  .ksd-doc:not(.is-editor) .ksd-tab tbody td:first-child{
    display:block; padding:0 0 5px; font-weight:600; }
  /* A factor that does not apply to this section leaves no trace. */
  .ksd-doc:not(.is-editor) .ksd-tab tbody td.n:empty{ display:none; }
  .ksd-doc:not(.is-editor) .ksd-tab tbody td[data-l]::before{
    content:attr(data-l) "\00a0"; font-size:var(--ksd-fs-lbl); letter-spacing:.09em;
    color:var(--ksd-faint); font-weight:600; }
  /* The amount closes the row on its own line, so the eye lands on the money last. */
  .ksd-doc:not(.is-editor) .ksd-tab tbody td.a{
    display:block; text-align:left; padding:4px 0 0; font-weight:700; }
}

/* ================================================================================================
   THE INFORMATION BANNER — the one way KitSlate talks to its user at the top of a page.

   This replaced the black full-width bars ("Client view preview", the amber simulation strip).
   Those looked like warnings and competed with the document below them; information the user
   needs is not an alarm. The banner is a normal page-width white card — same radius, same
   elevation as every other card — with the site's line-marker vocabulary: a small vertical bar
   whose colour is the severity, a bold title, a grey sentence underneath, and any actions on the
   right as ordinary buttons.

   Lives here because this stylesheet is loaded by BOTH the app and the hosted pages: one
   implementation, every surface. Tones change ONLY the marker: the card itself stays white.
   Never printed — it is KitSlate speaking, not part of any document.
   ================================================================================================ */
/* BLOCK BY DEFAULT; FLEX ONLY WHEN GIVEN THE BODY IT WAS BUILT FOR.
   This was display:flex unconditionally, for a three-part banner — colour bar, body, actions.
   Seven of the eleven callers in the app pass PLAIN SENTENCES instead, and every inline run in a
   flex container becomes its own flex item: a notice with two bold phrases rendered as five narrow
   columns of broken text (Settings → Accounting, photographed 2026-08-29). Patching the seven
   callers would leave the trap armed for the eighth — a component that shatters when handed a
   sentence has the wrong contract, not the wrong callers. :has() lets the four structured callers
   keep their bar and layout untouched, and changes no caller at all. */
.ksbanner{max-width:820px;margin:0 auto 14px;background:#fff;border:1px solid #e3e9ef;
  border-radius:12px;box-shadow:0 4px 16px rgba(20,40,60,.07);padding:12px 16px;
  display:block;line-height:1.55;
  font-family:"Plus Jakarta Sans",-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,system-ui,sans-serif}
.ksbanner:has(.kb-body){display:flex;gap:12px;align-items:flex-start;line-height:normal}
.ksbanner .kb-bar{flex:none;width:5px;min-height:34px;align-self:stretch;border-radius:3px;background:#8a97a5}
.ksbanner .kb-body{min-width:0;flex:1 1 auto}
.ksbanner .kb-t{font-weight:800;font-size:15px;color:#1a2430;line-height:1.4}
.ksbanner .kb-t .kb-meta{font-weight:400;color:#66788c;font-size:13px}
.ksbanner .kb-s{font-size:13px;color:#66788c;line-height:1.55;margin-top:2px}
.ksbanner .kb-a{margin-left:auto;flex:none;align-self:center;display:flex;gap:8px;flex-wrap:wrap}
.ksbanner .kb-a button,.ksbanner .kb-a a{font:inherit;font-size:12.5px;font-weight:700;cursor:pointer;
  color:#31414f;background:#fff;border:1px solid #dce3ea;border-radius:9px;padding:8px 15px;
  text-decoration:none;display:inline-flex;align-items:center}
.ksbanner .kb-a button:hover,.ksbanner .kb-a a:hover{border-color:#b9c5d0}
/* Severity is the marker's colour and nothing else. */
.ksbanner.info    .kb-bar{background:#8a97a5}
.ksbanner.preview .kb-bar{background:#4a68d9}
.ksbanner.success .kb-bar{background:#1a9c62}
/* The details line under a description: present, subordinate, never mistaken for the line itself. */
.ksd-linedet{font-size:.86em;line-height:1.45;color:var(--ksd-dim,#6f6f6f);margin-top:2px;
  white-space:pre-wrap;overflow-wrap:anywhere}
.ksbanner.updated .kb-bar{background:#1a9c62}
.ksbanner.draft   .kb-bar{background:#8a97a5}
.ksbanner.readonly .kb-bar{background:#8a97a5}
.ksbanner.archived .kb-bar{background:#8a97a5}
.ksbanner.warn    .kb-bar{background:#c07a00}
.ksbanner.sim     .kb-bar{background:#c07a00}
.ksbanner.error   .kb-bar{background:#d13045}
@media (max-width:620px){ .ksbanner{flex-wrap:wrap} .ksbanner .kb-a{margin-left:0;width:100%} }
@media print{ .ksbanner{display:none !important} }

/* ================================================================================================
   THE PUBLIC DOCUMENT ACTION BAR — one component for the foot of every hosted document.

   Today that is the hosted invoice; a hosted quote, estimate or proposal adopts these classes
   rather than growing its own footer. One action by design: Download PDF. The Contact button
   that used to sit beside it is gone — the company's details are on the letterhead and its email
   is in the payment section, so a second route to the same address was furniture. Centred,
   sized to the site's compact button convention, with room to breathe above. Never printed.
   ================================================================================================ */
.ksdocacts{display:flex;justify-content:center;gap:10px;margin-top:34px;padding:28px 34px 34px;
  border-top:1px solid #eceff2}
.ksdocacts button,.ksdocacts a{appearance:none;display:inline-flex;align-items:center;justify-content:center;
  font:inherit;font-size:12.5px;font-weight:700;color:#31414f;background:#fff;
  border:1px solid #dce3ea;border-radius:9px;padding:9px 18px;min-height:38px;cursor:pointer;text-decoration:none}
.ksdocacts button:hover,.ksdocacts a:hover{border-color:#b9c5d0}
.ksdocacts button:focus-visible,.ksdocacts a:focus-visible{outline:2px solid #4a68d9;outline-offset:2px}
@media (max-width:560px){ .ksdocacts{padding:22px 20px 26px} .ksdocacts button,.ksdocacts a{width:100%;max-width:320px} }
@media print{ .ksdocacts{display:none !important} }

/* ══ THE PAGE SHELL, SHARED ══════════════════════════════════════════════════════════════════════
   The hosted invoice page defined .sheet in its own <style>, so anything else rendering the same
   document had to reproduce it. docPreviewWindow did not, and the owner's preview came out
   full-bleed across the window instead of as a centred page — a second preview that looked like a
   different product. Owner, 2026-09-02: "make it look like the other preview pages — do not
   reinvent previews here."

   It lives here now, in the stylesheet every surface that draws this document already loads, so
   there is one page shell rather than one per opener. invoice.html keeps its own copy for the
   moment; the rules are identical, so whichever wins produces the same page. */
.sheet{background:#fff;max-width:820px;margin:0 auto;border-radius:16px;padding:0;
  box-shadow:0 12px 40px rgba(20,40,60,.12);border:1px solid #e3e9ef;overflow:hidden}
@media print{ .sheet{box-shadow:none;border:0;max-width:none;padding:0} }
