/* ════════════════════════════════════════════════════════════════════════════
   n-print.css — the office on paper (office spec §6)
   Loaded with media="print", so nothing in here can touch the screen.

   PAPER CONSTANTS. `A4 portrait` and `16mm` below are not design values and
   have no token: they are the physical sheet and the physical margin the
   spec names (§6), the way a bill's 80 mm roll is physical. The token file
   describes screens; a printer's paper cannot be themed, so these two live
   here — the only literals in this file, and listed for the lead alongside
   css/n-office.css's three.

   Everything else is ink on paper: the chrome (rail, command line, inspector,
   every button) goes, colour becomes ink, the table's head repeats on every
   page, and a block marked data-print="page" starts a fresh one.
   ════════════════════════════════════════════════════════════════════════ */

@page {
  size: A4 portrait;                    /* paper constant, not a design value */
  margin: 16mm;                         /* paper constant, not a design value */
}

/* ── ink on paper, whatever the screen was set to ─────────────────────── */
:root,
:root[data-scheme="dark"] {
  --n-bg: var(--n-paper);
  --n-surface: var(--n-paper);
  --n-surface-raised: var(--n-paper);
  --n-surface-sunken: var(--n-paper);
  --n-surface-well: var(--n-paper);
  --n-text: var(--n-ink);
  --n-text-2: var(--n-ink);
  --n-text-muted: var(--n-ink);
  --n-text-faint: var(--n-ink);
  --n-rule: var(--n-ink);
  --n-accent-text: var(--n-ink);
  --n-accent-soft: transparent;
  --n-ok: var(--n-ink);
  --n-warn: var(--n-ink);
  --n-danger: var(--n-ink);
  --n-shadow-1: none;
  --n-shadow-2: none;
  --n-shadow-e: none;
}

/* The tenant's accent is written by js/00-accent.js as INLINE custom
   properties on <html> — and an inline declaration outranks any :root block,
   however specific. On paper that meant the restaurant's colour printed as
   itself: a saffron total, a mauve rule, a wash behind the selected row that
   the ink of the sheet had to compete with. An IMPORTANT author declaration
   beats a normal inline one, which is the one lever that reaches an inline
   custom property from a stylesheet, so the whole accent is forced to ink
   here, and the chrome with it — a dark screen still prints light. */
@media print {
  :root {
    --n-accent: var(--n-ink) !important;
    --n-accent-text: var(--n-ink) !important;
    --n-accent-line: var(--n-ink) !important;
    --n-accent-soft: transparent !important;
    --n-accent-ink: var(--n-paper) !important;

    --n-bg: var(--n-paper) !important;
    --n-surface: var(--n-paper) !important;
    --n-surface-raised: var(--n-paper) !important;
    --n-surface-sunken: var(--n-paper) !important;
    --n-surface-well: var(--n-paper) !important;
    --n-text: var(--n-ink) !important;
    --n-text-2: var(--n-ink) !important;
    --n-text-muted: var(--n-ink) !important;
    --n-text-faint: var(--n-ink) !important;
    --n-rule: var(--n-ink) !important;
    --n-border: var(--n-ink) !important;
    color-scheme: light !important;
  }
}
[data-surface="office"] { background: var(--n-paper); }
.n-office { background: var(--n-paper); color: var(--n-ink); }

/* ── the chrome goes ──────────────────────────────────────────────────── */
.n-cmdrow,
.n-orail,
.n-oinspector,
.n-oacts,
.n-tbl-bar,
.n-tbl-foot,
.n-block-head .n-osegs,
#helpFab,
#vcBtn,
#toastRoot,
#nToastRoot,
#nSheetRoot,
.n-empty .n-obtn,
.n-tbl-emptyact,
.n-office button { display: none !important; }

/* ── data entry goes too ──────────────────────────────────────────────
   A report is a record of what happened; a blank box waiting for someone to
   type a category or an amount is not part of that record. `.n-office
   button` above already drops the "Add the expense"/"Save" buttons, but the
   labels and the empty inputs beside them stayed — Reports used to end on a
   page that was just "Category · Amount · Date · Note · VAT on the slip"
   with nothing filled in. #nOfB-rep-addexp is its own block (heading and
   all, since none of it is a report); .n-formrow is the same shape used
   inline inside a block that DOES belong on paper (Sales & money's Uber Eats
   commission editor sits beside the payout sums, so only the editor goes,
   not the block); a literal <form> is the general case for whatever a later
   batch (Payroll, Books) puts inside a block. */
#nOfB-rep-addexp,
.n-formrow,
.n-block form { display: none !important; }

/* Batch B2's own chrome. The buttons inside each of these are already gone
   (`.n-office button` above), but the BOXES they sat in were not, and an
   empty flex row still takes its height: Items printed a 44 px band of
   nothing where the category chips had been, and the stocktake sheet's drop
   zone printed as an empty dashed rectangle. A filter is not a finding and
   an editable line is not a record, so both go with them. */
.n-chips,
.n-editrow,
.n-ins-rowacts,
.n-drop,
.n-histrow-a { display: none !important; }

/* Batch B2b: a whole block that is work in progress rather than a record.
   Stock › Invoices prints its ARCHIVE — what was received, from whom, for how
   much — and nothing else: the drop zone, the "reading it with the AI… 41s"
   counter and a draft nobody has confirmed are all things in the middle of
   being done, and a sheet of paper that says a delivery was received when it
   has not been is worse than no sheet at all. Menu › Menu import is that
   block and only that block, so it prints its page header and stops.
   js/n-office-views-b2.js marks the two intake blocks; the attribute is the
   general mechanism, so a later batch's counter can say the same thing. */
[data-print="never"] { display: none !important; }

