/* ==========================================================================
   GearPilot — design system

   Colours, type and the rules below come from the production brand pack
   (design/brand/pack, guidelines PDF in 06_Brand_Guidelines). The pack is the
   source of truth: the marks are used as supplied and never redrawn.

   The pack is deliberately monochromatic — navy, cyan, two blues, silver. That
   buys coherence and costs the one thing the old amber system had for free:
   two unrelated hues to mean two different things. So the work is done by
   LIGHTNESS instead. Signal Blue is the accent, the "this one, out of all of
   them" colour: the found item, the next case, the focused control. Navy is
   structure — links' resting state is not navy, because navy is body text and
   a link that matches the paragraph around it is not a link.

   Accent is still rationed. If everything is highlighted, nothing is found.

   Every value below is either a pack colour or a tint of one, and every
   foreground/background pair a person has to read was checked to 4.5:1. Where
   a pack colour could not carry text at that ratio — Forward Cyan is 1.7:1 on
   silver — it is used as a surface with navy on top, never as text.

   Status bands are the exception and stay outside the brand palette, for the
   reason the old system gave: a band covering a third of the wall spends a
   brand colour on something that is not the brand.
   ========================================================================== */

:root {
  /* Pack palette — design/brand/pack/07_Source/gearpilot-design-tokens.json */
  --navy:      #071525;   /* Pilot Navy   */
  --navy-2:    #0B223A;
  --cyan:      #00C8F5;   /* Forward Cyan */
  --blue:      #087EFF;   /* Action Blue  */
  --blue-deep: #135CE6;   /* Signal Blue  */
  --silver:    #E8EDF2;
  --steel:     #9AA6B2;
  --graphite:  #202A35;

  /* Tints of the above, for surfaces and washes the pack does not enumerate. */
  --silver-sunken: #D9E1EA;
  --silver-line:   #CBD6E0;
  --steel-deep:    #5A6875;   /* dim text: Steel is 2.1:1 on silver, this is 4.9 */
  --blue-wash:     #E6EEFE;
  --navy-wash:     #DCE3EB;

  --red:      #A6402E;
  --red-wash: #F8E9E6;

  /* Status frames only (see Display/StatusBands.cs). Deliberately NOT brand
     colours, and deliberately five different HUES: nobody distinguishes five
     shades of the same blue at thumbnail size. */
  --teal:  #0E7C86;
  --ochre: #B4761F;
  --plum:  #6E4A7E;

  /* Semantic — remapped wholesale for dark mode below */
  --bg:            var(--silver);
  --bg-raised:     #FFFFFF;
  --bg-sunken:     var(--silver-sunken);
  --text:          var(--navy);
  --text-dim:      var(--steel-deep);
  --line:          var(--silver-line);
  --line-strong:   #B3C1CE;
  --accent:        var(--blue-deep);
  --accent-bright: var(--blue);
  --accent-wash:   var(--blue-wash);
  --on-accent:     #FFFFFF;      /* text ON an accent fill: 5.7:1 */
  --brand:         var(--navy);
  --brand-wash:    var(--navy-wash);
  --danger:        var(--red);
  --danger-wash:   var(--red-wash);
  --shelf:         var(--steel-deep);
  --going:         var(--plum);
  --away:          var(--teal);
  --blocked:       var(--ochre);

  --shadow: 0 1px 2px rgb(7 21 37 / 0.06), 0 4px 16px rgb(7 21 37 / 0.06);
  --shadow-lift: 0 2px 4px rgb(7 21 37 / 0.09), 0 12px 32px rgb(7 21 37 / 0.12);

  --radius: 10px;
  --radius-lg: 16px;
  --tap: 48px;            /* one-handed, standing up, possibly gloved */

  /* Avenir Next per the guidelines, then the platform grotesques, then the
     pack's own fallback. Nothing is fetched: a webfont on the critical path is
     a blank header on a bad connection in a loading bay. */
  --sans: "Avenir Next", Avenir, ui-sans-serif, -apple-system, "Segoe UI", "Helvetica Neue", Arial, sans-serif;
  --mono: ui-monospace, "SF Mono", "Cascadia Mono", "Roboto Mono", Menlo, monospace;
}

/* Dark is a first-class case, not an afterthought: the app gets used in a
   spare room at night. Surfaces lift towards navy rather than towards grey,
   because navy is the pack's own ground rather than a darkened white.

   Driven by [data-theme], not a media query: a tiny pre-paint script resolves
   the OS preference and any saved override into data-theme="light|dark" on
   <html>, and everything themed hangs off that one attribute. That is what
   lets the nav toggle actually win an argument with the OS. */
:root[data-theme="dark"] {
    /* Dark is not a dimmed light theme — it is the brand's own ground. Pilot
       Navy IS the pack's background colour, so this is the truer of the two. */
    --bg:            var(--navy);
    --bg-raised:     var(--navy-2);
    --bg-sunken:     #05080C;
    --text:          var(--silver);
    --text-dim:      var(--steel);
    --line:          #1B3350;
    --line-strong:   #2C4A6B;

    /* Cyan finally gets to be the accent. It is unreadable as text on silver
       (1.7:1) but sings on navy at 9.3:1, and carries navy text on top at the
       same ratio — so unlike the light theme, an accent fill here keeps dark
       text rather than white. */
    --accent:        var(--cyan);
    --accent-bright: #5FDDFA;
    --accent-wash:   #06263A;
    --on-accent:     var(--navy);
    --brand:         var(--blue);
    --brand-wash:    #0A2036;
    --danger:        #E88C7B;
    --danger-wash:   #2E1512;

    /* Lifted clear of the near-black ground — the light values vanish on it,
       and a frame nobody can see is not a frame. */
    --shelf:         var(--steel);
    --going:         #B693C8;
    --away:          #3FB6C2;
    --blocked:       #E0A94E;

    --shadow: 0 1px 2px rgb(0 0 0 / 0.5), 0 4px 16px rgb(0 0 0 / 0.4);
    --shadow-lift: 0 2px 4px rgb(0 0 0 / 0.6), 0 12px 32px rgb(0 0 0 / 0.5);
}

