/* ==========================================================================
   Draco Staff Development — v8 Print Stylesheet
   Loaded platform-wide from partials/head.php, LAST in the stylesheet order so
   it outranks every screen rule at equal specificity.

   SCOPE — read before adding anything here.
   This file is reachable ONLY by pages that include partials/head.php. Every
   statutory / parity-locked artifact (the CDPH renewal packet overlay, the
   276A skills evaluation, the Palm Village skills check-off, the scanned-form
   overlay in form-print.php, the resident-care evaluation, the CDPH capture
   page, and every certificate generator) builds its own complete <!doctype>
   document and never includes head.php. Those pages therefore cannot see a
   single rule in this file, which is why the rules below are written plainly
   rather than defensively fenced. If a statutory surface is ever refactored to
   adopt the shared head, it must be given an opt-out FIRST.

   Everything here lives inside @media print. Nothing in this file may affect
   the screen.

   HIGH CONTRAST IS THE RULE. Pale tints do not survive a real office printer,
   and none of these documents may depend on colour: they are evidence. Status
   is carried by a word plus a symbol plus weight, so the meaning survives a
   greyscale photocopy of a fax of a printout.
   ========================================================================== */

/* --------------------------------------------------------------------------
   SCREEN: the print-only document header must never appear in the app. This is
   the file's only rule outside @media print, which is why head.php links it
   without a media attribute rather than media="print" — the alternative was
   putting this line in components.css, which is owned elsewhere.
   -------------------------------------------------------------------------- */
.print-doc-head { display: none; }