/* Batch B3a: People. Two things on these screens are keys rather than
   records, and neither may leave the building on an A4 sheet by accident.

   A QR IS THE CREDENTIAL. A manager's authorisation code approves a void; a
   staff card's code logs someone in. Both have their OWN printed paths —
   printOverrideQr() and printIdCard(), which open a window of their own with
   the code alone on it — and both are reached deliberately, from a sheet.
   The page's copies (the sheet's preview, the void-code column's thumbnail)
   are on screen to be READ, so they are never part of a printout of the
   screen: an Employees list left on a desk must not be a set of working
   keys. (Sheets do not print at all — #nSheetRoot is already hidden above —
   so this is the void-code column and anything a later batch adds.)

   The claim offer is the other one: "your lunch is ready to claim" is an
   invitation with a button, not a record of anything, and the button is
   already gone by the time it reaches paper. */
.n-ofp-qr,
.n-ofp-qrbox,
.n-ofp-claim { display: none !important; }

/* The initials wear a wash on screen; on paper they are an outline, like
   every other state that was a colour. */
.n-ofp-av {
  background: transparent;
  color: var(--n-ink);
  border: thin solid var(--n-ink);
}

/* A tab that is not showing is not on the paper either — the same rule
   .n-reptab[hidden] gives Reports, for the two People screens and Payroll. */
.n-emptab[hidden],
.n-smtab[hidden],
.n-prtab[hidden] { display: none !important; }

/* Batch B3b: Payroll and Books.

   A FIELD PRINTS AS ITS VALUE. The payroll run is the one table on this
   surface whose cells are inputs and selects from end to end, and a printed
   run that is a page of empty-looking boxes is not a payroll record —
   somebody has to be able to sign it. On paper each field loses its box, its
   border and its background and prints the value it is holding, in the
   column it belongs to. (`.n-office button` above has already taken the
   "Reload" button inside a stale row's note away with every other button.)
   A select keeps the office's chevron on screen only; here it is dropped
   with everything else that was furniture. */
.n-tbl .n-cellin,
[data-surface="office"] .n-tbl select.n-cellin {
  appearance: none;
  -webkit-appearance: none;
  padding: 0;
  min-height: 0;
  border: 0;
  background: transparent;
  background-image: none;
  color: var(--n-ink);
}
[data-n="table-wrap-payroll-run"] tbody tr { background: transparent !important; }
/* …except where the box is EMPTY, because an empty box does not print as
   nothing: it prints its PLACEHOLDER. Bonus and Breakage are the two columns
   most rows leave blank, and every one of them printed a grey "0" that the
   eye reads as a figure somebody entered (fix round, D5). Each of those two
   cells carries its own printed words beside the box (`.n-cellval`, written
   by nOfPrRunUpd from _prComputed, the same source as Base pay and Tips), so
   on paper the field goes and the value stays. */
[data-n="table-wrap-payroll-run"] tbody [id^="pbon_"],
[data-n="table-wrap-payroll-run"] tbody [id^="pbrk_"] { display: none !important; }
.n-cellval { display: inline; }

/* …and the PAY SETUP does not print at all. Thirteen columns do not fit
   across 178 mm of A4 portrait: printed whole, the run's own header ran off
   the right-hand edge and took "Net payout" — the one figure the sheet
   exists for — with it, while every name wrapped into a four-line stack
   (measured on the PDF: the head ended at UIF). Five of those columns are
   the RULE rather than the record — whether a person is in this run, how
   they are paid, at what rate, for how long and in which rebate tier — and
   every one of them is said again on that person's own payslip, which is
   what a payroll printout is signed off. So the eight the old printed table
   had are what prints: the name and the seven amounts.

   The CONTENT is hidden rather than the column, and by the ids the old
   js/23-payroll-branches.js reads rather than by position: a column the
   owner has switched off from the header's ⋯ would slide an :nth-child rule
   onto the wrong money. An emptied column collapses to its padding under
   `table-layout: auto` (above), so the amounts get the width back. */
[data-n="table-wrap-payroll-run"] thead th[data-key="inc"],
[data-n="table-wrap-payroll-run"] thead th[data-key="rule"],
[data-n="table-wrap-payroll-run"] thead th[data-key="qty"],
[data-n="table-wrap-payroll-run"] thead th[data-key="age"] { visibility: hidden; }
/* …and their HEADINGS go, not just their ink. `visibility: hidden` keeps the
   box, and a box still demands the width of the words inside it: "Time
   worked", "Pay rule" and "Age band" were holding 219 pt of an A4 sheet open
   for four columns printing nothing, and that was the room the name column
   needed (fix round, D5 — measured: 38 + 51 + 72 + 58 px). Emptied, each
   collapses to its own padding. By data-key, like everything else here, so a
   column the owner has switched off cannot slide the rule onto another
   one's money. */
[data-n="table-wrap-payroll-run"] thead th[data-key="inc"] :is(.n-th-label, .n-th-btn),
[data-n="table-wrap-payroll-run"] thead th[data-key="rule"] :is(.n-th-label, .n-th-btn),
[data-n="table-wrap-payroll-run"] thead th[data-key="qty"] :is(.n-th-label, .n-th-btn),
[data-n="table-wrap-payroll-run"] thead th[data-key="age"] :is(.n-th-label, .n-th-btn) { display: none !important; }
[data-n="table-wrap-payroll-run"] tbody [id^="pinc_"],
[data-n="table-wrap-payroll-run"] tbody [id^="pbasis_"],
[data-n="table-wrap-payroll-run"] tbody [id^="prate_"],
[data-n="table-wrap-payroll-run"] tbody [id^="phrs_"],
[data-n="table-wrap-payroll-run"] tbody [id^="phrsu_"],
[data-n="table-wrap-payroll-run"] tbody [id^="phint_"],
[data-n="table-wrap-payroll-run"] tbody [id^="page_"] { display: none !important; }
/* …and the columns that are left divide what the sheet has. css/n-table.css
   gives every header `width: calc(--n-tbl-col * --n-col-w)` — 128 px times
   the column's ratio — which under `table-layout: auto` is read as a MINIMUM,
   and thirteen minimums add up to more than A4 is wide: the emptied columns
   above gave their content back but not their width, and "Net payout" was
   still off the right-hand edge (measured on the PDF). This is the same
   sentence css/n-office.css §14 says at the phone breakpoint, for the same
   reason and in the same words: at this width the office's contract is that
   the columns fit, and that outranks any one column's declared share. Scoped
   to this one table, so every other printed table keeps the auto layout the
   lead ruled on in batch B3a. */