*, *::before, *::after { box-sizing: border-box; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  color: var(--text);
  background: var(--bg);
  /* The mark, scattered and very faint, the way the old tag texture was —
     built by tools/pattern.py from the pack's single-colour masters. It should
     read as paper texture, not wallpaper: the sign-in lockup sits directly on
     it, and the guidelines are clear about not putting the logo on a busy
     area. That is what the 5% and the 520px tile are for — at this weight it
     is a surface, not a competing image. */
  background-image: url("brand/pattern-light.svg");
  background-size: 520px;
  -webkit-font-smoothing: antialiased;

  /* Sticky footer. The page is a column at least as tall as the viewport and
     the footer takes up the slack, so a short screen — sign-in, an empty
     search, a one-item pick list — puts the credit line on the bottom edge
     rather than floating it halfway up with dead space beneath.
     100vh first as a fallback; 100dvh then wins where supported, which is what
     keeps it honest on a phone with a retracting address bar. */
  min-height: 100vh;
  min-height: 100dvh;
  display: flex;
  flex-direction: column;
}

:root[data-theme="dark"] body { background-image: url("brand/pattern-dark.svg"); }

h1, h2, h3 { line-height: 1.2; letter-spacing: -0.02em; margin: 0 0 0.5rem; }
h1 { font-size: 1.6rem; font-weight: 680; }
h2 { font-size: 1.15rem; font-weight: 640; }
h3 { font-size: 1rem; font-weight: 640; }
p  { margin: 0 0 1rem; }

a { color: var(--accent); text-underline-offset: 3px; }
a:hover { color: var(--accent-bright); }

/* --- The reference ------------------------------------------------------
   Four digits and a check digit, read off handwriting under a rail and typed
   back in. Tabular, spaced, and unmissable — it is the single most important
   string in the product. */
.ref {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-weight: 600;
  letter-spacing: 0.08em;
  color: var(--text);
}

.ref-hero {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: clamp(2.4rem, 12vw, 3.6rem);
  font-weight: 700;
  letter-spacing: 0.1em;
  color: var(--accent);       /* the found item */
  line-height: 1;
}

/* --- Theme-paired assets -------------------------------------------------
   The brand ships separate light and dark SVGs because recolouring the body is
   forbidden. Both render; the attribute picks one. */
/* `!important` on all three, which is the whole point of them. The rule used
   to be a comment saying "nothing may set display on these elements" — and it
   was broken within the hour by a rule sizing the header logo, at (0,3,1)
   against the utility's (0,1,0). The failure is quiet and easy to miss in
   light mode: the dark logo draws underneath the light one with its white
   half invisible on white, so all you see is a stray cyan "Pilot" below the
   header. A convention that is one careless selector from silently breaking
   should be enforced by the cascade, not by a comment nobody reads twice. */
.theme-dark-only { display: none !important; }
/* `revert`, not `block`: forcing block made the visible variant lay out
   differently from its light-mode twin, so a lockup centred in one theme sat
   left in the other. This hands display back to the browser default, keeping
   both variants identical in everything but artwork. */
:root[data-theme="dark"] .theme-dark-only { display: revert !important; }
:root[data-theme="dark"] .theme-light-only { display: none !important; }

/* Layout still belongs on a wrapper, not on these elements: `!important` stops
   the wrong artwork appearing, it does not make sizing them a good idea. */

/* The toggle itself: sun or moon, matching the surface it sits on. */
button.theme-toggle {
  /* Outlined rather than a bare glyph. On the signed-out pages this sits alone
     in a lot of empty space, and a low-contrast character with no border does
     not read as something you can press — it reads as decoration, or as
     nothing at all. A tap target and a visible edge fix that without making it
     compete with the sign-in button. */
  /* 2.25rem in the nav bar, where its neighbours are compact links and a full
     48px target would make the sticky header a third taller on every screen.
     .bare-chrome below restores the proper tap size, since those pages have
     vertical room to spare. */
  min-height: 2.25rem; min-width: 2.25rem;
  padding: 0.35rem 0.55rem;
  background: transparent; border-color: var(--line); color: var(--text-dim);
  font-size: 1.15rem; line-height: 1;
}
button.theme-toggle:hover { background: var(--bg-sunken); color: var(--text); }
:root[data-theme="dark"] button.theme-toggle { background: transparent; border-color: var(--line); color: var(--text-dim); }
:root[data-theme="dark"] button.theme-toggle:hover { background: var(--bg-sunken); color: var(--text); }
.theme-toggle .sun { display: none; }
:root[data-theme="dark"] .theme-toggle .sun { display: inline; }
:root[data-theme="dark"] .theme-toggle .moon { display: none; }

/* The signed-out pages have no chrome to hang the toggle on, so it gets a
   strip of its own. Aligned to the same gutter as everything else. */
.bare-chrome {
  display: flex; justify-content: flex-end;
  padding: 0.6rem clamp(0.75rem, 3vw, 1.5rem) 0;
}
.bare-chrome button.theme-toggle { min-height: var(--tap); min-width: var(--tap); }

/* --- Chrome ------------------------------------------------------------- */
.chrome {
  display: flex; align-items: center; gap: 1rem;
  /* Full-bleed bar, but its CONTENTS line up with the .content column: the
     first term is the gutter either side of that column once it hits its
     90rem cap, the second is the column's own padding. Without it the brand
     and "sign out" sit on the screen edges while everything below starts
     240px inboard, which reads as two unrelated layouts on a wide monitor. */
  padding-block: 0.6rem;
  padding-inline: calc(max(0px, (100% - 90rem) / 2) + clamp(0.75rem, 3vw, 1.5rem));
  background: var(--bg-raised);
  border-bottom: 1px solid var(--line);
  position: sticky; top: 0; z-index: 20;
}

.chrome .brand { display: flex; align-items: center; gap: 0.5rem; text-decoration: none; min-width: 0; }
/* No `display` here on purpose. These are flex children of .brand, so they are
   blockified already and setting it gains nothing — but at (0,3,0) it outranks
   the .theme-dark-only utility at (0,1,0), which then cannot hide anything and
   both the light and dark artwork render side by side. */
/* The logo carries its own clear space inside the viewBox, so it needs no
   margin here — and 36px is the floor at which the descriptor line under the
   wordmark stays a crisp rule rather than grey mush. */