@media print {

  /* =======================================================================
     1. PAGE GEOMETRY
     The top margin is deliberately generous: it holds the running header that
     repeats on every sheet (see §3).
     ======================================================================= */
  @page {
    size: Letter portrait;
    margin: 0.68in 0.5in 0.6in;

    /* Chrome/Blink honours @page margin boxes and BOTH page counters —
       verified in Chrome 150 against a 3-page fixture: "Page 2 of 3" rendered
       correctly on every sheet. The facility / report / scope strings that
       join it here are emitted per document by partials/print-head.php,
       because a margin box can only contain a literal string and this file
       has no access to the data. Firefox and Safari ignore margin boxes
       entirely and simply print without the running header and page number;
       the first-page document header in §3 still carries the full provenance
       there, so nothing is ever printed unattributed. */
    @bottom-right {
      content: "Page " counter(page) " of " counter(pages);
      font-family: "Inter", Helvetica, Arial, sans-serif;
      font-size: 8pt;
      color: #000;
    }
  }

  /* =======================================================================
     2. FORCE THE LIGHT THEME
     A dark-mode page sent to a printer is illegible and empties a toner
     cartridge. The theme is applied as [data-theme="dark"] on <html> (set
     before paint by the inline script in head.php) with per-palette overrides
     at :root[data-palette="x"][data-theme="dark"] — specificity 0,3,0 — and a
     runtime custom-accent <style> block that lands AFTER this file in source
     order. !important on the custom properties is the only thing that beats
     all three reliably, so it is used deliberately and only here.
     ======================================================================= */
  :root,
  :root[data-theme],
  :root[data-palette][data-theme] {
    color-scheme: light !important;

    --bg:            #ffffff !important;
    --surface:       #ffffff !important;
    --surface-2:     #ffffff !important;
    --surface-3:     #ffffff !important;
    --form-surface:  #ffffff !important;
    --border:        #cbd5e1 !important;   /* mid grey: elegant rules, not boxed-in black */
    --border-strong: #94a3b8 !important;

    --text:          #000000 !important;
    --text-2:        #000000 !important;
    --text-3:        #1a1a1a !important;   /* captions: near-black, still prints */
    --text-invert:   #ffffff !important;
    --link:          #000000 !important;
    --link-hover:    #000000 !important;

    /* Branded header band stays navy with white text on paper (pinned to the
       LIGHT-theme values so a page printed from dark mode matches, not the
       darker dark-theme navy). The footer chrome is hidden in §4, so its tokens
       only matter to the rare report body that reuses them. */
    --header-bg:     #1e3a5f !important;
    --header-text:   #ffffff !important;
    --footer-bg:     #1e3a5f !important;
    --footer-text:   #ffffff !important;
    --app-footer-bg: #ffffff !important;

    /* Status families keep a legible dark-on-light tint on paper (pinned to the
       LIGHT-theme token values so dark-mode prints match). The former collapse
       to #000/#fff is exactly what erased the colour the owner wants printed;
       meaning is still reinforced in §7 by a symbol + word + weight. None of
       these fills is a forbidden hue — warning-bg is slate, not light yellow. */
    --success: #15803d !important; --success-bg: #dcfce7 !important; --success-border: #86efac !important;
    --warning: #b45309 !important; --warning-bg: #f1f5f9 !important; --warning-border: #fcd34d !important;
    --danger:  #b91c1c !important; --danger-bg:  #fee2e2 !important; --danger-border:  #fca5a5 !important;
    --info:    #0e7490 !important; --info-bg:    #cffafe !important; --info-border:    #67e8f9 !important;

    --ring: transparent !important;
    --overlay: transparent !important;
    --watermark-opacity: 0 !important;

    --shadow-sm: none !important;
    --shadow:    none !important;
    --shadow-md: none !important;
    --shadow-lg: none !important;
    --shadow-xl: none !important;
  }

  html, body {
    background: #fff !important;
    color: #000 !important;
    font-size: 10pt;
    line-height: 1.35;
  }

  /* COLOUR OPT-IN. Chrome's print dialog ships with "Background graphics" OFF,
     which normally drops every fill. Declaring print-color-adjust:exact on the
     document containers forces the browser to print the fills anyway — status
     tints, branded header bands and zebra rows all reach the paper even with
     that checkbox off. This is what lets the sheet below carry colour instead
     of flattening everything to black-on-white (its former strategy). */
  html, body, .app-content, .page-main {
    -webkit-print-color-adjust: exact !important;
    print-color-adjust: exact !important;
  }

  /* =======================================================================
     3. THE OFFICIAL DOCUMENT HEADER
     Markup comes from partials/print-head.php. Hidden on screen; this is the
     only place it becomes visible. A printed compliance document that does not
     say what it covers and when it was produced is not evidence, so this block
     is not optional and not suppressible by a page.
     ======================================================================= */
  .print-doc-head { display: block !important; }

  /* DELIBERATELY COMPACT — roughly half an inch, not the full inch a display
     header would want. Several report bodies render their own branded title
     block (facility logo + report name) immediately below this one, and those
     blocks are shared verbatim with the emailed mPDF twins, so they cannot be
     suppressed from here without changing an artifact this task must not
     touch. Two stacked headers are tolerable; two stacked TALL headers pushed
     a row onto a second sheet on the CNA expiry report during verification.
     The margin boxes repeat these same facts on every page anyway, so this
     block only has to establish provenance once, tightly. */
  .print-doc-head {
    border-bottom: 1.5pt solid #000;
    padding-bottom: 4pt;
    margin: 0 0 7pt;
    break-inside: avoid;
    break-after: avoid;   /* never orphan the header from its first rows */
  }

  .print-doc-head__top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 14pt;
  }

  /* THE REPORT TITLE APPEARS EXACTLY ONCE (owner, 2026-07-21). partials/print-head.php now emits the
     single tenant-branded masthead, so each report's OWN letterhead — every one of which is tagged
     .rpt-letterhead — is suppressed for print. Without this the facility name and report title printed
     TWICE (the "redundant title section"). Scoped to @media print only: the mPDF/PDF path never loads
     this sheet and still uses .rpt-letterhead as its header. */
  .rpt-letterhead { display: none !important; }

  /* Tenant lockup: the facility's logo and name are the brand on a printed report, so both are larger
     than the old Draco wordmark they replaced. */
  .print-doc-head__brand { display: flex; align-items: center; gap: 7pt; flex: none; }
  .print-doc-head__mark  { width: 26pt; height: 26pt; object-fit: contain; }
  .print-doc-head__word  {
    font-family: "Inter", Helvetica, Arial, sans-serif;
    font-weight: 800;
    font-size: 11pt;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    color: #000;
    line-height: 1.15;
  }
  .print-doc-head__word small {
    display: block;
    font-size: 5.5pt;
    font-weight: 600;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    color: #1a1a1a;
  }

  .print-doc-head__facility {
    font-size: 10pt;
    font-weight: 700;
    color: #000;
    margin: 0;
    text-align: right;
  }

  .print-doc-head__title {
    font-size: 11.5pt;
    font-weight: 700;
    color: #000;
    margin: 3pt 0 0;
    line-height: 1.15;
  }

  /* Scope + generation stamp on one tight line. */
  .print-doc-head__meta {
    display: flex;
    flex-wrap: wrap;
    gap: 0 16pt;
    margin: 2pt 0 0;
    font-size: 7.5pt;
    color: #000;
  }
  .print-doc-head__meta div { display: flex; gap: 3pt; }
  .print-doc-head__meta dt {
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    font-size: 7pt;
    margin: 0;
  }
  .print-doc-head__meta dd { margin: 0; font-weight: 500; }

  /* =======================================================================
     4. HIDE EVERY PIECE OF APPLICATION CHROME
     Nothing interactive belongs on paper. This is the fix for the printout
     that arrived carrying the mobile bottom navigation.
     ======================================================================= */
  .topbar,
  .sidebar,
  .bottomnav,
  .scrim,
  .watermark,
  .skip-link,
  .app-footer,
  .site-header,
  .site-footer,
  .breadcrumbs,
  .rpt-crumbs,            /* reports use their own back-link nav, not .breadcrumbs */
  .page-head,
  /* The report filter bar. .cmx-controls is the <form> wrapper — hiding only the inner
     .cmx-toolbar leaves an empty form still holding its margin above the table. */
  .cmx-controls,
  .rpt-toolbar,
  .toast-region,
  .toast,
  .modal,
  .modal__backdrop,
  .dsd-dialog,
  .dsd-dialog__backdrop,
  .menu,
  .menu__panel,
  .dsd-tablebar,          /* pagination + page-size control */
  .dsd-pagebtn,
  .pager,
  .pager__btn,
  /* The "Per page" selector under a paginated list (components.css:648).
     Deliberately NOT the whole .staff-foot: its sibling .staff-foot__count
     prints "Showing 1-20 of 154", and a printed roster that has silently
     dropped 134 people needs to SAY so. Hiding the count would turn page 1
     of a roster into something indistinguishable from the whole roster.
     Absorbed from assets/css/training-calendar.css, which hid all of
     .staff-foot and therefore lost that line on the calendar's event list. */
  .staff-foot__view,
  .btn,
  .icon-btn,
  .tr-bulkbar,
  .tr-toolbar,
  .tr-search,
  .onboarding,
  [data-drawer-toggle],
  [data-kebab],
  [data-gear],
  [role="dialog"],
  [role="alertdialog"],
  [role="tooltip"],
  [aria-hidden="true"].watermark {
    display: none !important;
  }

  /* Filter and search controls. Deliberately NOT a blanket `select` /
     `input` hide: form-entry.php, the evaluation forms and the skills forms
     all print a SUBMITTED record whose answers live in exactly those
     controls, and hiding them would silently blank the data on a document
     someone signs. Only search boxes and the known toolbar containers go. */
  input[type="search"],
  .input--search,
  .filters,
  .filter-bar,
  .toolbar,
  .tr-toolbar select,
  .toolbar select,
  .filters select,
  .filter-bar select {
    display: none !important;
  }

  /* Deliberate escape hatch: a page can force something back onto paper.
     Applied to the element only — NOT its descendants. `.print-keep *` would
     also reveal genuinely closed things inside it (a collapsed menu panel, an
     inactive tab), which is how an "escape hatch" turns into a leak. */
  .print-keep { display: revert !important; }
  .no-print { display: none !important; }

  /* =======================================================================
     5. USE THE WHOLE SHEET
     ROOT CAUSE: the printed page box is roughly 740 CSS px wide, which is
     BELOW the 767px mobile breakpoint in layout.css. Chrome therefore
     evaluates the mobile media query when printing and lays the entire
     application out as a phone — narrow column, bottom tab bar and all. That
     is why the owner's printout looked like a phone screenshot. Unwinding
     every mobile/desktop layout artefact here is what reclaims the sheet.
     ======================================================================= */
  .app-shell,
  .page {
    display: block !important;
    min-height: 0 !important;
  }

  .app-main {
    margin-left: 0 !important;    /* desktop sidebar gutter */
    padding-bottom: 0 !important; /* mobile bottom-nav clearance */
    min-height: 0 !important;
    display: block !important;
  }

  .app-content,
  .page-main {
    padding: 0 !important;
    margin: 0 !important;
    width: auto !important;
    max-width: none !important;
    display: block !important;
  }

  .container {
    width: auto !important;
    max-width: none !important;
    margin-inline: 0 !important;
    padding-inline: 0 !important;
  }

  /* Cards and panels become plain paper: no shadow, no rounding, no tint. */
  .panel,
  .card,
  .table-card,
  .tablewrap,
  .dsd-tablewrap,
  .table-wrap {
    background: #fff !important;
    box-shadow: none !important;
    border: 0 !important;
    border-radius: 0 !important;
    margin: 0 0 10pt !important;
    padding: 0 !important;
    overflow: visible !important;  /* a scroll container would clip on paper */
    max-height: none !important;
  }

  /* Anything that scrolled on screen must expand on paper or it prints
     truncated — a silent, dangerous failure in a compliance record. */
  [style*="overflow"],
  .overflow-auto,
  .scroll {
    overflow: visible !important;
    max-height: none !important;
  }

  /* =======================================================================
     6. TABLES
     Header repeats on every sheet; rows never split across a page break.
     ======================================================================= */
  table { width: 100% !important; border-collapse: collapse !important; }

  thead { display: table-header-group !important; }  /* repeat on every page */
  tfoot { display: table-footer-group !important; }
  tr    { break-inside: avoid !important; page-break-inside: avoid !important; }
  thead tr { break-after: avoid !important; }

  .dsd-table,
  table { font-size: 8.5pt !important; }

  /* Table headers: keep the report's own band colour (a branded navy/teal thead
     with white text, or the default pale surface tint) — the global colour
     opt-in above makes it reach the paper even with "Background graphics" off,
     so the former black-on-white flatten is gone. Only the print typography and
     rules are normalised here. Report bodies that paint their own dark header
     band (e.g. .oig-roster thead th #1e293b/#fff) now print that band correctly
     instead of losing it to white-on-white. */
  .dsd-table thead th,
  thead th,
  .app-content th,
  .app-content [class] th,
  .app-content [class] thead th {
    font-weight: 700 !important;
    font-size: 7.5pt !important;
    text-transform: uppercase;
    letter-spacing: 0.05em;
    text-align: left;
    padding: 4pt 5pt !important;
    border-bottom: 1.5pt solid #94a3b8 !important;
    border-top: 1.5pt solid #94a3b8 !important;
  }

  .dsd-table tbody td,
  tbody td {
    padding: 3.5pt 5pt !important;
    color: #000 !important;
    border-bottom: 0.5pt solid #cbd5e1 !important;   /* light rule: structure
                                                        without the heavy black grid */
    vertical-align: top;
  }

  /* Zebra banding. This is the one fill worth forcing: it carries no meaning,
     but it is what lets a reader track a name across eight columns. #e8e8e8 is
     dark enough to reproduce on a cheap laser printer; the row rule above
     guarantees legibility even if a printer drops the fill entirely. */
  .dsd-table tbody tr:nth-child(even),
  tbody tr:nth-child(even) {
    background: #e8e8e8 !important;
    -webkit-print-color-adjust: exact;
    print-color-adjust: exact;
  }
  .dsd-table tbody tr:hover,
  tbody tr:hover { background: transparent !important; }
  .dsd-table tbody tr:nth-child(even):hover,
  tbody tr:nth-child(even):hover { background: #e8e8e8 !important; }

  /* NEVER band a table nested inside a cell. The selector above is deliberately bare (`tbody tr`) so
     it reaches every report, but a `tr` inside a td is a descendant of the OUTER tbody too — so a
     detail list rendered as a table got striped line by line. On the Title 22 report that banded each
     completed training inside the TRAININGS COMPLETED cell, so the stripes counted courses instead of
     people and the roster read as noise (owner, 2026-07-21).
     Zebra exists to let a reader track ONE PERSON across the columns; only the row that IS a person
     may carry it. A nested table is a layout/detail list, never the roster. */
  td table tbody tr:nth-child(even),
  td table tbody tr:nth-child(even):hover,
  td table tbody tr:hover { background: transparent !important; }

  .dsd-table tbody tr:last-child td { border-bottom: 1.5pt solid #94a3b8 !important; }

  /* =======================================================================
     7. STATUS THAT SURVIVES GREYSCALE
     On screen these are pale tinted pills (#a9edc0 / #dbe2ec / #ffc9c7 /
     #cfe0fd). Printed, those tints are nearly identical in luminance —
     "Met" and "Overdue" would become the same grey. On paper each becomes a
     white chip with a black rule, and the meaning is re-encoded three ways:
     a symbol, the existing word, and weight.
     ======================================================================= */
  /* The `.app-content [class] …` variants are load-bearing, not belt-and-braces.
     Report pages re-tint their own status chips at two-class specificity WITH
     !important — dashboard/oig-report.php ships
       .oig-roster .badge--success { background:#dcfce7 !important; color:#166534 !important; }
     which is 0,2,0 and beats a bare `.badge` (0,1,0) however important it is.
     Verification caught this: the OIG roster was still printing dark-green text
     on a pale-green fill after the first pass, i.e. status riding on hue and a
     tint that a laser printer barely reproduces. 0,3,0 takes it. */
  .badge,
  .cnar-chip,
  .pill,
  .chip,
  .tag,
  .app-content [class] .badge,
  .app-content [class] .cnar-chip,
  .app-content [class] .pill,
  .app-content [class] .chip,
  .app-content [class] .tag {
    /* Keep the tinted screen fill + coloured text (status now survives on
       paper); only normalise the print size and drop the screen shadow. The
       colour-killing white-fill/black-text/black-border flatten is gone. */
    border-radius: 2pt !important;
    font-weight: 700 !important;
    font-size: 7.5pt !important;
    padding: 0.5pt 3pt !important;
    box-shadow: none !important;
    white-space: nowrap;
  }

  .badge--success::before,
  .cnar-chip--success::before,
  .cnar-chip--ok::before      { content: "\2713\00a0"; }   /* ✓ */
  .badge--warning::before,
  .cnar-chip--soon::before    { content: "\0021\00a0"; }   /* ! */
  .badge--danger::before,
  .cnar-chip--danger::before,
  .cnar-chip--missing::before { content: "\2715\00a0"; }   /* ✕ */
  .badge--info::before,
  .cnar-chip--info::before    { content: "\2022\00a0"; }   /* • */

  /* Danger reads heaviest at a glance — the one status a surveyor must not
     miss gets a doubled rule rather than a colour. */
  .badge--danger,
  .cnar-chip--danger,
  .cnar-chip--missing {
    border-width: 1.5pt !important;
  }

  /* ---- Status pills that carry NO class at all -------------------------
     The report bodies in app/reports.php are shared with the mPDF renderers
     that produce the emailed and downloaded twins, and mPDF needs its colour
     inline — so those pills are written as bare
       <span style="…border-radius:999px;color:#b45309;background:#fef3e2">
     with no class to hook. These are dark-on-light and legible, so with the
     global colour opt-in above they now print in their own colour — the flatten
     that used to collapse "Needs more CE" and "Current" to the same grey is
     gone. Only the print weight is kept. (The two light-yellow/cream fills that
     these can carry are remapped just below, not here.) */
  [style*="border-radius:999px"] {
    font-weight: 700 !important;
  }

  /* Pale inline FILLS, enumerated from what the renderers actually emit.
     Enumerated rather than matched by pattern on purpose: a blanket
     [style*="background:#"] rule would also flatten the dark navy section
     bands, and any of those carrying white text would vanish.

     ===================================================================
     COUPLED TO LITERAL HEX IN THE RENDERERS. READ BEFORE EDITING EITHER.
     ===================================================================
     Each selector below matches a colour SPELLED OUT in a PHP renderer.
     There is no shared constant. Nothing links the two halves, nothing
     tests them, and nothing errors when they drift — the selector simply
     stops matching and the fill silently comes back on paper. That is a
     banned hue reappearing on a printed compliance document with no
     symptom anywhere on screen.

     The spread is wide and NOT a tidy one-to-one table. Measured with
     `grep -rn "<hex>" --include='*.php' .`, the fifteen values below are
     emitted from between 1 and 20 PHP files each (#f8fafc: 20 files,
     #fffbeb: 8, #fdf4e7 and #ecf7f2: 1). A line-number list here would be
     wrong within a week, so use the grep rather than trusting a table.

     RULE, both directions:
       * Changing a hex in a renderer? Run `grep -rn "<hex>" .` FIRST and
         update every match in the SAME commit.
       * Changing a selector here? Confirm the renderer still emits that
         exact string, byte for byte, including whether there is a space
         after the colon. `background: #fffbeb` and `background:#fffbeb`
         are two different attribute matches; this list carries the
         no-space form because that is what these renderers emit.

     Why this is not a class instead: these fills are consumed by mPDF,
     which needs colour inline, and the emitting files are governed by the
     output-parity standard (the printed page and the emailed PDF must
     stay identical). Converting to a class means editing those renderers,
     which is a parity-reviewed change, not a styling one. Until that
     happens the coupling is real and this comment is the only thing
     holding it together. Two more copies of this same mechanism exist:
     dashboard/training-record-report.php:305-312 and
     dashboard/cna-expiry-report.php:237.

     WHAT CHANGED (colour wave): the pale blue / green / slate members of this
     list (#fef3e2 amber, #fee2e2, #fbeaea, #d1fae5, #dcfce7, #e6f5ee, #ecf7f2,
     #e8effb, #eef3fc, #eef2f8, #eef1f6, #f8fafc, #f1f5f9) are all legible
     dark-on-light and now print in their own colour — they are no longer
     flattened. Only the two LIGHT-YELLOW / cream fills the owner banned on
     paper are intercepted, and remapped to a legible pale amber (not a flatten
     to white) so the pill still reads as a tinted status. Dark text on these
     comes from the inline style and is preserved.
     NOTE for the per-report wave: the renderer-side flatten LISTS still exist
     (dashboard/training-record-report.php:305-312 and cna-expiry-report.php:237
     hold their own copies of this hex-coupled mechanism); reconcile them there,
     not here. */
  [style*="background:#fffbeb"],
  [style*="background:#fdf4e7"] {
    /* #228: the remap target was #fef3e2 — still a banned hue (36°, 93% sat). It intercepted a
       light-yellow the owner banned on paper and replaced it with a slightly darker AMBER, which
       satisfies neither the print legibility rule nor the hue rule. It is now the same neutral
       slate the --warning-bg token was moved to in 8.193.0 (#f1f5f9), so the pill still reads as a
       tinted status rather than flattening to white, and the dark text carried by the inline style
       gains contrast rather than losing it. */
    background: #f1f5f9 !important;   /* light-yellow → neutral slate tint */
  }

  /* Dark inline navy/slate bands now KEEP their colour and white text — the
     global colour opt-in prints the fill even with "Background graphics" off,
     so the former flatten-to-white (which erased a branded section band) is
     gone. */

  /* .ap-step__ic renewal-tracker step markers (#1e56c9 / #0e7346 on white text)
     also keep their colour now, for the same reason. */

  /* FullCalendar event chips on the Training Calendar. The category colour is
     set by FullCalendar as an inline backgroundColor with color:#fff on top,
     so the blanket black-text rule below turned every chip into BLACK TEXT ON
     A SATURATED FILL. Measured on the July 2026 calendar, the four category
     colours in use were #b91c1c, #2563eb, #0e7490 and #4f46e5, giving contrast
     ratios against black text of 3.25, 4.06, 3.92 and 3.34 to 1 — every one of
     them under the 4.5:1 floor, on 11px text. On a greyscale office laser they
     print as near-solid dark blocks with the title barely legible, which is the
     toner complaint and the readability complaint at the same time.

     Nothing is lost by flattening. The chip still prints its time and title,
     and the colour it is losing encoded the CATEGORY, which the event list on
     the following sheet already prints as a word in its own column
     (dashboard/modules/training-calendar.php:181). The colour key itself is
     hidden on paper for the same reason (see assets/css/training-calendar.css).
     Inline backgroundColor means !important is the only lever. */
  .fc-event,
  .fc-daygrid-event,
  .fc-event-main,
  .fc-h-event,
  .fc-v-event {
    background: #fff !important;
    background-color: #fff !important;
    color: #000 !important;
    border: 0.75pt solid #000 !important;
  }
  .fc-event *, .fc-daygrid-event * { color: #000 !important; }

  /* The "today" cell prints a yellow wash. The colour is not in this repo's CSS at all — it is
     --fc-today-bg-color: rgba(255,220,40,.15), i.e. literally yellow, set inside the minified
     vendor bundle at assets/vendor/fullcalendar.min.js. A hex sweep over .css and .php cannot see
     it: it is rgba(), and it is in a .js file. Measured on a rasterised print as #EBE6CB.
     Today is still legible on paper from the date number and the surrounding grid. */
  .fc-day-today,
  .fc-daygrid-day.fc-day-today,
  .fc-timegrid-col.fc-day-today {
    background: #fff !important;
    background-color: #fff !important;
  }

  /* FullCalendar's own toolbar is navigation, not content. Its buttons carry
     no .btn class, so section 4's chrome sweep does not reach them and the
     prev / next / today controls were printing as live-looking buttons.
     Only the BUTTONS are hidden, deliberately not their .fc-toolbar-chunk
     wrapper: the sibling chunk holds .fc-toolbar-title ("July 2026"), which is
     the one thing on the sheet that says WHICH MONTH was printed. Verified in
     the DOM that the chunks are [buttons] [title] [empty], so hiding the first
     chunk would have been safe today — but that is a FullCalendar layout
     detail, not a contract, and it is not worth betting the month heading on. */
  .fc-button, .fc-button-group { display: none !important; }

  /* COLOUR WAVE — the blanket `.app-content *, .page-main * { color:#000 }` rule
     that used to live here has been REMOVED. It was the dominant monochrome
     lever: it turned every status count green/red into flat black and, worse,
     turned white-on-navy band text black-on-navy. Text colour now comes from
     the (light-theme, coloured) tokens, so status counts, branded headings and
     white-on-dark bands all print in colour. The colour opt-in at the top of
     this file is what carries the fills through with "Background graphics" off,
     and legibility is dark-on-light by construction (see the §2 status tokens).
     The greyscale-survivability glyphs (✓ ! ✕ • injected in §7) are kept as a
     belt-and-braces cue for photocopies. */

  /* Borders soften to a mid grey rather than pure black — pale #e2e8f0 hairlines
     still print too faint on a laser, but a full black grid reads heavy. */
  .app-content [style*="border"],
  .app-content table,
  .app-content td,
  .app-content th {
    border-color: #cbd5e1 !important;
  }

  /* =======================================================================
     8. TYPOGRAPHY, LINKS, IMAGES, BREAKS
     ======================================================================= */
  a, a:link, a:visited {
    color: #000 !important;
    text-decoration: none !important;   /* platform rule: links are not underlined */
  }
  /* Deliberately NOT expanding href via a[href]::after — these are internal
     application URLs behind a login. Printing them adds noise and leaks paths
     into a document that may leave the building. */

  h1, h2, h3, h4, h5, h6 {
    color: #000 !important;
    break-after: avoid !important;
    page-break-after: avoid !important;
    break-inside: avoid !important;
  }
  h1 { font-size: 14pt !important; }
  h2 { font-size: 11.5pt !important; }
  h3 { font-size: 10pt !important; }

  p, li, dd, dt, td, th { orphans: 3; widows: 3; }

  img, svg { max-width: 100% !important; }

  /* Keep-together applies ONLY to genuinely small units.
     `.panel` / `section` / `.card` were in this list during verification and it
     backfired badly: the OIG roster is a 275-row table inside a .panel, so
     "never break this box" could not be satisfied, and Chrome resolved it by
     pushing the entire panel to the next sheet — leaving page 1 about 60%
     empty and still breaking the table anyway. A container that can outgrow a
     page must be allowed to break; §6 already guarantees the parts that
     actually matter (the header repeats, no row splits). */
  .stat-cards, .stat-card, .kpi, .kpis, figure { break-inside: avoid !important; }

  /* =======================================================================
     STAT CARDS ON PAPER — one compact band, not two rows of tiles
     -----------------------------------------------------------------------
     Screen sizing carried straight onto paper and cost roughly a fifth of
     page 1: .stat-grid is repeat(auto-fit, minmax(220px, 1fr)), and a letter
     page's 7.5in text column only fits THREE 220px tracks — so a five-card
     report wrapped 3 + 2, each tile locked to .dash-card > .stat's 98px
     height. Two rows of tall tiles before the roster even starts.

     The figures are worth keeping — they are the summary a reader checks
     before reading 34 pages of rows — so this compresses them rather than
     hiding them: every card on ONE row, auto height, tight padding.
     ======================================================================= */
  .stat-grid {
    /* Explicitly as many columns as there are cards, and let them go narrow:
       minmax(0, 1fr) removes the 220px floor that forced the wrap. */
    grid-auto-flow: column !important;
    grid-auto-columns: minmax(0, 1fr) !important;
    grid-template-columns: none !important;
    gap: 6pt !important;
    margin-bottom: 9pt !important;
    break-inside: avoid !important;
  }
  /* Height is set on .dash-card > .stat (98px) and .dash-card--compact (62px);
     both must yield or the row stays tall no matter how little padding it has. */
  .stat,
  .dash-card > .stat,
  .dash-card--compact > .stat {
    height: auto !important;
    min-height: 0 !important;
    padding: 5pt 7pt !important;
    gap: 5pt !important;
    box-shadow: none !important;
    border: 0.75pt solid #999 !important;
    border-radius: 3pt !important;
    break-inside: avoid !important;
  }
  /* The icon is pure decoration here — a coloured glyph that survives print
     badly and eats a third of a narrow card's width. The number and its label
     carry the whole meaning, and dropping the tile lets five cards sit
     comfortably across the page instead of being crushed. */
  .stat__icon { display: none !important; }
  .stat__n { font-size: 15pt !important; line-height: 1.05 !important; }
  .stat__l {
    font-size: 7.5pt !important; margin-top: 1pt !important; color: #000 !important;
    /* .dash-card .stat__l ellipsises on one line, which on a narrow printed card
       truncated "Needs Action" to "Needs…". On paper there is no tooltip and no
       chooser to recover the full text from, so let it wrap instead. */
    white-space: normal !important; overflow: visible !important; text-overflow: clip !important;
  }
  /* The customise gear beside the row is interactive furniture. */
  .dash-stats__cust { display: none !important; }
  .dash-stats { display: block !important; }

  hr { border: 0 !important; border-top: 1pt solid #000 !important; }

  /* Anything still relying on a decorative gradient or tinted background
     flattens to paper white. */
  [class*="gradient"], [class*="--accent"], [class*="tint"] {
    background-image: none !important;
  }
}