[data-n="table-wrap-payroll-run"] thead th { width: auto; }
/* An AMOUNT never breaks. `auto` shares the sheet out by shrinking whichever
   column can wrap, and with the eight left it chose the money: "R 18 500.00"
   printed as "R 18" over "500.00" and the header's "Base pay" over three
   lines, which pushed a fragment of the repeating head onto the next sheet.
   The name column is the one that has prose in it (the pro-rata note), so it
   is the one that should absorb the shortfall — which is exactly the reason
   `.n-nowrap` exists further up this file, said here for a whole table. */
[data-n="table-wrap-payroll-run"] :is(th, td).n-num { white-space: nowrap; }
/* …and the column that absorbs the shortfall still needs a floor. `auto` gave
   the name column whatever was left after eight nowrap amounts, which on a
   row carrying the pro-rata note ("Monthly salary doesn't pro-rate — this
   books the full month's pay for the range.") was one word per line and
   eleven lines deep (measured on the PDF, fix round D5). One and a half
   --n-tbl-col is the width the note sets in three or four lines, and it is
   the only minimum on the table — every other column is an amount that
   cannot break anyway. */
[data-n="table-wrap-payroll-run"] thead th[data-key="name"] { min-width: calc(var(--n-tbl-col) * 1.5); }

/* A LIST OF SHORT ROWS IS NOT BROKEN ACROSS A SHEET (fix round, B3). The
   running header above is `position: fixed`, so Chrome paints it over the
   top of every page and the flow has to buy that strip back — which .n-block
   and a repeating <thead> do, and which a plain row of a list does not. A
   payroll summary (`_nOfSums` → .n-sums: eleven lines from Base pay to
   Employer wage cost) that happened to straddle a break therefore came back
   on the next sheet with the header printed THROUGH one of its lines: "To
   SARS · PAYE" struck out by the restaurant's own name, on the sheet that
   goes to the accountant. Every list of short rows on this surface says so
   here: it stays whole, and each row inside it stays whole too, so a break
   can only ever fall BETWEEN two of them.

   A list taller than a whole sheet cannot stay whole, so the row itself buys
   the strip back the way a block does — the padding is cancelled by an equal
   negative margin, which nothing can see in normal flow and which is the one
   mechanism a stylesheet has for "clearance only where the browser decided to
   break". */
.n-sums,
.n-checks,
.n-ofp-list { break-inside: avoid; }
.n-sum,
.n-checkrow,
.n-ofp-listrow { break-inside: avoid; }
/* …and the same for every other SHORT box that can begin a page. A block
   that holds a table is the only kind that flows across a break at all
   (`.n-block:has(.n-tbl)`, below), so its own short children are the boxes a
   break can land in front of: the summary list and its rows, the sentence
   under them ("9 of 9 employees in this run"), a lead paragraph. Found on
   the 9-employee run: the sums stayed whole and the sentence AFTER them went
   over on its own, straight under the restaurant's name. Everything inside
   the table itself is excluded — a row's notes live in a <td>, where a
   negative margin would drag the cell's own lines over each other, and a row
   is short enough never to straddle a break anyway. */
/* …and where a list DOES open a sheet, it buys the header's strip back the
   way .n-block does — the one mechanism that is proven on this page (a
   negative margin to hide the gap in normal flow was tried and is not: at a
   fragment start Chrome keeps the margin and the clearance is cancelled,
   measured on the nine-employee run). The cost is a strip of white above
   every printed summary, which reads as the separation a set of totals wants
   anyway. */
.n-sums { padding-top: var(--n-print-head); }
/* …and the sentence UNDER a summary goes with it. Found on the nine-employee
   run: the list stayed whole (the rule above) and "9 of 9 employees in this
   run" went over the break on its own, straight under the restaurant's name
   on the next sheet. A total and the sentence that qualifies it are one
   reading, so they travel together — and when the pair does move, the list's
   first row is what opens the page, which is the box that buys the strip. */
.n-sums { break-after: avoid; }
/* The list is a formatting context of its own, so the rows' negative margins
   can never collapse out through it and cancel the clearance above (the same
   reason .n-block-body is not given this treatment: a negative margin that
   escapes a block hoists the block's own heading into the header). */
.n-sums { display: flow-root; }

/* THE PAYSLIP AND THE VAT201, one sheet each (spec §6). Both already carry
   data-print="page", which the rule further down turns into a page break;
   these two say what a page of that kind may not do. A payslip is a name, a
   period and eight figures, and it must never be split down the middle —
   half a payslip is a document that says the wrong net pay. The VAT201 is
   the same case with SARS's name on it. */
[data-n="pr-payslip"],
#nOfB-bk-vat { break-inside: avoid; }
/* …and the block that introduces the payslips does not print AT ALL. It is
   one screen sentence — "one sheet of paper each when this page is printed"
   — about the very sheets it is sitting on, and it introduces pages that
   each carry the person's own name. `break-after: avoid` used to keep it
   with the first payslip; once the summary above it stopped being split
   (B3) it no longer fitted on the run's page, and an orphaned block gets no
   padding on its continuation, so it printed a heading underneath the
   running header on a sheet of its own (measured on the four-employee run).
   The block that says nothing on paper is the block that goes. */