/* No `display` on these images. They carry .theme-light-only/.theme-dark-only,
   and any display here lands at (0,3,1) — which outranks the (0,1,0) utility
   that hides the wrong variant, so BOTH render. That is exactly what happened:
   the dark logo drew underneath the light one, its white "Gear" invisible on
   white, leaving a stray cyan "Pilot" hanging below the header. The wrappers
   below own the switching; these only get sized. */
.chrome .brand .brand-logo img { height: 36px; width: auto; }
.chrome .brand .brand-mark { display: none; }
.chrome .brand .brand-mark img { height: 28px; width: auto; }
/* flex-wrap on both: the nav holds a variable set of links — an administrator
   carries three more than an Operator — plus the worth figure, and `flex: none`
   let it slide left OVER the brand mark on a narrow phone once the set outgrew
   the row. Nothing may ever overlap; a second right-aligned row is the honest
   fallback, and on the common link set nothing changes. */
.chrome { flex-wrap: wrap; }
.chrome .brand { flex: none; }
.chrome nav {
  margin-left: auto; display: flex; align-items: center; gap: 0.6rem;
  flex: 0 1 auto; min-width: 0; flex-wrap: wrap; justify-content: flex-end;
}

/* What the room is worth. Quiet on purpose — a standing figure, not an alert,
   sitting beside a brand mark already asking for attention. Tabular numerals so
   it does not reflow every time the total changes. */
.worth {
  display: inline-flex; align-items: baseline; gap: 0.4rem;
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.worth strong { font-weight: 640; }
/* A dim inline run beside something louder — the listed price next to the
   worth, a bank reference next to a payment. Unscoped because it was scoped to
   .worth and the second place that wanted it silently rendered as body text. */
.listed { font-size: 0.8rem; color: var(--text-dim); }

/* The nav is already fighting for room on a phone, and the total is the more
   useful half. */
@media (max-width: 46rem) {
  .worth .listed { display: none; }
}

.chrome .who {
  color: var(--text-dim); font-size: 0.85rem;
  max-width: 8rem; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.chrome nav button.link { white-space: nowrap; }

/* On a phone the signature drops to the mark alone — permitted by the
   guidelines where the brand is already established, and a signed-in header
   qualifies — and the name goes too, because "sign out" must not wrap. */
@media (max-width: 30rem) {
  .chrome .brand .brand-logo { display: none; }
  .chrome .brand .brand-mark { display: block; }
  .chrome .who { display: none; }
}

.content {
  /* 62rem wasted most of a desktop screen: the tile menu and the photo wall are
     both auto-fit grids, so they were capped at two or three columns by the
     container rather than by anything about the design. Widening it lets them
     flow to four or five, and the grids stay responsive on the way down. */
  max-width: 90rem;
  /* width:100% is load-bearing, not decoration. body is a flex column, and an
     `auto` horizontal margin on a flex item switches OFF the default stretch —
     the element then shrinks to fit its contents instead of filling to
     max-width. Without this the whole app rendered about 611px wide at every
     desktop size, which is what capped the tile menu at two columns. */
  width: 100%;
  margin: 0 auto;
  padding: clamp(1rem, 4vw, 2rem) clamp(0.75rem, 3vw, 1.5rem) 5rem;
}

/* The container is wide so the GRIDS can use it — not the prose. A line of text
   140 characters long, or a text input a metre wide, is worse than the
   whitespace it replaced, so anything read or typed into keeps its own measure.
   Grids, walls and row lists deliberately are not on this list. */
.content > p,
.content > .lede,
.content > form,
.content > .empty,
.content > .notice { max-width: 46rem; }

/* --- Home tiles ---------------------------------------------------------
   Sale capture is one tap from here and never buried in a menu: if capturing
   a sale takes more than ~20 seconds it will not happen consistently, and a
   stale index is worse than no index. */
.tiles { display: grid; gap: 0.75rem; grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr)); }

.tile {
  display: flex; flex-direction: column; gap: 0.15rem;
  padding: 1.1rem 1.2rem;
  min-height: 5.5rem;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius-lg);
  text-decoration: none; color: var(--text);
  box-shadow: var(--shadow);
  transition: transform 0.12s ease, box-shadow 0.12s ease, border-color 0.12s ease;
}
.tile:hover { transform: translateY(-2px); box-shadow: var(--shadow-lift); border-color: var(--line-strong); color: var(--text); }
.tile strong { font-size: 1.05rem; font-weight: 640; }
.tile span { color: var(--text-dim); font-size: 0.88rem; }

.tile.primary { border-color: var(--accent); background: linear-gradient(180deg, var(--accent-wash), var(--bg-raised) 60%); }
.tile.primary strong { color: var(--accent); }

/* --- Forms --------------------------------------------------------------
   Every identifier input opens with a numeric keypad, focused and ready.
   Scanning is secondary. Layout must not assume a camera exists. */
form { margin: 0 0 1.5rem; }

/* Vertical rhythm in a form comes from the LABEL's top margin, so a submit
   button straight after a field got nothing and sat touching it. Space the
   button here rather than on each page — the auth screens had already patched
   it locally, and the admin console and import page had not. Hidden inputs are
   excluded: the little inline forms put one before their button, and spacing
   off an invisible element would push those buttons around. */
form :is(input:not([type="hidden"]), select, textarea) + button,
form :is(input:not([type="hidden"]), select, textarea) + .actions {
  margin-top: 1.1rem;
}

label {
  display: block;
  font-weight: 600; font-size: 0.9rem;
  margin: 1rem 0 0.35rem;
}
label .hint { display: block; font-weight: 400; color: var(--text-dim); font-size: 0.82rem; }

/* A checkbox and its words belong on one line, with the words not sliding back
   under the box when they wrap. */
label:has(input[type="checkbox"]) {
  display: flex; align-items: flex-start; gap: 0.6rem;
  font-weight: 500; line-height: 1.35;
  min-height: var(--tap); padding: 0.5rem 0;
}
label input[type="checkbox"] { width: 1.4rem; height: 1.4rem; flex: none; accent-color: var(--accent); }

/* Every text-like input, stated as an EXCLUSION rather than a list.
   The list version silently missed each new type as it appeared — tel on the
   invite, profile and join screens, url on the listing screens, date at intake
   — and an unmatched input falls back to the browser default: narrow, unpadded
   and below the tap target, sitting next to siblings that are none of those
   things. Excluding is the safe direction: a type nobody thought about is
   styled like a field, which is what it almost always is.

   StylesheetCoverageTests fails if a type is used that neither this rule nor a
   deliberate exception below covers. */
input:not([type="checkbox"], [type="radio"], [type="file"], [type="submit"],
          [type="button"], [type="image"], [type="reset"], [type="hidden"], [type="range"],
          [type="color"]),
select, textarea {
  width: 100%;
  min-height: var(--tap);
  padding: 0.6rem 0.8rem;
  font: inherit; color: var(--text);
  background: var(--bg-raised);
  border: 1.5px solid var(--line-strong);
  border-radius: var(--radius);
}

input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible, a:focus-visible {
  outline: 3px solid var(--accent);
  outline-offset: 2px;
}

/* Blazor's FocusOnNavigate moves focus to the h1 on every navigation, which is
   right for a screen reader and wrong for a mouse — it drew a box round the
   heading of every page. The heading stays focusable; it just stops looking
   like something you clicked. */
h1:focus, h1:focus-visible { outline: none; }

/* The keypad field: big, tabular, centred. Typed with a thumb while the other
   hand holds the bag. */
input.keypad {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-size: 1.9rem;
  font-weight: 600;
  letter-spacing: 0.12em;
  text-align: center;
  min-height: 4rem;
}

/* A reference typed as a secondary field. Tabular and spaced like the big
   keypad so a number reads the same wherever it is entered, but not sized as
   though it were the point of the screen. */
input.keypad-small {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.08em;
  max-width: 12rem;
  /* Block, or a narrow field lets whatever follows sit beside it — the submit
     button flowed up alongside the reference box and read as part of it. */
  display: block;
}

/* The retire form is the one destructive thing on the item screen, so it is
   set apart rather than reading as more fields to fill in. */
form.retire {
  border: 1px solid var(--line);
  border-left: 3px solid var(--danger);
  border-radius: var(--radius);
  padding: 1rem 1.1rem 1.2rem;
  background: var(--bg-raised);
}
form.retire label:first-of-type { margin-top: 0; }

input:disabled, select:disabled {
  background: var(--bg-sunken); color: var(--text-dim); cursor: not-allowed;
}

input[type="file"] {
  width: 100%; padding: 0.9rem;
  background: var(--bg-sunken);
  border: 1.5px dashed var(--line-strong);
  border-radius: var(--radius);
}

button, .button {
  display: inline-flex; align-items: center; justify-content: center; gap: 0.4rem;
  min-height: var(--tap); padding: 0.6rem 1.2rem;
  font: inherit; font-weight: 620;
  color: var(--silver); background: var(--navy);
  border: 1.5px solid var(--navy); border-radius: var(--radius);
  cursor: pointer; text-decoration: none;
  transition: background 0.12s ease, border-color 0.12s ease;
}
button:hover, .button:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
button[disabled] { opacity: 0.45; cursor: not-allowed; }

:root[data-theme="dark"] button,
:root[data-theme="dark"] .button { color: var(--navy); background: var(--silver); border-color: var(--silver); }
:root[data-theme="dark"] button:hover,
:root[data-theme="dark"] .button:hover { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }

/* Reserved for the action that finds or confirms the one item. */
button.found, .button.found {
  background: var(--accent); border-color: var(--accent); color: var(--on-accent);
}
button.found:hover { background: var(--accent-bright); border-color: var(--accent-bright); color: var(--navy); }

button.quiet, .button.quiet {
  background: transparent; color: var(--text); border-color: var(--line-strong);
}
button.quiet:hover { background: var(--bg-sunken); color: var(--text); border-color: var(--line-strong); }

/* Destructive. The only actions wearing this are the ones with no undo, so it
   must not be mistakable for `quiet` sitting next to it in a row of buttons. */
button.danger, .button.danger {
  background: var(--danger); border-color: var(--danger); color: #fff;
}
button.danger:hover { background: var(--danger); border-color: var(--danger); filter: brightness(0.92); }

/* Inline warning text — a warehouse counting down to deletion, an owner with
   no TOTP. Amber means "this one", per the brand pack, and it stays scarce. */
.warn { color: var(--accent); font-weight: 600; }

button.link {
  background: none; border: none; color: var(--accent);
  min-height: auto; padding: 0.25rem 0.4rem; font-weight: 600;
}
button.link:hover { background: none; color: var(--accent-bright); }

form.inline { display: inline; margin: 0; }
.actions { display: flex; flex-wrap: wrap; gap: 0.5rem; margin-top: 1rem; }

/* --- Messages ----------------------------------------------------------- */
.error, .notice, .wrote, .empty, .saved {
  padding: 0.85rem 1rem; border-radius: var(--radius);
  border: 1px solid var(--line); margin: 0 0 1rem;
}
.error  { background: var(--danger-wash); border-color: var(--danger); color: var(--danger); font-weight: 560; }
.notice { background: var(--brand-wash); border-color: var(--brand); }
.empty  { background: var(--bg-sunken); color: var(--text-dim); text-align: center; }

/* Quieter than a notice. It confirms something small went through and is
   read once, so it must not compete with the screen it appears on. */
.saved  { background: var(--bg-sunken); border-color: var(--line-strong); color: var(--text-dim); }

/* "Write 0412 7 on the bag." The one moment the operator has to copy
   something down, so it gets the whole width and the accent. */
.wrote {
  background: var(--accent-wash); border-color: var(--accent);
  text-align: center; padding: 1.4rem 1rem;
}
.wrote .label { display: block; color: var(--text-dim); font-size: 0.85rem; margin-bottom: 0.4rem; }

/* Paging under a wall. Links, not buttons — a page is a place, and a place
   can be refreshed and sent to somebody. The disabled ends stay rendered so
   the row does not jump as the controls appear and vanish. */
.pager {
  display: flex; align-items: center; justify-content: center; gap: 1.25rem;
  margin: 1.5rem 0; font-variant-numeric: tabular-nums;
}
.pager a { min-height: var(--tap); display: inline-flex; align-items: center; }
.pager .ghost { color: var(--text-dim); opacity: 0.45; }
.pager .where { color: var(--text-dim); font-size: 0.9rem; }