#nOfB-pr-slips { display: none !important; }

/* ── the page header the spec asks for: restaurant · screen · range ·
      printed date. Filled by js/n-office.js; invisible on screen. ───────
   RUNNING, not once. A block in normal flow prints at the top of page one
   and nowhere else, so page four of a stocktake was four hundred numbers
   with nothing on the sheet saying whose stock it was or when it was
   counted — the one thing a printout that leaves the building has to carry.
   `position: fixed` is the only mechanism a stylesheet has for a running
   header: Chrome paints a fixed box once per printed page, at the top of
   the page area. It is out of flow, so the content column reserves the
   height back (below) rather than sliding underneath it. */
/* TWO SLOTS (fix round, O11): what this sheet is, at the left; when it came
   off the printer, at the right. js/n-office.js writes the two spans; the
   gap between them is the page, not a dash. */
.n-printhead {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--n-space-4);
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  padding-bottom: var(--n-space-2);
  border-bottom: thin solid var(--n-ink);
  background: var(--n-paper);
  font-family: var(--n-font-ui);
  font-size: var(--n-step--1);
  color: var(--n-ink);
}
/* Neither slot may break across the line or crowd the other. */
.n-printhead-l { min-width: 0; }
.n-printhead-r { flex: 0 0 auto; white-space: nowrap; }

/* …and the height it takes off the top of every page, given back twice —
   because "every page" is two different boxes here and only one of them is
   laid out once:

     · the content column reserves it for PAGE ONE, where the flow starts
       with the breadcrumb and the title;
     · the table's <thead> reserves it for every page AFTER that, because a
       repeating head is the one box in the document that is laid out again
       on each sheet. Without this the column headings printed underneath the
       restaurant's name on page two and every page after it (measured: the
       head's first row at 71.4 pt against a header occupying 80.5 pt).

   Anything lifted out of the page area instead — a negative `top` into a
   widened @page margin — stops Chrome repeating the box at all (measured:
   the header vanished from the last page and moved to 178.7 pt on the
   rest), so the header stays inside the page area and the content moves. */
/* The strip the running header takes off the top of every sheet. Declared
   once, spent in the three places below. */
:root { --n-print-head: calc(var(--n-space-6) + var(--n-space-2)); }
[data-surface="office"] .n-ocontent,
[data-surface="office"] #main { padding-top: var(--n-print-head); }


/* ── the layout unwinds into one column of paper ──────────────────────── */
/* All the way up. The app's #shell is a grid and the office shell is one of
   its cells; a table that fragments inside a grid item does NOT repeat its
   <thead> in Chrome — the head appeared on the table's first page and then
   never again. On paper there is one column, so every box between the sheet
   and the table is put back into normal block flow. */
[data-surface="office"] #shell {
  display: block;
  height: auto;
  overflow: visible;
}
.n-office {
  display: block;
  height: auto;
  overflow: visible;
}
.n-ocontent,
.n-obody,
.n-ohead {
  overflow: visible;
  padding-left: 0;
  padding-right: 0;
}
/* The screen's scroll-tail under the last block. On paper it is 64 px of
   nothing, and 64 px of nothing after the last row of a table that ended
   near a break is a whole extra sheet carrying the running header and not
   one other mark. */
.n-obody { padding-bottom: 0; }
/* …and the last block's own tail with it, for the same reason: six points of
   padding hanging over a page boundary printed a whole sheet holding the
   running header and nothing else. */
.n-block:last-child { padding-bottom: 0; }
[data-surface="office"] #main { overflow: visible; }
.n-otitle { font-family: var(--n-font-display); font-size: var(--n-step-3); }
.n-crumb { color: var(--n-ink); }

/* ── blocks and their rules ─────────────────────────────────────────────
   A fresh sheet of paper is expensive, and a page holding one line of a
   report is what the owner throws away. So only what the spec NAMES starts
   a page — a payslip, per person; the VAT201, per period — and everything
   else simply tries not to be split down the middle. */
/* A block and a repeating <thead> are the two boxes that can begin a page,
   so they are the two that buy the header's strip back. (A table ROW can
   begin one too, but paying for the strip on every row would cost a sheet of
   paper for every four printed — and on a real A4 page area a 36 px row does
   not straddle a break.) */
.n-block {
  border-top: thin solid var(--n-ink);
  break-inside: avoid;
  padding-top: var(--n-print-head);
}
.n-tbl thead th { padding-top: var(--n-print-head); }
/* …but never a block holding a table. A table is the one thing that is
   routinely taller than a sheet of paper, and "avoid" on it costs a whole
   page: the browser shunts the block to the next page, breaks it there
   anyway, and leaves page 1 holding the title. It flows, and its rows and
   its repeating head do the tidiness instead. */
.n-block:has(.n-tbl) { break-inside: auto; }
.n-block-h { break-after: avoid; }
[data-print="page"] { break-before: page; }
[data-print="page"]:first-child { break-before: auto; }
.n-reptab[hidden] { display: none !important; }

/* ── tables: the head repeats, the figures stay tabular ─────────────────
   Chrome only repeats a <thead> on every page when the table is a real
   table (border-collapse: collapse), the head is a static
   table-header-group, and neither is inside a scroll container — the
   screen's `overflow:auto; max-height` box was silently turning the whole
   table into one un-breakable block on page 1. */