/* --- Item cards ---------------------------------------------------------
   Photo-first. A wall of thumbnails is how humans recognise clothing; a table
   of text is not. And after a reference is entered, the photo is shown BEFORE
   any action is permitted — handwriting is occasionally ambiguous. */
.card {
  display: grid; gap: 1rem;
  grid-template-columns: minmax(0, 8rem) minmax(0, 1fr);
  padding: 1rem;
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-left: 4px solid var(--accent);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow);
  margin: 0 0 1rem;
}
/* The left column: the confirmation photo, and whatever the screen stacks under
   it. align-content keeps it at the top rather than spread down a tall body. */
.card > .shots { display: grid; gap: 0.6rem; align-content: start; min-width: 0; }
.card .confirm {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: var(--radius); background: var(--bg-sunken);
}
.card h2 { margin: 0; }
.card .body { min-width: 0; }
.card .no-photo {
  display: flex; align-items: center; justify-content: center; text-align: center;
  aspect-ratio: 1; padding: 0.5rem;
  color: var(--text-dim); font-size: 0.8rem;
  background: var(--bg-sunken); border-radius: var(--radius);
}

/* The other photographs, two across in an 8rem column. Twenty stacked one above
   the other would run past the bottom of the form they sit beside. */
.more-shots {
  list-style: none; margin: 0; padding: 0;
  display: grid; gap: 0.4rem;
  grid-template-columns: repeat(auto-fill, minmax(3.25rem, 1fr));
}
.more-shots > li { display: grid; gap: 0.15rem; }
.more-shots img {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: calc(var(--radius) - 2px); background: var(--bg-sunken);
  border: 1px solid var(--line);
}
/* Stacked deliberately rather than wrapped by accident: at 3.25rem the two
   actions never fit on one line, and space-between then strands them. */
.more-shots .photo-actions {
  display: grid; justify-items: start; gap: 0.05rem; font-size: 0.7rem;
}
.more-shots button.link { font-size: 0.7rem; padding: 0; }

.add-shots { margin: 0; display: grid; gap: 0.35rem; }
.add-shots input[type=file] { font-size: 0.75rem; padding: 0.35rem; }
.add-shots button { width: 100%; }
.add-shots label { font-size: 0.8rem; }

@media (max-width: 30rem) {
  .card { grid-template-columns: 1fr; }
  .card .confirm, .card .no-photo { aspect-ratio: 4 / 3; }
  .more-shots { grid-template-columns: repeat(auto-fill, minmax(4.5rem, 1fr)); }
}

dl.facts { display: grid; grid-template-columns: auto 1fr; gap: 0.25rem 1rem; margin: 0.6rem 0 0; }
dl.facts dt { color: var(--text-dim); font-size: 0.85rem; }
dl.facts dd { margin: 0; font-weight: 560; }

/* Where it is. The answer to the question the product is named after. */
.where {
  font-family: var(--mono); font-weight: 600;
  letter-spacing: 0.04em;
  padding: 0.1rem 0.45rem; border-radius: 5px;
  background: var(--brand-wash); color: var(--brand);
}
.where.nowhere { background: var(--bg-sunken); color: var(--text-dim); }

/* --- Grid of thumbnails ------------------------------------------------- */
.wall { display: grid; gap: 0.6rem; grid-template-columns: repeat(auto-fill, minmax(8.5rem, 1fr)); }
.wall a { text-decoration: none; color: var(--text); }
.wall figure { margin: 0; }
/* The frame carries the status. Three pixels because one is invisible at
   thumbnail size across a room, and it sits on this wrapper rather than on the
   picture so that dimming a written-off item does not dim its border too. */
.wall .frame {
  border: 3px solid var(--band, var(--line));
  border-radius: var(--radius);
  overflow: hidden;
  background: var(--bg-sunken);
  line-height: 0;
}
.wall img, .wall .no-photo {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  background: var(--bg-sunken);
}

/* --- Status bands ------------------------------------------------------
   Five, not eleven. Nobody learns eleven colours, and what matters at a glance
   is on the shelf / going out / out / not available / gone. See
   Display/StatusBands.cs for which status is which. */
.band-on-the-shelf  { --band: var(--shelf); }         /* steel  — bookable        */
.band-going-out     { --band: var(--going); }         /* plum   — in staging      */
.band-on-hire       { --band: var(--away); }          /* teal   — out earning     */
.band-not-available { --band: var(--blocked); }       /* ochre  — here, not free  */
.band-gone          { --band: var(--danger); }        /* red    — gone            */

/* Gone is gone: the picture is a record, not kit. Dimming it means a unit
   written off never gets mistaken for one on a shelf, even in a thumbnail too
   small to read the frame on. */
.wall .band-gone img { opacity: 0.5; filter: grayscale(0.7); }

.status-key {
  list-style: none; margin: 0 0 0.75rem; padding: 0;
  display: flex; flex-wrap: wrap; gap: 0.35rem 1rem;
  font-size: 0.8rem; color: var(--text-dim);
}
.status-key li { display: flex; align-items: center; gap: 0.35rem; }
.status-key .swatch {
  width: 0.85rem; height: 0.85rem; flex: none;
  border-radius: 3px; border: 3px solid var(--band, var(--line));
}

/* Available to a screen reader, absent from the page. The frame is the glance;
   this is the same fact for somebody who cannot see it. */
.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip-path: inset(50%); white-space: nowrap; border: 0;
}
.wall figcaption { font-size: 0.8rem; margin-top: 0.3rem; }
.wall figcaption .ref { display: block; color: var(--text-dim); }

/* Edit, on the picture rather than under it. The wall is a recognition surface
   before it is a menu, so this must not push the thumbnails apart or compete
   with them — but a wrong title is noticed here and nowhere else. */
.wall figure { position: relative; }
.wall .edit {
  position: absolute; top: 0.3rem; right: 0.3rem;
  padding: 0.25rem 0.55rem;
  font-size: 0.75rem; font-weight: 640; line-height: 1.4;
  text-decoration: none; color: var(--text);
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: 999px; box-shadow: var(--shadow);
  opacity: 0.85;
}
.wall .edit:hover, .wall .edit:focus-visible {
  opacity: 1; color: var(--accent); border-color: var(--accent);
}