.n-tbl-scroll {
  overflow: visible;
  max-height: none;
  height: auto;
}
/* `auto`, not `fixed`. Round 2 tried carrying the screen's ratios onto paper
   (`table-layout: fixed` plus the header's `--n-col-w` width, restated below)
   and it read the room wrong the other way: A4 is not 1440px, so a column
   sized for the SCREEN wrapped on PAPER — Receipts' three-word "Time" column
   wrapped to two lines and pushed an 8-page slip to 9, and Inventory's "Last
   edited" did the same. `auto` is what round 1 measured names on one line
   under, and the lead's ruling (batch B3a) is that print keeps it: the
   browser fits each column to what is actually printed on THAT page, which
   is the one thing a fixed ratio inherited from a 1440px screen cannot do.
   The `fixed` behaviour — the drag, the saved-view ratio — stays on screen
   only; css/n-table.css is untouched. */
.n-tbl table {
  table-layout: auto;
  border-collapse: collapse;
}
.n-tbl thead {
  position: static;
  display: table-header-group;
  break-inside: avoid;
}
.n-tbl tfoot { display: table-footer-group; }
.n-tbl tbody tr {
  break-inside: avoid;
  background: transparent;
}
.n-tbl tbody tr[aria-selected="true"] { background: transparent; }
.n-tbl tbody tr[aria-selected="true"] td:first-child { box-shadow: none; }
.n-tbl thead th {
  position: static;
  background: transparent;
  border-bottom: thin solid var(--n-ink);
  color: var(--n-ink);
  /* No width rule: under `table-layout: auto` (above) the browser sizes each
     column from what it actually holds on THIS page, which is what fits
     paper. The screen's own --n-col-w ratio (css/n-table.css) is left where
     it belongs — on screen. */
}
.n-tbl thead th[data-cursor] { box-shadow: none; }
.n-tbl tbody td { border-bottom: thin solid var(--n-border-soft); white-space: normal; }
/* `.n-nowrap` (js/n-table.js's `nowrap` column flag) is the one exception to
   the rule above. `table-layout: auto` still has to share a page that is not
   as wide as every column wants, and it pays for that by shrinking whichever
   column CAN break — a date-and-time reading, the one column on Receipts and
   on Inventory with a space in every cell — before it touches an identifier,
   a name or an amount, none of which has anywhere to break at all. The
   result was "Fri 5 Sep" over "19:42" on every row (measured: an 8-page
   Receipts slip became 9). Nothing is gained by letting a date break, so
   these two say so and the columns that DO need the room — Time, Last
   edited — stop being where auto-layout's shrinkage lands. */
.n-tbl .n-nowrap { white-space: nowrap; }
/* On screen the gap between two columns is the sortable header BUTTON's own
   padding (.n-th-btn, css/n-table.css) — a th/td itself carries none. Print
   turns that button to `display: contents` (the rule below, kept for the
   reason written there) and takes its padding with it, so "Value" and "Last
   edited" butted against each other with nothing between them. Adjacent
   cells get the gap back directly, on the LEFT of the second one only, so
   the first column keeps its edge flush with the page margin. */
.n-tbl :is(th, td) + :is(th, td) { padding-left: var(--n-space-3); }
.n-num,
.n-tbl .n-num {
  font-variant-numeric: var(--n-font-num-variant);
  font-feature-settings: var(--n-font-num-features);
}
/* A sortable header is a <button>, and the rule above hides every button —
   its words have to stay. `contents`, not `block`: a <button> BOX inside a
   <thead> is the one thing that stops Chrome repeating the head on page two
   (measured — a header cell holding a button repeats once, the same cell
   holding a span repeats on every page). `display: contents` takes the box
   away and leaves the words in the <th>, which is all paper wanted. */
.n-tbl .n-th-btn { display: contents !important; }
.n-tbl .n-th-mark, .n-tbl .n-th-grip { display: none !important; }

/* ── a state is a word, not a colour ──────────────────────────────────── */
.n-flag,
.n-quad {
  background: transparent;
  color: var(--n-ink);
  border: thin solid var(--n-ink);
}
.n-check-dot { background: var(--n-ink) !important; }
.n-bar-t { background: transparent; border: thin solid var(--n-border-soft); }
.n-bar-t > i { background: var(--n-ink); }
.n-donut-seg { stroke: var(--n-ink); }
.n-legend-sw { background: var(--n-ink); }

/* ── links print as words; the URL is noise on paper ──────────────────── */
a[href]::after { content: ""; }

/* ── batch B4a: Guests ────────────────────────────────────────────────
   Customers & partners and Bookings are tables, and the rules above already
   print a table properly (repeating <thead>, ink rules, tabular figures).
   Three things on these four screens need saying.

   1. NOTHING that is data ENTRY prints. The forms are already covered
      (.n-formrow, .n-block form, every button, the whole inspector); these
      are the boxes that are not inside one — the three connection panels, the
      send form and the bot's own facts, each of which is a page of
      credentials and switches rather than a record. Each carries
      data-print="never" in the markup, which is hidden above; the blocks
      themselves would still print their heading and their explanation, so
      the WHOLE block goes. */
#nOfB-res-wa,
#nOfB-res-email,
#nOfB-res-google,
#nOfB-wa-send,
#nOfB-wa-profile { display: none !important; }

/* 2. A THREAD PRINTS AS A TRANSCRIPT. On screen it is two columns of
      bubbles, which is how a conversation is read; on paper a bubble
      floating against the right margin is a puzzle, and the wash it is told
      apart by is not there at all. So the pane becomes one column, every
      line starts at the margin, and the SPEAKER — a visually-hidden word in
      the markup, so a screen reader has always had it — becomes visible ink.
      Date · sender · text, one line per message, in the order they were
      sent. */
.n-msg {
  display: block !important;
}
.n-msg-list { display: none !important; }      /* the index, not the record */
.n-msg-reply { display: none !important; }
/* A TRANSCRIPT IS A TABLE, and for the one reason a table is: it is the only
   box in this document that repeats a head on every sheet. The running
   header is `position: fixed` (above), so whatever opens a page has to buy
   its strip back, and a transcript's rows are a hundred short boxes — paying
   for the strip on every one of them would cost a sheet of paper for every
   three messages, which is the same arithmetic that keeps a table ROW from
   paying for it. Measured before this rule: pages three and four of a
   44-message thread opened with a bubble printed THROUGH the restaurant's
   name.

   So on paper the thread becomes a table: the head (whose conversation this
   is) repeats on every sheet and buys the strip, and each message is a row.
   The markup is untouched — it is `display` alone, exactly as
   `.n-tbl-scroll` unwinds the screen's scroll box. */
.n-msg-thread { padding-left: 0; border-left: 0; }
.n-msg-t {
  display: table;
  width: 100%;
  table-layout: auto;
  border-collapse: collapse;
}
.n-msg-head { display: table-header-group; }
.n-msg-head tr { display: table-row; }
/* The head buys the running header's strip back on every sheet, exactly as
   .n-tbl thead th does — that is the whole reason the thread is a table. */
.n-msg-headcell {
  display: table-cell;
  padding-top: var(--n-print-head);
  text-align: left;
  break-inside: avoid;
}
.n-msg-bubbles {
  display: table-row-group;
  max-height: none;
  overflow: visible;
  margin: 0;
  padding: 0;
}
.n-msg-b { display: table-row; break-inside: avoid; }
/* …and the block that holds a transcript flows, for the reason
   `.n-block:has(.n-tbl)` above flows: a block that "avoids" a break and is
   taller than a sheet is shunted to the next page, breaks there anyway, and
   leaves the first sheet holding nothing but the title (measured on a
   44-message thread: page one was the breadcrumb and four blank fifths). */
.n-block:has(.n-msg-t) { break-inside: auto; }
.n-msg-bb {
  display: table-cell;
  max-width: none;
  padding: var(--n-space-1) 0;
  background: transparent !important;
  border: 0;
  border-bottom: thin solid var(--n-border-soft);
  border-radius: 0;
  color: var(--n-ink);
  break-inside: avoid;
}
/* The speaker, and the time, become the line's own label — read before the
   words, as a transcript is. */
.n-msg-bb .n-msg-who-p {
  position: static !important;
  width: auto !important;
  height: auto !important;
  margin: 0 !important;
  clip: auto !important;
  clip-path: none !important;
  overflow: visible !important;
  white-space: normal !important;
  font-weight: var(--n-w-semibold);
}
.n-msg-txt { display: inline; }
.n-msg-meta { display: block; justify-self: start; color: var(--n-ink); }
.n-msg-fail { display: block; }

/* 3. …and the screen's own chrome on these four goes with the rest of it:
      the day picker, the tab strips and the two disclosures are controls. */
.n-ofg-help { display: none !important; }

/* ── batch B4b: Setup, and the pass over every office screen (spec §6) ───
   Printers, Help, Reset, Benchmark and Appearance, plus what a page.pdf of
   all twenty-two office ids at A4 turned up on the screens before them.

   1. A TAB THAT IS NOT SHOWING IS NOT ON THE PAPER either — the same rule
      .n-emptab, .n-smtab and .n-prtab already have, for Printers' four. What
      prints is the tab the owner is on, which is what is on the screen. */
.n-prntab[hidden] { display: none !important; }

/* 2. EVERYTHING THAT CAN OPEN A SHEET BUYS THE RUNNING HEADER'S STRIP, and
      it is said ONCE (fix round, B2). The running header is `position: fixed`
      and takes --n-print-head off the top of every page, so any box that can
      land at a page top has to pay for it — .n-block and a repeating <thead>
      already did, and the Help guides were the third case, patched on their
      own. Two more turned up on a per-PANEL pass: the Bill design tab's
      preview figure, which carries data-print="page" and so ALWAYS opens a
      sheet ("The bill" printed through "Printers · printed Wed 9 Sep"), and
      Benchmark › Your progress, whose section heading opened page two
      ("Your progress against your class" through the same line). Rather
      than a third and fourth patch, the four selectors below are the ONE
      rule: a box that starts a page (data-print="page"), and every heading
      inside the office's content column that can land at a page top —
      a section heading (.n-ins-h) and a block heading whose block flows
      because it holds a table.

      A heading also never leaves the thing it introduces behind
      (`break-after: avoid`), and the box directly under a heading that has
      just paid does not pay a second time. */
[data-print="page"],
[data-surface="office"] #main .n-ins-h,
[data-surface="office"] .n-ocontent .n-ins-h,
[data-surface="office"] .n-block:first-child {
  padding-top: var(--n-print-head);
}
/* …the last of those four because css/n-office.css's `.n-block:first-child`
   drops a block's rule and its padding — right on screen, where the page
   header is the rule above the first block — and outranks this file's own
   `.n-block` by a class. A tab PANEL has a first child too: Benchmark ›
   Your progress is `#bmTab > .n-block:first-child` and printed its heading
   through the running header for exactly that reason. The document's own
   first block is the one case where it is still right: the page area's top
   padding has already bought the strip there, so it pays nothing. */
[data-surface="office"] .n-obody > .n-block:first-child { padding-top: 0; }
[data-surface="office"] :is(.n-ins-h, .n-block-h) {
  break-inside: avoid;
  break-after: avoid;
}
/* The first heading inside a block that has just bought the strip itself
   would double the gap; the block is the box that opened the page. */
.n-block-body > .n-ins-h:first-child,
.n-osu-billgrid > .n-ins-h:first-child { padding-top: 0; }