/* --- Photo strip on the edit screen ------------------------------------- */
/* Up to twenty per item, so this wraps rather than scrolls sideways — a
   horizontal scroller hides the photos at the end, and the whole point of the
   strip is seeing which one comes first. */
.photo-strip {
  list-style: none; margin: 0 0 1.25rem; padding: 0;
  display: grid; gap: 0.6rem;
  grid-template-columns: repeat(auto-fill, minmax(7rem, 1fr));
}
.photo-strip > li {
  display: flex; flex-direction: column; gap: 0.35rem;
  padding: 0.4rem; border: 1px solid var(--line);
  border-radius: var(--radius); background: var(--bg-sunken);
}
/* The confirmation photo (CLAUDE.md 6) is the one that settles which bag this
   is, so it is marked, not merely first. */
.photo-strip > li.primary { border-color: var(--accent); background: var(--accent-wash); }
.photo-strip img {
  width: 100%; aspect-ratio: 1; object-fit: cover;
  border-radius: calc(var(--radius) - 2px); background: var(--bg);
}
.photo-actions {
  display: flex; align-items: center; justify-content: space-between;
  gap: 0.4rem; flex-wrap: wrap; font-size: 0.8rem;
}
.photo-actions .pill { font-size: 0.7rem; }
.photo-strip button.link { font-size: 0.8rem; padding: 0; }

/* --- Status pills ------------------------------------------------------- */
.pill {
  display: inline-block; padding: 0.1rem 0.5rem;
  font-size: 0.75rem; font-weight: 640; letter-spacing: 0.02em;
  border-radius: 999px;
  background: var(--bg-sunken); color: var(--text-dim);
  border: 1px solid var(--line);
}
.pill.live    { background: var(--brand-wash); color: var(--brand); border-color: var(--brand); }
.pill.found   { background: var(--accent-wash); color: var(--accent); border-color: var(--accent); }
.pill.gone    { background: var(--danger-wash); color: var(--danger); border-color: var(--danger); }

/* A field a model filled in. Quiet on purpose — it marks something to CHECK,
   not something that is done, and there may be a dozen of them at once. */
.pill.suggested {
  background: transparent; color: var(--text-dim);
  border-style: dashed; font-weight: 560; text-transform: lowercase;
}

/* --- Something is happening ---------------------------------------------
   Enhanced navigation posts by fetch, so the browser shows nothing at all
   while a model is looking at a photograph. These two say so: a bar at the
   top of the window for when the button has scrolled away, and a spinner in
   the button itself for where the eye already is. */
#busy-bar {
  /* Where there is no nav bar to hang under — sign-in, the field page — it
     pins to the top of the window instead. z-index clears the scanner overlay:
     an indicator that can be covered is not an indicator. */
  position: fixed; inset: 0 0 auto 0; z-index: 200;
  height: 3px; opacity: 0;
  background: var(--bg-sunken);
  pointer-events: none;
  /* The travelling fill runs past both ends; without this it widens the page
     and the whole layout shifts every time something loads. */
  overflow: hidden;
  transition: opacity 120ms linear;
}

/* Along the bottom of the nav bar, which is where the eye already is after
   pressing something in the page below it. Absolute against the sticky header,
   so no height is measured and none needs re-measuring. */
.chrome > #busy-bar { position: absolute; inset: auto 0 0 0; }
#busy-bar.on { opacity: 1; }
#busy-bar.on::before {
  content: ""; display: block; height: 100%; width: 40%;
  background: var(--accent);
  animation: busy-slide 1.1s ease-in-out infinite;
}
@keyframes busy-slide {
  0%   { transform: translateX(-100%); }
  100% { transform: translateX(250%); }
}

.spinner {
  display: inline-block; vertical-align: -0.125em;
  width: 0.9em; height: 0.9em; margin-right: 0.45em;
  border: 2px solid currentColor; border-right-color: transparent;
  border-radius: 50%;
  animation: busy-spin 0.7s linear infinite;
}
@keyframes busy-spin { to { transform: rotate(360deg); } }

button[aria-busy="true"] { cursor: progress; }

/* A moving bar is the whole signal, so it cannot simply be switched off here —
   it slows to something that reads as alive rather than as motion. */
@media (prefers-reduced-motion: reduce) {
  #busy-bar.on::before { width: 100%; animation: busy-fade 1.6s ease-in-out infinite; }
  .spinner { animation-duration: 2s; }
  @keyframes busy-fade { 0%, 100% { opacity: 0.35; } 50% { opacity: 1; } }
}

/* --- The magic button --------------------------------------------------- */
.suggest { margin: 0 0 1rem; display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap; }
.suggest .hint { margin: 0; flex: 1 1 14rem; }
button.wand { white-space: nowrap; }
button.wand span { color: var(--accent); margin-right: 0.3rem; }

/* --- Lists and tables --------------------------------------------------- */
.rows { list-style: none; margin: 0 0 1.5rem; padding: 0; display: grid; gap: 0.5rem; }
.rows > li {
  display: flex; align-items: center; gap: 0.75rem; flex-wrap: wrap;
  padding: 0.75rem 1rem;
  background: var(--bg-raised); border: 1px solid var(--line); border-radius: var(--radius);
}
.rows > li .grow { flex: 1; min-width: 8rem; }
.rows > li.done { border-left: 4px solid var(--brand); }
.rows > li.next { border-left: 4px solid var(--accent); }

.money { font-variant-numeric: tabular-nums; font-weight: 620; }

/* --- Auth pages ---------------------------------------------------------
   No navigation, no warehouse chrome, no circuit. */
.auth {
  /* Same reason as .content: auto margins on a flex item disable stretch. */
  width: 100%;
  max-width: 24rem; margin: 0 auto; padding: clamp(1.5rem, 8vh, 4rem) 1.25rem 3rem;
}
/* The wrapper owns the layout so the images can own nothing but their size. */
.auth .lockup-slot { text-align: center; margin: 0 0 1.75rem; }
.auth .lockup { width: min(15rem, 70%); }
.auth .voice { text-align: center; color: var(--text-dim); font-size: 0.9rem; margin: -1rem 0 1.75rem; }
.auth form { background: var(--bg-raised); padding: 1.25rem; border: 1px solid var(--line); border-radius: var(--radius-lg); box-shadow: var(--shadow); }
.auth button { width: 100%; margin-top: 1.25rem; }