/* …and A GUIDE PRINTS WHAT IT SAYS, open or closed. On screen the manual is
      34 disclosures and nobody wants them all open at once; on paper the
      whole point is the steps, so the body is shown whatever the browser's
      own details state is.

      Each guide buys the strip for the same reason as everything above.
      Measured before that rule (Help, six pages): the third and fourth
      sheets opened with a guide heading printed THROUGH the restaurant's
      line — "Help | Smart& Support | reorder· printed Wed 9 Sep watch". A
      guide is a short box in a long list, so any of the 34 can be the box
      that opens a page; there is no smaller unit to charge, and the strip
      reads as the separation a reference list wants anyway. */
.n-osu-guide {
  break-inside: avoid;
  padding-top: var(--n-print-head);
}
/* A closed <details> hides its body with the UA's own `content-visibility`
   on the slot, which `display` alone cannot reach — both are said here. */
.n-osu-guide-b {
  display: block !important;
  content-visibility: visible !important;
}
/* …and the box the browser itself hides. A closed <details> keeps its body in
   the UA's own `::details-content`, which is `content-visibility: hidden` and
   which no rule on the body's own element can reach — measured: the two
   declarations above alone printed 34 headings and not one step. */
.n-osu-guide::details-content { content-visibility: visible !important; }
/* The section heading inside the manual — "Partners & marketing" came back
   printed through the restaurant's line on sheet four — is now one of the
   headings the shared rule above charges; all that is left to say here is
   that the guide immediately under one does not pay a second time. */
#helpGuides .n-ins-h + .n-osu-guide { padding-top: 0; }

/* 3. THE BENCHMARK'S MAP IS AN INSTRUMENT, NOT A RECORD. It prints as a grey
      rectangle with a zoom control and a tile licence in it — the pins are
      the reading, and the sentence under the box (#bmMapNote) is the same
      reading in words, which is what paper can carry. */
.n-osu-map { display: none !important; }

/* 4. …and the one form on that screen that is not inside a block this file
      already hides: the Google Places key. js/26-benchmark.js draws it into
      #bmLocalCard when no key is set, so the block cannot be hidden whole —
      the local-market summary that replaces it IS a reading. The label and
      the empty box go; the paragraph that says what connecting Google buys
      you stays. (An empty box does not print as nothing: it prints its
      placeholder — "AIza…" was on the sheet.) */
#bmLocalCard label { display: none !important; }

/* …and the general case behind it, found on Tonight: A BOX YOU TYPE INTO IS
   NEVER A RECORD. An empty one does not print as nothing — it prints its
   placeholder, and the dashboard's last sheet carried "Ask anything about
   the business…" as if somebody had written it. Every form control outside a
   table therefore goes.

   INSIDE a table it stays, and that carve-out is load-bearing — measured in
   the fix round (M3) by deleting it and printing the screens that hold
   in-table controls: Payroll › Payroll run lost every figure on the sheet
   (its Hours, Days, Rate, Bonus, Breakage and Basis cells ARE inputs and
   selects — `.n-cellin`, which the rule 180 lines above deliberately keeps
   and strips of its box so the field prints as its value) and People ›
   Employees' pay-basis select went with them. Staff & comps' entitlement
   ticks are the same case: a checkbox in a cell IS the record. Without
   `:not(.n-tbl *)` a printed payroll run is a page of empty columns nobody
   can sign. */
[data-surface="office"] :is(input, textarea, select):not(.n-tbl *) { display: none !important; }

/* 5. APPEARANCE IS A PICKER. It is the one office screen with no record on
      it at all: two rows of swatches and a line of samples ("Custom… Link
      text Selected") that mean nothing in ink. The headings and the
      sentences that say what each choice does are left, so a printed sheet
      still says what the screen is for. */
[data-surface="office"] #main :is(.ap-themes, .ap-accents),
[data-surface="office"] #main [aria-label="How your colour is used"] { display: none !important; }

/* 6. A COLOUR IS NOT A COLUMN. Menu › Categories carries the till's own tab
      colour as a dot; the dot is a background and the word beside it is
      visually hidden, so the column printed as a "Colour" heading over five
      centimetres of nothing. The heading goes with it and the column
      collapses to its padding (`table-layout: auto`), which gives the width
      back to the names. By data-key, like the payroll rules above, so a
      column the owner has switched off cannot slide this onto another. */
[data-n="table-wrap-categories"] thead th[data-key="hue"] :is(.n-th-label, .n-th-btn) { display: none !important; }

/* 7. A COLUMN WHOSE CELL IS ONLY A CONTROL HAS NO HEADING ON PAPER (fix
      round, B3) — the same treatment "Colour" gets above, for the same
      reason and by the same means. `.n-office button` takes every button off
      the paper, so Print jobs' "Again" printed as a heading over four
      centimetres of nothing on all three sheets of a 40-job slip. The
      heading goes and the column collapses to its padding
      (`table-layout: auto`), which gives the width back to "Why not".
      By data-key, like the rule above, so a column the owner has switched
      off cannot slide this onto another. */
[data-n="table-wrap-print-jobs"] thead th[data-key="_again"] :is(.n-th-label, .n-th-btn) { display: none !important; }
/*    …and the third of them (closing polish). Employees › Authorisation's
      "Code" column is one "View QR" BUTTON per manager and nothing else: on
      paper it was a heading over an empty column on every sheet. It could
      not carry a paper twin the way rule 8's does either — the twin would
      have to be the manager's own override code, and an Employees list left
      on a desk must not be a set of working keys (the rule at the top of
      this file that hides .n-ofp-qr says the same thing about the picture).
      So the heading goes and the column collapses to its padding. */
[data-n="table-wrap-emp-managers"] thead th[data-key="qr"] :is(.n-th-label, .n-th-btn) { display: none !important; }

/* 8. …and a column that HAS something to say on paper says it. Reset's
      "Restored" holds a flag once the erasure has been put back and a "Put
      it back" BUTTON while it has not, so on paper half that column read and
      the other half was blank. The cell now carries both: the button, which
      only the screen can use, and its paper twin — the same words the
      column's own `text` gives the CSV, hidden on screen the way every other
      visually-hidden label on this surface is (.n-vh, css/n-office.css §0)
      and un-hidden here, exactly as a message's speaker is above. */
.n-onpaper {
  position: static !important;
  width: auto !important;
  height: auto !important;
  margin: 0 !important;
  clip-path: none !important;
  overflow: visible !important;
  white-space: normal !important;
}

/* ── batch B5: the intelligence layer (spec §7, §6) ─────────────────────
   Tonight now carries two blocks the model feeds. What a sheet of paper is
   for decides which of them prints:

   1. THE BRIEFING PRINTS, as text. It is a record — what the numbers said
      this morning and what was decided about them — and it is the one thing
      on this surface an owner might want to hand to somebody. Its own
      buttons already carry data-print="never" (and `.n-office button` takes
      every button anyway), so nothing is said here about them.

   2. THE WATCH PRINTS AS A LIST. On screen each card is separated from the
      next by a hairline; on paper the hairlines are chrome. What is left is
      what the card says: two sentences, the figure with its label, and no
      button. A card is never broken across two sheets — half a finding is
      not a finding — and the figure keeps the number face the screen gave
      it (css/n-table.css's .n-num rule covers the tabular figures; the size
      comes from css/n-office.css and is not restated). */
.n-intel-card {
  break-inside: avoid;
  padding: var(--n-space-3) 0;
  border-top: 0;
}
.n-intel-fig { margin-bottom: 0; }

/* 3. …and the two states that are NOT a record. A "thinking" bar and the
      sentence beside it are the middle of something happening — a sheet
      printed while the model was still writing said "Working it out on the
      on-site AI…" where the briefing should have been. The sentence stays
      (it is the honest reason the block is empty); the bar, which is a
      picture of time passing, goes. */
.n-intel-bar { display: none !important; }

/* 4. THE INSPECTOR'S ANSWER NEVER PRINTS, and it already does not: above
      1200 px the panel is `.n-oinspector` (hidden at the top of this file)
      and below it the same content is an n-sheet inside #nSheetRoot (hidden
      there too). Said again here for the one thing neither selector covers —
      the answer is the model's reading of the numbers, not the numbers, and
      nothing in it may reach paper by a route a later batch opens. */
[data-n="intel-answer"], [data-n="intel-how"], [data-n="intel-error"] { display: none !important; }

/* ── Reports › Period report: the CLASSIC office on paper ───────────────
   Everything above this line dresses the redesigned office (`.n-office`,
   behind the n_office flag). Mulino runs the classic one, and the period
   report is the one classic screen whose whole purpose is to leave the
   building: the owner picks a month, presses Print, and the browser's own
   "Save as PDF" is the PDF. So this block is the classic shell's version of
   the same three rules the office already follows — the chrome goes, colour
   becomes ink, and a section is never broken across two sheets.

   `body:has(.rep-period)` is the gate. js/27-reports-books.js puts that
   class on the report's group only while it is the tab on screen (repGo),
   so printing from any other Reports tab, or any other section, prints
   exactly what it printed before this feature existed. */
body:has(.rep-period) #side,
body:has(.rep-period) #top,
body:has(.rep-period) #tabbar,
body:has(.rep-period) #helpFab,
body:has(.rep-period) #toastRoot,
body:has(.rep-period) .rep-acts,
body:has(.rep-period) #main > *:not(.rep-period) { display: none !important; }

/* The shell is a grid whose columns are the rail and the page; with the rail
   gone the page is the sheet, edge to edge. #main scrolls on screen, and a
   scroll container prints its first screenful and stops — the report is a
   month long, so it has to flow. */
/* …and `!important`, because js/05-auth-license.js sets `#shell.style.display
   = "grid"` on the element itself when the till logs in, and an inline
   declaration outranks any stylesheet rule that is not important — the same
   lever the accent block at the top of this file had to reach for. Without
   it the rail's 232 px column stayed on the paper as an empty margin. */
body:has(.rep-period) #shell { display: block !important; }
body:has(.rep-period) #main {
  overflow: visible;
  padding: 0;
}

/* Colour becomes ink. The classic themes carry their own palette (Midnight
   would print a black card with white type), so the report states the two
   paper tokens for itself rather than trusting whichever theme the till
   happens to be set to. */
body:has(.rep-period) { background: var(--n-paper); }
.rep-period,
.rep-period * {
  color: var(--n-ink);
  background-color: transparent;
}
.rep-period .card {
  border: 0;
  border-radius: 0;
  padding: 0;
  box-shadow: none;
}
/* A card is a section, and a section starts its own sheet: Sales, Profit and
   loss, Refunds and voids, Tips and wages, What you bought. The first one
   (the headline figures) keeps the top of page one. */
.rep-period .rep-sec { break-inside: avoid; }
.rep-period .rep-sec + .rep-sec { break-before: page; }
/* …but a section that is mostly table may be longer than a sheet, and a
   table that "avoids" a break with nowhere to go overflows off the paper
   instead. Those flow, with the head repeated on each sheet — the same
   answer `.n-block:has(.n-tbl)` gives the office. */
.rep-period .rep-sec:has(table) { break-inside: auto; }
.rep-period thead { display: table-header-group; }
/* A heading stays with what it heads: "How it was paid" sat alone at the
   foot of a sheet with its table overleaf on the first A4 proof. */
.rep-period h3,
.rep-period h4 { break-after: avoid; }
.rep-period tr { break-inside: avoid; }
/* The phone stylesheet gives a wide table a minimum width so it can scroll
   sideways inside its card; paper has no sideways. */
.rep-period table { min-width: 0; }