/* --- Back ---------------------------------------------------------------
   Sized for a thumb, and above the heading so it is the first thing focus
   reaches. */
.back {
  display: inline-flex; align-items: center; gap: 0.4rem;
  min-height: var(--tap); padding: 0.4rem 0.7rem 0.4rem 0.5rem;
  margin: 0 0 0.25rem -0.5rem;
  font-weight: 600; font-size: 0.95rem;
  color: var(--text-dim); text-decoration: none;
  border-radius: var(--radius);
}
/* Amber on hover, not ink. This is the one place the accent is spent on
   something other than "this one, out of all of them" — a back link is on every
   screen, only ever one of them, and only lit while the pointer is on it. */
.back:hover, .back:focus-visible { color: var(--accent); background: var(--bg-sunken); }
.back span { font-size: 1.15rem; line-height: 1; }

.auth .back { margin-bottom: 0.75rem; }

/* --- Warehouse switcher --------------------------------------------------
   Rarely seen — most people belong to one warehouse — but when it is seen the
   question is "which room am I about to be in", so the name carries the row and
   the slug and role sit under it. */
.warehouses { list-style: none; margin: 0 0 1.5rem; padding: 0; display: grid; gap: 0.5rem; }
.warehouses > li {
  background: var(--bg-raised);
  border: 1px solid var(--line);
  border-radius: var(--radius);
}
.warehouses > li.here { border-left: 4px solid var(--accent); }
.warehouses form {
  display: flex; align-items: center; gap: 0.75rem;
  margin: 0; padding: 0.7rem 0.9rem;
}
.warehouses .grow { flex: 1; min-width: 0; }
.warehouses strong { display: block; }

/* --- Section headers ---------------------------------------------------- */
.section-head { display: flex; align-items: baseline; gap: 0.75rem; margin: 2rem 0 0.75rem; }
.section-head h2 { margin: 0; }
.section-head .count { color: var(--text-dim); font-size: 0.85rem; }
.section-head a { margin-left: auto; font-size: 0.9rem; }

.lede { color: var(--text-dim); margin-top: -0.25rem; }

/* --- Depot headings -----------------------------------------------------
   Only ever rendered when a company has more than one building. A single-depot
   warehouse sees an unbroken list, because for them the grouping would be a
   heading with everything under it. */
h3.depot {
  margin: 1.5rem 0 0.4rem;
  font-family: var(--mono); font-size: 0.8rem; letter-spacing: 0.08em;
  text-transform: uppercase; color: var(--text-dim);
  border-bottom: 1px solid var(--line); padding-bottom: 0.3rem;
}
h3.depot:first-of-type { margin-top: 0.5rem; }

/* A name over its address, in a row that is otherwise all on one line. */
.stacked { display: flex; flex-direction: column; gap: 0.15rem; }

/* --- Permission list ----------------------------------------------------
   Thirty-two rows at row height is a form nobody reads, and an access model
   nobody reads is one that ends up with everything ticked. */
.rows.checks { gap: 0.25rem; }
.rows.checks > li { padding: 0.35rem 0.7rem; }
.rows.checks label { display: flex; align-items: center; gap: 0.6rem; margin: 0; cursor: pointer; }
.rows.checks input[type="checkbox"] { flex: none; margin: 0; }

/* --- Pick list ----------------------------------------------------------
   Sorted by location path — zone, rail, position — not by item. A three-item
   bundle is one walk, not three. */
.walk { counter-reset: stop; }
.walk > li::before {
  counter-increment: stop; content: counter(stop);
  display: grid; place-items: center;
  width: 1.9rem; height: 1.9rem; flex: none;
  font-family: var(--mono); font-weight: 700; font-size: 0.9rem;
  border-radius: 50%; background: var(--bg-sunken); color: var(--text-dim);
}
.walk > li.next::before { background: var(--accent); color: var(--on-accent); }
.walk > li.done::before { background: var(--brand); color: var(--silver); content: "✓"; }
.walk img { width: 3rem; height: 3rem; object-fit: cover; border-radius: 6px; flex: none; }

/* --- Enrolment ---------------------------------------------------------- */
.qr { display: block; width: min(14rem, 60%); margin: 1rem auto; background: #fff; padding: 0.75rem; border-radius: var(--radius); }
.secret { font-family: var(--mono); font-size: 0.9rem; letter-spacing: 0.1em; word-break: break-all; text-align: center; color: var(--text-dim); }

/* --- Scanner ------------------------------------------------------------
   The secondary button and the full-screen viewfinder. The button only exists
   when BarcodeDetector does. */
button.scan { margin-top: 0.4rem; }

.scan-overlay {
  position: fixed; inset: 0; z-index: 100;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 1rem; padding: 1rem;
  background: rgb(17 26 22 / 0.92);
}
.scan-overlay video {
  width: 100%; max-width: 28rem; border-radius: var(--radius-lg);
}

/* --- Misc --------------------------------------------------------------- */
.stack > * + * { margin-top: 1rem; }
.muted { color: var(--text-dim); }
.small { font-size: 0.85rem; }
.right { margin-left: auto; }

/* --- Footer -------------------------------------------------------------
   Quiet on purpose. It is on every screen including the ones used a hundred
   times a day, so it has to sit below the work rather than compete with it. */
.site-footer {
  padding: 2rem 1rem 1.5rem;
  text-align: center;
}
.site-footer a {
  color: var(--text-dim);
  font-size: 0.8rem;
  text-decoration: none;
}
.site-footer a:hover { color: var(--text); text-decoration: underline; }

/* The content already reserves space at the bottom for thumb reach, so the
   footer does not need to add more. */
.content { padding-bottom: 2rem; }

/* Takes the slack in the body column above. margin-top rather than flex-grow
   on the main element, because both layouts have a different main — .content
   and .auth — and the footer is the one element common to both. */
.site-footer { margin-top: auto; }

#blazor-error-ui {
  display: none; position: fixed; bottom: 0; left: 0; right: 0; z-index: 1000;
  padding: 0.75rem 1.25rem; background: var(--danger); color: #fff;
}
#blazor-error-ui .dismiss { cursor: pointer; float: right; }

/* The way out of the error bar, on a red ground where the ordinary link
   colour disappears. It was unstyled and rendered as a default blue link on
   red, which is the least readable pair of colours in the app. */
#blazor-error-ui .reload { color: #fff; text-decoration: underline; font-weight: 560; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after { transition: none !important; animation: none !important; }
}


/* --- The availability grid ----------------------------------------------
   The screen the desk lives on: models down, weeks across, and how many are
   free in each cell. It is the only wide thing in the app, so it scrolls
   inside its own box rather than making the page scroll sideways — a page
   that moves horizontally on a phone loses the navigation. */
.scroller { overflow-x: auto; margin: 0 0 1.5rem; }

.grid {
  border-collapse: collapse; width: 100%; font-size: 0.9rem;
  background: var(--bg-raised);
}

.grid th, .grid td {
  border: 1px solid var(--line); padding: 0.4rem 0.6rem; text-align: center;
  white-space: nowrap;
}

.grid thead th { background: var(--bg-sunken); font-weight: 620; }

/* The model column is the one thing being read down rather than across, so
   it is left-aligned and sticks while the weeks scroll past it. */
.grid tbody th {
  text-align: left; position: sticky; left: 0; background: var(--bg-raised);
  font-weight: 560;
}

/* Three states, not two. "None free" and "none free unless you bump a quote"
   are different answers and only a person can choose between them, so the
   middle one gets its own colour rather than being rounded down to red. */
.grid td.ok   { color: var(--text); }
.grid td.warn { background: var(--accent-wash); color: var(--text); }
.grid td.bad  { background: var(--danger-wash); color: var(--danger); font-weight: 560; }

.grid td .small { display: block; font-size: 0.75rem; color: var(--text-dim); }

/* --- Form shapes --------------------------------------------------------
   Three, and the difference between them is how much of the screen the form
   owns. A card-form is one of several on a page — check-in draws one per unit
   — so it carries its own border; an inline-form is a row of controls inside
   a screen that belongs to something else. */
.card-form {
  background: var(--bg-raised); border: 1px solid var(--line);
  border-radius: var(--radius-lg); padding: 1.25rem; margin: 0 0 1rem;
  box-shadow: var(--shadow);
}

.card-form h2 { margin-top: 0; }

.inline-form, .filters {
  display: flex; flex-wrap: wrap; align-items: flex-end; gap: 0.75rem;
  margin: 0 0 1.25rem;
}

.inline-form label, .filters label {
  margin: 0; font-size: 0.8rem; color: var(--text-dim);
}

.inline-form select, .inline-form input, .filters select, .filters input {
  margin: 0; width: auto; min-width: 8rem;
}

.inline-form button, .filters button { margin: 0; }

/* A checkbox is a box beside a label, not a field with a label above it. */
.check {
  display: flex; align-items: center; gap: 0.5rem; font-weight: 400;
  margin: 0 0 0.5rem;
}

.check input { width: auto; margin: 0; }

/* A row of secondary destinations under a form — the other things you might
   do to this unit. Quieter than .actions, which carries buttons. */
.also {
  display: flex; flex-wrap: wrap; gap: 1rem; margin-top: 1.25rem;
  padding-top: 1rem; border-top: 1px solid var(--line);
  font-size: 0.9rem;
}

/* --- Paper ---------------------------------------------------------------
   Documents that get printed and handed to somebody: quotes, pick sheets,
   delivery notes, invoices. A screen shows them so they can be checked before
   they go out, and the browser's own print dialog is the PDF button — a
   headless browser per request is a heavy dependency to run on a home
   connection, and it would sit in the path of the one action a crew does while
   a van is waiting. */
.paper {
  max-width: 190mm; margin: 1.5rem auto; padding: 12mm;
  background: #fff; color: #1a1a1a;
  border: 1px solid var(--line); border-radius: var(--radius);
  font-size: 10.5pt; line-height: 1.45;
}
.paper h1 { font-size: 18pt; margin: 0 0 0.2rem; }
.paper h2 { font-size: 11pt; margin: 1.4rem 0 0.4rem; letter-spacing: 0.04em;
            text-transform: uppercase; color: #555; }
.paper a { color: inherit; }
/* A job title is a name, not a section label. The h2 rule shouts on purpose;
   this is what stops it shouting a customer's show back at them. */
.paper .subject { font-size: 13pt; font-weight: 700; margin: 0 0 0.3rem; }

.paper-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 2rem; }
.paper-head .who { text-align: right; font-size: 9.5pt; color: #444; }
.paper-mark { font-family: var(--mono); font-weight: 700; letter-spacing: 0.06em; }

.paper table { width: 100%; border-collapse: collapse; margin: 0.4rem 0 1rem; }
.paper th { text-align: left; font-size: 8.5pt; letter-spacing: 0.06em;
            text-transform: uppercase; color: #555;
            border-bottom: 1px solid #bbb; padding: 0.3rem 0.4rem; }
.paper td { padding: 0.35rem 0.4rem; border-bottom: 1px solid #eee; vertical-align: top; }
.paper td.num, .paper th.num { text-align: right; white-space: nowrap; }
.paper tfoot td { border-bottom: none; border-top: 1px solid #bbb; font-weight: 700; }

.paper .terms { font-size: 8.5pt; color: #555; margin-top: 1.5rem; }
.paper .sign { margin-top: 2rem; display: flex; gap: 3rem; }
.paper .sign div { flex: 1; border-top: 1px solid #999; padding-top: 0.3rem;
                   font-size: 8.5pt; color: #555; }

.paper-actions { text-align: center; margin: 0 0 2rem; }

@media print {
  /* The page IS the document. Everything else was scaffolding to look at it
     with. */
  .no-print, nav, header, footer, .brand-tile { display: none !important; }
  body { background: #fff; }
  .paper { max-width: none; margin: 0; padding: 0; border: none; border-radius: 0; }
  .paper table { page-break-inside: auto; }
  .paper tr { page-break-inside: avoid; }
  .paper h2 { page-break-after: avoid; }
}
