:root {
  /* Warm neutrals, but cleaner and further apart than they were. The old set
     sat inside six shades of the same khaki, which is what made every screen
     read as one flat grey field. */
  --cream: #f2eee4;
  --cream-2: #e6e0d1;
  --paper: #fffefb;
  --graphite: #22262a;
  --graphite-2: #363c42;
  --ink: #171a1d;
  --muted: #6b665c;
  --line: #d6cfbf;

  /* Brass with the dust taken off — it is the brand colour and it should look
     like metal, not like cardboard. */
  --brass: #c08f3e;
  --brass-lt: #e8c98a;
  --brass-dk: #8a6420;
  --brass-wash: #fbf2e0;

  /* Status colours. The palette had exactly one (danger) and everything else
     was grey, so nothing on screen ever told you anything by colour alone. */
  --danger: #a8352a;
  --danger-wash: #fbeceb;
  --good: #3f7a4b;
  --good-wash: #eaf4ec;
  --warn: #a86a12;
  --warn-wash: #fcf1de;
  --info: #2c6379;
  --info-wash: #e8f2f6;

  --radius: 4px;
  --shadow: 0 1px 2px rgba(23,26,29,.07), 0 4px 14px rgba(23,26,29,.06);
}

* { box-sizing: border-box; }

/* Any bare `display:` rule outranks the UA stylesheet's [hidden]{display:none},
   which silently leaves JS-hidden elements on screen. Bitten three times now
   (lightbox, mention menu, assignee chip), so settle it globally. */
[hidden] { display: none !important; }

body {
  margin: 0;
  background: var(--cream);
  color: var(--ink);
  font: 15px/1.5 "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--brass-dk); text-decoration: none; }
a:hover { text-decoration: underline; }

/* ---------- chrome ---------- */

.topbar {
  background: var(--graphite);
  color: var(--cream);
  display: flex; align-items: center; gap: 28px;
  padding: 0 20px; height: 52px;
  position: sticky; top: 0; z-index: 50;
}
.brand {
  font-weight: 700; letter-spacing: .14em; text-transform: uppercase;
  font-size: 12px; color: var(--cream);
}
.brand span { color: var(--brass); }
/* nav scrolls; More must not be inside it.
   Setting overflow-x on an element makes the browser compute overflow-y to
   `auto` too, so a sideways-scrolling nav is a scroll container in BOTH axes
   and clips anything drawn below it — which is exactly what hid the More panel
   when it lived in here. The wrapper holds both and does not scroll, so the
   panel has nothing to be clipped by. Same failure tip.js works around by
   drawing tooltips fixed and appended to <body>.

   min-width:0 because a flex item will not shrink below its content by
   default: without it the nav keeps its full width on a narrow screen and the
   links spill out past the dark bar instead of staying inside it. */
/* flex-basis auto, not 0, now that the search box shares this row. With a
   basis of 0 the nav asked for no width at all, so the search kept its full
   250px and the nav gave up the difference — on a 1100px window that meant
   "Invoices" cut off mid-word behind the scroller. Sized from its content,
   the two shrink in proportion and the last link stays whole for longer. */
.navwrap { display: flex; align-items: center; gap: 4px; flex: 1 1 auto; min-width: 0; }
.topbar nav {
  display: flex; gap: 4px; min-width: 0;
  overflow-x: auto; scrollbar-width: none;
}
.topbar nav::-webkit-scrollbar { display: none; }
.topbar nav a {
  color: #c9c3b6; padding: 6px 12px; border-radius: var(--radius);
  font-size: 14px; text-decoration: none;
  /* "Production Board" broke onto two lines inside a 52px bar. */
  white-space: nowrap;
}
.topbar nav a:hover { background: var(--graphite-2); color: var(--cream); }
.topbar nav a.on {
  background: var(--graphite-2); color: #fff;
  box-shadow: inset 0 -3px 0 var(--brass);
}
.topbar nav a.on::first-letter { color: inherit; }

/* The More menu.
   A native <details>, so it opens and takes the keyboard without a script —
   seven pages live in here and must not depend on one loading. more.js only
   adds Escape and click-away. */
.more { position: relative; flex: none; }
.more > summary {
  /* Same shape as a nav link, so the bar reads as one row of controls. */
  color: #c9c3b6; padding: 6px 12px; border-radius: var(--radius);
  font-size: 14px; cursor: pointer; user-select: none;
  display: flex; align-items: center; gap: 6px;
  white-space: nowrap;
  list-style: none;               /* Firefox */
}
.more > summary::-webkit-details-marker { display: none; }   /* Safari, older Chrome */
.more > summary:hover { background: var(--graphite-2); color: var(--cream); }
.more > summary:focus-visible { outline: 2px solid var(--brass); outline-offset: -2px; }
/* Active when the page you are on lives inside the menu, so "where am I"
   survives being one level down. Matches .topbar nav a.on exactly. */
.more > summary.on {
  background: var(--graphite-2); color: #fff;
  box-shadow: inset 0 -3px 0 var(--brass);
}
.more > summary .chev {
  width: 0; height: 0; flex: none;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor; opacity: .7;
}
.more[open] > summary .chev { transform: rotate(180deg); }
.more[open] > summary { background: var(--graphite-2); color: var(--cream); }

/* Drawn below the bar rather than inside it — the topbar is only 52px tall and
   clips anything taller. No fade in: a CSS transition does not advance in a
   throttled tab, which is what left tooltips invisible before tip.js stopped
   animating opacity. The panel is simply there or not. */
.morepanel {
  position: absolute; top: calc(100% + 6px); left: 0;
  min-width: 178px; z-index: 60;
  background: var(--graphite); border: 1px solid #3a3f43;
  border-radius: var(--radius); padding: 6px;
  box-shadow: 0 10px 28px rgba(0,0,0,.38);
  display: flex; flex-direction: column; gap: 1px;
}
.morepanel a {
  color: #c9c3b6; text-decoration: none; font-size: 13.5px;
  padding: 7px 10px; border-radius: 5px; white-space: nowrap;
}
.morepanel a:hover { background: var(--graphite-2); color: var(--cream); }
.morepanel a.on { color: #fff; box-shadow: inset 2px 0 0 var(--brass); }
/* Setting the board up and working a claim are different errands, so the admin
   half is called out rather than run on from the working pages. */
.moresep {
  font: 700 9.5px "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: .14em; text-transform: uppercase; color: #7d776c;
  padding: 9px 10px 4px; margin-top: 5px; border-top: 1px solid #3a3f43;
}

/* Narrow screens: give the links the room back.
   The bar is brand + nav + bell + name + sign out, and on a phone the fixed
   parts leave the nav squeezed to a sliver — the links are technically
   scrollable but there is nothing to grab. So the wordmark drops to its logo
   and the signed-in name goes, both of which are decoration here: the name is
   on the profile page and the mark still goes home. Sign out stays, because
   hunting for it is worse than the space it costs. */
@media screen and (max-width: 760px) {
  .topbar { gap: 12px; padding: 0 12px; }
  .brand.has-mark > span { display: none; }
  .topbar .who > a { display: none; }
  .topbar .who { gap: 8px; }
}
/* nowrap: with the search box on the bar there is less slack, and "Sign out"
   broke onto two lines inside a 52px bar rather than the bar simply being
   tighter. This end of the row is fixed furniture — it should be the last
   thing to give, not the first. */
.topbar .who { font-size: 13px; color: #a9a396; display: flex; align-items: center; gap: 12px;
               flex: none; white-space: nowrap; }
.topbar .who form { margin: 0; }
/* A link-shaped button.
   The colours here were written for the dark topbar — grey going to cream —
   and every use on a light background has had to defend against them with an
   inline colour. One that did not, the Invoice link on a supplement round,
   turned near-white on hover and vanished. So the default is now the light
   background case, which is almost all of them, and the topbar states its own. */
.linkbtn {
  background: none; border: 0; color: var(--muted); cursor: pointer;
  font: inherit; font-size: 13px; padding: 0;
}
.linkbtn:hover { color: var(--graphite); text-decoration: underline; }

.topbar .linkbtn { color: #a9a396; }
.topbar .linkbtn:hover { color: var(--cream); }

/* Search, in the bar, on every page.
   flex: 0 1 250px — it may shrink when the nav needs the room but never grows
   into it, because the nav is how you get anywhere and this is only how you
   get somewhere faster. */
.topsearch { position: relative; flex: 0 1 250px; min-width: 0; margin: 0; }
/* .topbar in front of every one of these, and it is load-bearing.
   `input[type=search]` further down the file is (0,1,1) — an attribute
   selector counts as a class — so a bare `.topsearch input` tied with it and
   lost on source order. The result: a box that was paper-white with near-black
   text at rest, then turned dark on focus while the text stayed near-black,
   because only the background had been overridden. Black on black, the moment
   you clicked into it.

   Settled by making it the same white box as every other input in the app —
   which is what it has actually been showing — and saying so at a specificity
   that wins, so the focused state cannot drift away from the resting one. */
.topbar .topsearch input {
  width: 100%; height: 32px; font: inherit; font-size: 13px;
  padding: 0 30px 0 10px; border-radius: var(--radius);
  border: 1px solid #5a6066; background: var(--paper); color: var(--ink);
}
.topbar .topsearch input::placeholder { color: var(--muted); }
.topbar .topsearch input:focus {
  outline: none; background: var(--paper); color: var(--ink);
  border-color: var(--brass); box-shadow: 0 0 0 3px rgba(192,143,62,.35);
}
/* Safari draws its own clear button inside a type=search, on top of the hint. */
.topsearch input::-webkit-search-cancel-button { display: none; }
/* The shortcut, shown until you are using the box — at which point saying how
   to reach a box you are already in is just clutter. After the input in the
   HTML so a plain sibling selector can do it, with no script involved. */
.topsearch kbd {
  position: absolute; right: 7px; top: 50%; transform: translateY(-50%);
  font: 600 11px/1 ui-monospace, SFMono-Regular, Menlo, monospace;
  color: var(--muted); border: 1px solid var(--line); border-radius: 3px;
  padding: 3px 5px; pointer-events: none;
}
.topsearch input:focus ~ kbd { display: none; }
/* The box is narrow; its results need not be. Right-aligned so a wider menu
   grows back under the bar instead of off the edge of the window. */
.topsearch .tamenu { width: 360px; left: auto; right: 0; }
/* A 1100px window was 33px short of fitting the whole nav, so "Invoices" was
   cut off mid-word behind its scroller. The bar's own 28px gaps are the
   cheapest thing to spend: three of them, and nobody has ever asked for more
   air between the wordmark and the first link. */
@media screen and (max-width: 1200px) {
  .topbar { gap: 18px; }
  .topsearch { flex-basis: 170px; }
  .topsearch kbd { display: none; }
}
@media screen and (max-width: 900px) {
  .topsearch { flex-basis: 140px; }
  .topsearch .tamenu { width: 300px; }
}

/* Your account from the top bar, and sign out.
   A computer shows your name, as it always has. A phone shows a circle with
   your photo or initials in its place, and moves Sign out into More along with
   a "My account" link. Before this the phone hid your name and showed nothing
   instead, so your account page — where phone notifications are switched on —
   could not be reached from a phone at all.

   `a.whome` beats the older phone rule `.topbar .who > a { display: none }`,
   which would otherwise hide this link exactly the way it hid the name. */
.whome .avatar { display: none; }
.morephone { display: none; }
.morepanel form { margin: 0; }
.morepanel .morebtn {
  display: block; width: 100%; text-align: left; cursor: pointer;
  background: none; border: 0; font: inherit;
  color: #c9c3b6; font-size: 13.5px; padding: 7px 10px; border-radius: 5px; white-space: nowrap;
}
.morepanel .morebtn:hover { background: var(--graphite-2); color: var(--cream); }
@media screen and (max-width: 760px) {
  .topbar .who > a.whome { display: inline-flex; align-items: center; }
  .whome .whoname { display: none; }
  .whome .avatar { display: inline-grid; border-color: var(--graphite); box-shadow: 0 0 0 1.5px #5a6066; }
  .topbar .who > form { display: none; }
  .morephone { display: flex; flex-direction: column; gap: 1px; }
}

/* ---------- phones ----------
   On a phone the bar is two rows, not one squeezed row. On a single 375px row
   the fixed parts — helmet, bell, sign out — left the search box a 34px white
   square and the menu room for "Dashbo" before its scroller cut it off.

   Row one: helmet, search filling the width, bell, sign out.
   Row two: the menu as a strip that slides sideways, More at its end — the
   same gesture as the board, which was the part of the phone view that
   already made sense. All of it is inside this query, so the desktop bar is
   exactly as it was.

   Placed after every base rule for these elements on purpose. Several of them
   (.topsearch, .topbar .who) are defined further down the file than the older
   phone block above, and a media rule loses to a later rule of equal weight —
   part of why that block never fixed this. */
@media screen and (max-width: 760px) {
  .topbar { height: auto; flex-wrap: wrap; gap: 0 10px; padding: 7px 12px 0; }
  .topbar .brand { order: 1; }
  .topbar .topsearch { order: 2; flex: 1 1 auto; }
  .topbar .who { order: 3; gap: 10px; }
  .topbar .navwrap {
    order: 4; flex: 1 0 100%; margin: 7px -12px 0; padding: 3px 8px;
    border-top: 1px solid #33383c;
  }
  .topbar nav a, .more > summary { padding: 7px 10px; }
  /* The strip's right edge fades out, so a link half-hidden by the scroller
     reads as "there is more this way" instead of a stray letter — "Tasks" was
     cut to a lone T beside More. The padding is wider than the fade (32 vs
     30), so the last link can scroll fully clear of it: at 24 the end of
     "Invoices" still sat 6px inside the fade. */
  .topbar nav {
    padding-right: 32px;
    -webkit-mask-image: linear-gradient(to right, #000 calc(100% - 30px), transparent);
            mask-image: linear-gradient(to right, #000 calc(100% - 30px), transparent);
  }

  /* More sits at the right-hand end of the strip, so it opens leftward.
     Opening rightward from there put most of it off the edge of the phone. */
  .morepanel { left: auto; right: 0; }

  /* Search results and notifications, full width under the whole bar. Their
     own boxes stop being the anchor (position: static), so the bar is — it is
     sticky, and a sticky element anchors absolute children. Anchored to the
     narrow search box or the bell, both panels ran off one side of the
     screen: notifications already sat 25px off the left edge. */
  .topbar .topsearch.tawrap, .topbar .bellwrap { position: static; }
  .topbar .topsearch .tamenu { left: 8px; right: 8px; width: auto; top: calc(100% + 4px); }
  .topbar .bellpanel {
    left: 8px; right: 8px; width: auto; max-width: none; top: calc(100% + 4px);
  }

  /* Tabs slide sideways rather than stretching the page. Five tabs across is
     wider than a phone, and a row that would neither wrap nor scroll made the
     whole job page lay out at 603px — so the phone shrank it to fit, or cut
     off the right edge. */
  .tabs { overflow-x: auto; scrollbar-width: none; }
  .tabs::-webkit-scrollbar { display: none; }
  .tabs a { flex: none; white-space: nowrap; }

  /* Tables scroll inside themselves. display:block makes a table its own
     scroll box with no wrapper element, so all of them are covered at once;
     the ones already inside a .tablescroll are left to it. A table narrower
     than the screen stops stretching to full width here, which on a phone is
     rare and costs nothing. */
  main table:not(.tablescroll > table) {
    display: block; overflow-x: auto; -webkit-overflow-scrolling: touch;
  }
}

.pill {
  background: var(--brass); color: #1b1e20; border-radius: 10px;
  padding: 1px 7px; font-size: 11px; font-weight: 700;
}

.wrap { padding: 24px 20px 60px; max-width: 1180px; margin: 0 auto; }
.wrap-wide { max-width: none; padding-bottom: 8px; }

.pagehead {
  display: flex; align-items: baseline; gap: 16px;
  margin: 0 0 18px; flex-wrap: wrap;
}
.pagehead h1 { font-size: 22px; margin: 0; font-weight: 650; letter-spacing: -.01em; }
.pagehead .sub { color: var(--muted); font-size: 13px; }
.pagehead .spacer { flex: 1; }

/* The job header carries more than a title: who the contractor is, who is
   handling the claim, and which of the homeowner's addresses this one is. A
   homeowner can own eight houses, so the name alone does not identify the job
   and the address had to be scrolled to. */
/* Open / Closed on the task list. A pair rather than a dropdown: there are two
   answers, one is the default, and both counts are worth seeing without
   opening anything. */
.segtoggle {
  display: inline-flex; align-items: stretch;
  border: 1px solid var(--line); border-radius: 6px; overflow: hidden;
  background: #fff;
}
/* Buttons on the job panel (an in-page swap), links on the Tasks page (a
   different query). Same control either way, so they share the styling. */
.segtoggle button,
.segtoggle a {
  font: inherit; font-size: 11.5px; font-weight: 650;
  text-decoration: none;
  text-transform: none; letter-spacing: 0;
  padding: 3px 9px; border: 0; background: none; cursor: pointer;
  color: var(--muted); display: inline-flex; align-items: center; gap: 5px;
}
.segtoggle button + button,
.segtoggle a + a { border-left: 1px solid var(--line); }
.segtoggle button:hover:not(:disabled):not(.on),
.segtoggle a:hover:not(.on) { background: #f4f0e6; color: var(--ink); }
.segtoggle button.on,
.segtoggle a.on { background: var(--graphite); color: var(--cream); }
.segtoggle button:disabled { opacity: .45; cursor: default; }
/* The count rides inside the button, so the pair reads as "Open 3 / Closed 5"
   without a separate label. */
.segtoggle .n {
  font-size: 10.5px; opacity: .75;
  font-variant-numeric: tabular-nums;
}
.segtoggle button.on .n,
.segtoggle a.on .n { opacity: .9; }
/* An empty list, sitting between the toggle above it and the new-task form
   below. Left-aligned it read as a stray line of text hard against the panel
   edge, with nothing between it and the form's heading. Centred and ruled off,
   it reads as the answer to "what is in here" rather than as a label for what
   follows. */
/* One manager per row: their name, the roles they cover, and Save. Rows
   rather than a table because the middle column is a wrapping set of
   checkboxes, which a table cell handles badly at narrow widths. */
.manages {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px 14px;
  padding: 10px 0; border-top: 1px solid var(--line);
}
.manages:first-of-type { border-top: 0; padding-top: 0; }
.manages__who { font-weight: 650; min-width: 130px; }
.manages__roles { display: flex; flex-wrap: wrap; gap: 6px 14px; flex: 1; }
.manages__role {
  display: inline-flex; align-items: center; gap: 6px;
  font-size: 13px; text-transform: none; letter-spacing: 0; font-weight: 400;
  cursor: pointer; margin: 0;
}
.manages__role input { margin: 0; accent-color: var(--brass-dk); }

/* Whose list you are looking at. Sits with the Open/Closed pair, so it is
   sized to match rather than to match the buttons beside it. */
.whopick select {
  font: inherit; font-size: 13px;
  padding: 5px 9px; border: 1px solid var(--line); border-radius: 6px;
  background: #fff; max-width: 190px;
}

/* The bar under the supplement rounds. Reads as an instruction until
   something is ticked, then as a count. */
/* Says why an action is missing from a menu, rather than leaving a gap. */
/* Where a contractor stands with Armor. Two figures big enough to read at a
   glance — what they have paid and what is still out — over the detail. */
.finrow { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }
.finrow .finstat { min-width: 118px; }
.finstat {
  flex: 1; padding: 10px 12px;
  border: 1px solid var(--line); border-radius: 6px; background: #fbf7ee;
}
.finstat .v {
  display: block; font-size: 19px; font-weight: 650; color: var(--ink);
  font-variant-numeric: tabular-nums;
}
.finstat .k {
  display: block; margin-top: 2px;
  font: 700 9.5px "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
/* Money in reads green, money out reads red. */
.finstat--paid { background: var(--good-wash); border-color: #b9d6c0; }
.finstat--paid .v { color: var(--good); }
.finstat--owing { background: var(--danger-wash); border-color: #e8bdb8; }
.finstat--owing .v { color: var(--danger); }
/* The return is the contractor's number rather than Armor's, so it is brass
   rather than a verdict colour. */
.finstat--roi { background: var(--brass-wash); border-color: var(--brass-lt); }
.finstat--roi .v { color: var(--brass-dk); }
/* Credit is money Armor owes back, so it reads as the exception it is. */
.fincredit { color: var(--brass-dk); }

/* Credit sitting on a contractor's account, offered against this invoice.
   Never applied on its own — a total that moves without being asked is worse
   than one you have to set. */
.creditbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  padding: 10px 12px; margin-bottom: 14px;
  border: 1px solid var(--brass-lt); border-radius: 6px; background: var(--brass-wash);
  font-size: 13px;
}
.creditbar select {
  font: inherit; font-size: 13px; padding: 4px 8px;
  border: 1px solid var(--brass-lt); border-radius: 5px; background: #fff;
}
.creditraise .two { display: grid; grid-template-columns: 1fr 2fr; gap: 12px; margin-bottom: 12px; }
@media screen and (max-width: 620px) { .creditraise .two { grid-template-columns: 1fr; } }

.menunote {
  display: block; padding: 7px 12px;
  font-size: 12px; color: var(--muted); white-space: nowrap;
}

.billbar {
  display: flex; flex-wrap: wrap; align-items: center; gap: 10px;
  margin-top: 10px; padding: 9px 12px;
  border: 1px dashed var(--line); border-radius: 6px; background: #fbf7ee;
}
.billbar__msg { font-size: 12.5px; color: var(--muted); flex: 1; }
.billbar button[disabled] { opacity: .5; cursor: default; }
.roundtable .pickcol { width: 26px; padding-right: 0; }
.roundtable .pickcol input { margin: 0; accent-color: var(--brass-dk); }

.taskempty {
  margin: 0;
  padding: 16px 14px 18px;
  text-align: center;
  font-size: 12.5px; color: var(--muted);
  border-bottom: 1px dashed var(--line);
}

/* Stands in for `required` on the chip picker: present and focusable so the
   browser will validate and point its bubble at it, but taking no space. */
.assignguard {
  width: 0; height: 0; padding: 0; margin: 0; border: 0;
  opacity: 0; position: absolute; pointer-events: none;
}

/* Bigger than the text it separates, so the contractor and the claim handler
   read as two facts rather than one run-on line. */
.pagehead .dot {
  color: var(--muted); opacity: .55;
  font-size: 19px; line-height: 0; margin: 0 7px;
  position: relative; top: 2px;
}
/* Black, not brass. In brass it was the same colour as the contractor link
   beside it and the two ran together. */
.chtag {
  color: var(--ink); font-weight: 650; white-space: nowrap;
}
/* Full basis so it wraps onto its own line beneath the title row. It is last
   in the source for the same reason — earlier, it would push the buttons down. */
/* The row it sits on is full width; the LINK is only as wide as the address.
   Before this the whole strip to the right of the text was clickable, which
   put a link under the mouse on its way to Edit. */
/* The line under a job's name carries the two things read off a job while
   somebody is on the phone: where the house is, and where to send the mail.
   Side by side, wrapping onto two lines when the window is narrow rather than
   squeezing either one. */
.pagehead__addrline {
  flex-basis: 100%; margin-top: -8px;
  display: flex; flex-wrap: wrap; align-items: center; gap: 4px 18px;
}
/* Blue, in a faint blue pill. On a page of brass and grey the claim email
   blended into everything around it, and it is the one thing on this line
   somebody is hunting for with a phone to their ear. Blue because nothing
   else in the header is: it reads as its own thing at a glance. Red when
   there is no email, the same red as "No claim email" on the Clients list. */
.pagehead__claim {
  display: inline-flex; align-items: center; gap: 7px; font-size: 13px;
  padding: 2px 4px 2px 10px; border-radius: 12px;
  background: #eaf2fa; border: 1px solid #c5dbef;
}
.pagehead__claim .k {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--link-blue-dk);
}
.pagehead__claim a { color: var(--link-blue); font-weight: 650; }
.pagehead__claim a:hover { color: var(--link-blue-dk); }
.pagehead__claim--missing { background: var(--danger-wash); border-color: #e8bdb8; padding-right: 10px; }
.pagehead__claim--missing .k { color: var(--danger); }
.pagehead__claim a.pagehead__claimmissing { color: var(--danger); font-weight: 600; }

/* A small copy button beside a value. Quieter than a .btn because it sits
   inside a line of text, and it turns green for a moment to say it worked —
   copying is otherwise invisible, and "did that take?" means doing it twice. */
.copybtn {
  font: inherit; font-size: 11.5px; line-height: 1.5; padding: 0 7px; cursor: pointer;
  border: 1px solid var(--line); border-radius: 3px; background: var(--paper); color: var(--muted);
}
.copybtn:hover { border-color: var(--brass); color: var(--brass-dk); }
.copybtn.done { border-color: #b9d6c0; background: var(--good-wash); color: var(--good); }

/* A tick box with its words beside it, at reading size — not the small
   uppercase of a field label, which is what a bare <label> gets here. */
.checkline {
  display: flex; align-items: center; gap: 7px; margin: 9px 0 0; cursor: pointer;
  text-transform: none; letter-spacing: 0; font-size: 14px; font-weight: 500; color: var(--ink);
}

/* The onboarding flag on a contractor's page. */
.onboard { margin: 0 0 16px; }
.onboard a { font-weight: 600; margin-left: 6px; }
.pagehead__addr {
  display: inline-block; max-width: 100%;
  font-size: 13px; color: var(--muted); text-decoration: none;
}
.pagehead__addr:hover { color: var(--brass-dk); text-decoration: underline; }

/* ---------- controls ---------- */

.btn {
  display: inline-block; border: 1px solid var(--graphite);
  background: var(--graphite); color: var(--cream);
  padding: 7px 14px; border-radius: var(--radius); font-size: 14px;
  cursor: pointer; text-decoration: none; font-family: inherit;
}
.btn { transition: background-color .12s ease, border-color .12s ease,
                    box-shadow .12s ease, transform .08s ease; }
.btn:hover { background: var(--graphite-2); text-decoration: none; }
.btn:active { transform: translateY(1px); }
.btn.ghost { background: transparent; color: var(--graphite); border-color: var(--line); }
.btn.ghost:hover { background: var(--cream-2); }
.btn.brass {
  background: linear-gradient(var(--brass), #ab7d33);
  border-color: var(--brass-dk); color: #fff; font-weight: 600;
  text-shadow: 0 1px 0 rgba(0,0,0,.18);
}
.btn.brass:hover {
  background: linear-gradient(#d6a24a, #9c7028); color: #fff;
  box-shadow: 0 1px 0 rgba(255,255,255,.35) inset, 0 3px 10px rgba(138,100,32,.35);
}
.btn.danger { background: transparent; color: var(--danger); border-color: #e8bdb8; }
.btn.danger:hover { background: var(--danger-wash); border-color: var(--danger); }
.btn.sm { padding: 4px 9px; font-size: 12.5px; }

input[type=text], input[type=email], input[type=password], input[type=date],
input[type=search], select, textarea {
  width: 100%; padding: 7px 10px; font: inherit;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper); color: var(--ink);
}
input:focus, select:focus, textarea:focus {
  outline: 2px solid rgba(176,141,87,.45); outline-offset: -1px; border-color: var(--brass);
}
textarea { resize: vertical; min-height: 76px; }
label { display: block; font-size: 12px; text-transform: uppercase; letter-spacing: .07em;
        color: var(--muted); margin: 0 0 4px; font-weight: 600; }

/* The note under a field. Four places had already written this rule scoped to
   their own panel, identically; everywhere else `class="hint"` matched nothing
   and rendered as body copy — the same size and colour as the answer to the
   question above it, which is exactly what a hint must not be. The scoped
   copies are left alone: they say the same thing and win on specificity. */
.hint { display: block; margin-top: 4px; font-size: 11.5px; line-height: 1.45; color: var(--muted); }
label.hint { text-transform: none; letter-spacing: 0; font-weight: 400; }

.filters { display: flex; gap: 8px; align-items: flex-end; flex-wrap: wrap; margin-bottom: 16px; }
.filters .f { min-width: 150px; }
.filters .f.grow { flex: 1; min-width: 220px; }
/* The buttons at the end of a filter row travel together. On their own they
   wrap one at a time, stranding a single button on a line of its own. */
.filters .f.acts { display: flex; gap: 8px; min-width: 0; }

/* ---------- cards / panels ---------- */

.panel {
  background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: var(--shadow);
  margin-bottom: 18px;
}
.panel > h2 {
  margin: 0; padding: 11px 16px; font-size: 12px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .09em; color: var(--graphite);
  display: flex; align-items: center; gap: 10px;
  /* A brass rule under the heading, so a panel reads as a titled thing rather
     than a box with small grey text at the top of it. */
  border-bottom: 2px solid var(--brass-lt);
  background: linear-gradient(var(--brass-wash), var(--paper));
  border-radius: var(--radius) var(--radius) 0 0;
}
.panel > h2 .spacer { flex: 1; }
/* A small action in a panel heading. Deliberately not a .btn — a button's
   padding stretches the header and the panel stops lining up with its
   neighbours, so this sits at the heading's own weight. */
.panel > h2 .panelact {
  background: none; border: 0; padding: 0; cursor: pointer; font: inherit;
  font-size: 11.5px; letter-spacing: .06em; text-transform: uppercase;
  font-weight: 700; color: var(--brass-dk);
}
.panel > h2 .panelact:hover { text-decoration: underline; }
.panel .body { padding: 16px; }

.grid2 { display: grid; grid-template-columns: 1fr 1fr; gap: 14px; }
.grid3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: 14px; }
.grid4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: 14px; }
.grid-side { display: grid; grid-template-columns: minmax(0,1.55fr) minmax(0,1fr); gap: 18px; align-items: start; }
@media screen and (max-width: 900px) {
  /* minmax(0, 1fr), not 1fr. A bare 1fr means "never narrower than the widest
     thing inside", so one unwrappable row — the job tabs, a table — set the
     width of the whole page: 603px for a job on a 375px phone. The two-column
     rule above already uses minmax(0, …) for exactly this reason; the stacked
     one had lost it. */
  .grid2, .grid3, .grid4, .grid-side { grid-template-columns: minmax(0, 1fr); }
}

.kv { display: grid; grid-template-columns: 120px 1fr; gap: 6px 14px; font-size: 14px; }
.kv dt { color: var(--muted); font-size: 12px; text-transform: uppercase;
         letter-spacing: .06em; font-weight: 600; padding-top: 2px; }
.kv dd { margin: 0; }

/* ---------- tables ---------- */

table { width: 100%; border-collapse: collapse; font-size: 14px; }
th {
  text-align: left; font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--graphite-2); font-weight: 700; padding: 9px 16px;
  border-bottom: 2px solid var(--brass); background: var(--brass-wash);
}
td { padding: 10px 16px; border-bottom: 1px solid #eae4d6; vertical-align: middle; }
tbody tr:last-child td { border-bottom: 0; }
tbody tr:hover { background: var(--brass-wash); }
td.num { text-align: right; font-variant-numeric: tabular-nums; }

/* ---------- board ---------- */

.board {
  display: flex; gap: 12px; align-items: flex-start;
  overflow-x: auto; padding-bottom: 8px;
}
.col {
  flex: 0 0 258px;
  /* --stage is set inline from the column's own colour. */
  background: color-mix(in srgb, var(--stage, var(--brass)) 7%, var(--cream-2));
  border-top: 3px solid var(--stage, var(--brass));
  border: 1px solid var(--line); border-radius: var(--radius);
  /* Height comes from where the board actually starts, measured at runtime —
     a hardcoded offset breaks the moment the filter row wraps. */
  display: flex; flex-direction: column;
  max-height: calc(100vh - var(--board-top, 210px));
}
.col > header {
  padding: 9px 12px; border-bottom: 1px solid var(--line);
  display: flex; align-items: center; gap: 8px;
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  position: sticky; top: 0; border-radius: var(--radius) var(--radius) 0 0;
  color: var(--graphite);
  /* Mixed into paper, not the column beige: beige is already dark and muddy, so
     mixing into it swallowed the hue and every column came out the same colour.
     Against near-white the stage reads clearly and the text stays legible. */
  background:
    linear-gradient(color-mix(in srgb, var(--stage, var(--brass)) 46%, var(--paper)),
                    color-mix(in srgb, var(--stage, var(--brass)) 28%, var(--paper)));
  border-bottom-color: color-mix(in srgb, var(--stage, var(--brass)) 55%, var(--line));
}
.col > header .dot { width: 9px; height: 9px; border-radius: 50%; flex: 0 0 auto; }
.col > header .nm {
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0;
}
.col > header .n {
  margin-left: auto; flex: none; font-weight: 700; font-size: 11px; color: var(--graphite-2);
  background: var(--paper); border: 1px solid var(--line);
  border-radius: 999px; padding: 1px 8px; min-width: 22px; text-align: center;
}
.col .stack { padding: 8px; display: flex; flex-direction: column; gap: 8px; overflow-y: auto; flex: 1; min-height: 60px; }
.col { transition: box-shadow .12s ease; }
.col:hover { box-shadow: 0 2px 14px rgba(23,26,29,.09); }
.col.dragover {
  outline: 2px dashed var(--stage, var(--brass)); outline-offset: -3px;
  background: color-mix(in srgb, var(--stage, var(--brass)) 14%, var(--cream-2));
}

/* An empty column should look deliberately empty, not broken. Server-rendered
   rather than :empty — EJS leaves whitespace in the stack, so :empty never
   matches and the placeholder silently never appears. */
.colempty {
  margin: 6px 2px;
  border: 1px dashed color-mix(in srgb, var(--stage, var(--brass)) 40%, var(--line));
  border-radius: var(--radius); padding: 16px 10px; text-align: center;
  color: var(--muted); font-size: 12px;
}

.jobcard {
  background: var(--paper); border: 1px solid var(--line);
  border-left: 3px solid var(--stage, var(--brass));
  border-radius: var(--radius); padding: 9px 11px; cursor: grab;
  box-shadow: 0 1px 1px rgba(27,30,32,.04); display: block; color: inherit;
}
.jobcard {
  transition: transform .12s ease, box-shadow .12s ease,
              border-color .12s ease, background-color .12s ease;
}
.jobcard:hover {
  text-decoration: none; transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(23,26,29,.07), 0 8px 20px rgba(23,26,29,.10);
  border-color: color-mix(in srgb, var(--stage, var(--brass)) 55%, var(--line));
  border-left-color: var(--stage, var(--brass));
  background: color-mix(in srgb, var(--stage, var(--brass)) 5%, var(--paper));
}
.jobcard:active { transform: translateY(0); }
.jobcard.dragging { opacity: .4; }
.jobcard .who { font-weight: 650; font-size: 14px; margin-bottom: 2px; }
.jobcard .co { color: var(--muted); font-size: 12.5px; }
.jobcard .meta {
  margin-top: 7px; display: flex; gap: 8px; flex-wrap: wrap;
  font-size: 11px; color: var(--muted); align-items: center;
}
.tag {
  background: var(--brass-wash); border: 1px solid var(--brass-lt);
  border-radius: 3px; padding: 1px 6px; font-size: 11px; color: var(--brass-dk);
  font-weight: 600;
}
.tag.plain { background: var(--cream-2); border-color: var(--line); color: var(--graphite-2); font-weight: 400; }
.tag.good { background: var(--good-wash); border-color: #b9d6c0; color: var(--good); }
.tag.warn { background: var(--warn-wash); border-color: #e6cb95; color: var(--warn); }
.tag.bad  { background: var(--danger-wash); border-color: #e8bdb8; color: var(--danger); }
.tag.info { background: var(--info-wash); border-color: #b6d2dd; color: var(--info); }
/* NOT `.tag.warn`: there is a standalone `.warn` alert-box style further down
   with its own padding and font size, and class="tag warn" picks up both —
   which is why this chip came out much larger than the ones beside it. Its
   own name, so it cannot collide again. */
.tag.wait { background: var(--warn-wash); border-color: #e6cb95; color: var(--warn); }

/* ---------- notes ---------- */

/* Notes are cards, not rows divided by a rule.
   A 1px hairline read as one continuous block when you scrolled a long thread;
   2px and a darker colour did not fix it either. A line is a hint, a gap is a
   boundary — so separation now comes from the space between cards and the
   cream showing through behind them, which cannot be squinted past. */
.notelist { padding: 11px; background: var(--cream); }
.note {
  background: var(--paper); border: 1px solid #c9c0ab; border-radius: var(--radius);
  padding: 13px 15px; margin-bottom: 10px;
  box-shadow: 0 1px 2px rgba(23,26,29,.06);
}
.note:last-child { margin-bottom: 0; }
.note .head { display: flex; gap: 8px; align-items: baseline; font-size: 12px; color: var(--muted); margin-bottom: 5px; }
.note .head .author { font-weight: 700; color: var(--graphite); font-size: 13px; }
.note .head .spacer { flex: 1; }
.note .body { white-space: pre-wrap; font-size: 14.5px; line-height: 1.55; }
.note .mention { color: var(--brass-dk); font-weight: 650; background: #f3ead9; border-radius: 3px; padding: 0 3px; }
/* Unseen rides the left edge. It swaps the 1px card border for 3px of brass,
   nudging the content 2px right — invisible on a card, and cheaper than a
   pseudo-element that would have to track the border radius. */
.note.unseen { background: #fdf8ee; border-left: 3px solid var(--brass); }

.feed { font-size: 13px; }
.feed li { padding: 6px 0; border-bottom: 1px solid #ece7dc; color: var(--muted); list-style: none; }
.feed li:last-child { border-bottom: 0; }
.feed ul, .feed { margin: 0; padding: 0; }
.feed b { color: var(--graphite); font-weight: 650; }

.checks { display: grid; grid-template-columns: repeat(auto-fill, minmax(155px, 1fr)); gap: 6px; }
.checks label {
  display: flex; align-items: center; gap: 7px; text-transform: none;
  letter-spacing: 0; font-size: 14px; color: var(--ink); font-weight: 400;
  padding: 5px 8px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper); cursor: pointer; margin: 0;
}
.checks input { margin: 0; accent-color: var(--brass-dk); }

.empty { padding: 34px 16px; text-align: center; color: var(--muted); font-size: 14px; }
.alert {
  background: #f7ecea; border: 1px solid #dbc4bf; color: var(--danger);
  padding: 10px 14px; border-radius: var(--radius); margin-bottom: 16px; font-size: 14px;
}
.warn {
  background: #fdf6e6; border: 1px solid #e6d5ab; color: #7a5c1e;
  padding: 10px 14px; border-radius: var(--radius); margin-bottom: 14px; font-size: 13.5px;
}
.muted { color: var(--muted); }
.mono { font-family: "SFMono-Regular", Consolas, monospace; font-size: 13px; }
.stagechip {
  display: inline-flex; align-items: center; gap: 6px; font-size: 12px;
  font-weight: 650; padding: 3px 9px; border-radius: 11px; color: var(--graphite);
  background: color-mix(in srgb, var(--dot, var(--brass)) 15%, var(--paper));
  border: 1px solid color-mix(in srgb, var(--dot, var(--brass)) 40%, var(--line));
}
.stagechip .dot { width: 8px; height: 8px; border-radius: 50%; flex: none; }

/* ---------- login ---------- */

.loginwrap {
  min-height: 100vh; display: grid; place-items: center;
  background: var(--graphite);
}
.loginbox {
  width: 340px; background: var(--paper); padding: 30px;
  border-radius: var(--radius); box-shadow: 0 10px 40px rgba(0,0,0,.3);
  border-top: 3px solid var(--brass);
}
.loginbox .brand { color: var(--graphite); font-size: 13px; margin-bottom: 22px; display: block; }
.loginbox .field { margin-bottom: 14px; }
.loginbox .btn { width: 100%; text-align: center; }

/* ---------- mention autocomplete ---------- */

.mentionwrap { position: relative; }
.mentionmenu {
  position: absolute; z-index: 40; left: 0; right: 0; top: 100%;
  margin-top: 3px; background: var(--paper);
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 6px 20px rgba(27,30,32,.14); overflow: hidden;
  max-width: 320px;
}
.mentionitem {
  display: flex; gap: 9px; align-items: baseline; width: 100%;
  padding: 7px 11px; background: none; border: 0; cursor: pointer;
  font: inherit; text-align: left; color: var(--ink);
}
.mentionitem:hover, .mentionitem.on { background: var(--cream-2); }
/* Names in the picker read as plain text, not as links. Brass here looked
   like every name was already tagged. The brass belongs on an ACCEPTED
   mention in the note body, where it means something. */
.mentionitem .h { font-weight: 650; color: var(--ink); font-size: 13.5px; }
.mentionitem .n { color: var(--muted); font-size: 12.5px; }

/* ---------- my jobs: filters and pager ---------- */

.jobfilters {
  display: flex; gap: 8px; flex-wrap: wrap; align-items: center;
  padding: 11px 16px; border-bottom: 1px solid var(--line);
  background: var(--cream);
}
.jobfilters select {
  width: auto; min-width: 120px; max-width: 190px;
  padding: 5px 8px; font-size: 13px;
}
.jobfilters .clearf { font-size: 12.5px; color: var(--muted); }

.pager {
  display: flex; align-items: center; gap: 14px; justify-content: center;
  padding: 11px 16px; border-top: 1px solid var(--line);
  font-size: 13px;
}
.pager .count { color: var(--muted); font-variant-numeric: tabular-nums; }
.pager .off { color: var(--line-strong, #c6bfb0); }

/* ---------- tasks ---------- */

.task {
  display: flex; gap: 10px; align-items: flex-start;
  padding: 9px 16px; border-bottom: 1px solid #ece7dc;
}
.task:last-child { border-bottom: 0; }
.task form.tick { margin: 0; display: flex; }
.task .tickbox {
  width: 17px; height: 17px; border: 1.5px solid var(--line); border-radius: 3px;
  background: var(--paper); cursor: pointer; padding: 0; line-height: 1;
  color: var(--good); font-size: 12px; font-weight: 700;
}
.task .tickbox:hover { border-color: var(--brass); }
.task.done .title { text-decoration: line-through; color: var(--muted); }
.task .title { flex: 1; font-size: 14px; }
.task .when { font-size: 12px; color: var(--muted); white-space: nowrap; }
.task .when.overdue { color: var(--danger); font-weight: 650; }
.task .when.today { color: var(--brass-dk); font-weight: 650; }
.task .who { font-size: 11.5px; }

/* ---------- dashboard ---------- */

.tiles { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 18px; }
.tile {
  background: var(--paper); border: 1px solid var(--line); border-top: 3px solid var(--brass);
  border-radius: var(--radius); padding: 14px 16px; box-shadow: var(--shadow);
  transition: transform .12s ease, box-shadow .12s ease;
}
a.tile:hover, .tile:hover {
  transform: translateY(-1px);
  box-shadow: 0 2px 4px rgba(23,26,29,.07), 0 8px 20px rgba(23,26,29,.10);
}
.tile .v { font-size: 26px; font-weight: 680; letter-spacing: -.02em; font-variant-numeric: tabular-nums; }
.tile .k {
  font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--muted); font-weight: 700; margin-top: 3px;
}
.tile.warn-tile { border-top-color: var(--danger); }
.tile.warn-tile .v { color: var(--danger); }

/* Pipeline. The label sits above its own bar rather than in a fixed-width
   column: "Waiting on ITEL / Repairability Test" was never going to fit in
   108px, and a truncated label makes the chart useless. */
.pipeline { padding: 6px 0; }

.bar {
  display: grid; grid-template-columns: 1fr auto; gap: 2px 10px;
  padding: 7px 16px; font-size: 13px; text-decoration: none; color: inherit;
  transition: background-color .12s ease;
}
.bar:hover { background: var(--brass-wash); text-decoration: none; }
.bar .lab {
  color: var(--graphite-2); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.bar .n {
  grid-row: 1; grid-column: 2; text-align: right; font-weight: 700;
  font-variant-numeric: tabular-nums; color: var(--graphite);
}
.bar .track {
  grid-column: 1 / -1;
  background: var(--cream-2); border-radius: 3px; height: 10px; overflow: hidden;
  box-shadow: inset 0 1px 2px rgba(23,26,29,.07);
}
.bar .fill {
  display: block; height: 100%; border-radius: 3px;
  background: linear-gradient(90deg,
    color-mix(in srgb, var(--stage, var(--brass)) 78%, white), var(--stage, var(--brass)));
  transition: width .25s ease;
}
/* An empty column still draws its track, so the gaps in the pipeline read as
   gaps rather than as missing rows. */
.bar:has(.fill[style*="width: 0%"]) .lab,
.bar:has(.fill[style*="width: 0%"]) .n { color: var(--muted); font-weight: 500; }

.money { font-variant-numeric: tabular-nums; }
.money.gain { color: var(--good); font-weight: 650; }

/* ---------- photo / document links ---------- */

:root { --link-blue: #2f6fa8; --link-blue-dk: #24567f; }

.linkrow {
  display: flex; gap: 10px; align-items: center;
  padding: 10px 16px; border-bottom: 1px solid #ece7dc;
}
.linkrow:last-of-type { border-bottom: 0; }
.linkrow .ico {
  width: 26px; height: 26px; flex: 0 0 auto; border-radius: 4px;
  background: var(--cream-2); border: 1px solid var(--line);
  display: grid; place-items: center; font-size: 13px;
}
.linkrow .main { flex: 1; min-width: 0; }
.linkrow a.ext {
  color: var(--link-blue); text-decoration: underline;
  text-underline-offset: 2px; font-weight: 600; font-size: 14.5px;
}
.linkrow a.ext:hover { color: var(--link-blue-dk); }
.linkrow .url {
  display: block; color: var(--muted); font-size: 12px;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 100%;
}
.linkrow .by { font-size: 11.5px; color: var(--muted); white-space: nowrap; }

.addlink { display: flex; gap: 7px; align-items: flex-end; flex-wrap: wrap; }
.addlink .u { flex: 1; min-width: 210px; }
.addlink .l { width: 150px; }

.soon {
  font-size: 12.5px; color: var(--muted); padding: 0 16px 14px;
  border-top: 1px dashed var(--line); margin-top: 2px; padding-top: 11px;
}

/* ---------- job tabs ---------- */

.tabs {
  display: flex; gap: 2px; border-bottom: 1px solid var(--line);
  margin-bottom: 0; background: var(--cream-2);
  border: 1px solid var(--line); border-bottom: 0;
  border-radius: var(--radius) var(--radius) 0 0; padding: 5px 5px 0;
}
.tabs a {
  padding: 8px 15px; font-size: 13px; font-weight: 650; color: var(--muted);
  text-decoration: none; border-radius: var(--radius) var(--radius) 0 0;
  border: 1px solid transparent; border-bottom: 0; display: flex; align-items: center; gap: 7px;
}
.tabs a:hover { color: var(--graphite); text-decoration: none; background: #f1ece1; }
.tabs a.on {
  background: var(--paper); color: var(--ink);
  border-color: var(--line); margin-bottom: -1px; padding-bottom: 9px;
}
.tabs a .c {
  background: var(--cream); border: 1px solid var(--line); border-radius: 9px;
  padding: 0 6px; font-size: 11px; color: var(--muted); font-weight: 700;
}
.tabpanel {
  background: var(--paper); border: 1px solid var(--line); border-top: 0;
  border-radius: 0 0 var(--radius) var(--radius); box-shadow: var(--shadow); margin-bottom: 18px;
}

/* ---------- dropzone ---------- */

.dropzone {
  margin: 16px; border: 2px dashed var(--line); border-radius: var(--radius);
  padding: 26px 18px; text-align: center; background: var(--cream);
  transition: border-color .12s, background .12s;
}
.dropzone.over { border-color: var(--brass); background: #f7f0e2; }
.dropzone.busy { opacity: .65; pointer-events: none; }
.dropzone .big { font-size: 15px; font-weight: 600; margin-bottom: 3px; }
.dropzone .small { font-size: 12.5px; color: var(--muted); }
.dropzone input[type=file] { display: none; }
.dropzone .progress { height: 4px; background: var(--cream-2); border-radius: 2px; margin-top: 12px; overflow: hidden; }
.dropzone .progress span { display: block; height: 100%; width: 0; background: var(--brass); transition: width .2s; }
.dropzone [data-status] { font-size: 12.5px; color: var(--muted); margin-top: 8px; min-height: 16px; }
.dropzone [data-status].bad { color: var(--danger); }

/* ---------- photo grid ---------- */

.photogrid {
  display: grid; grid-template-columns: repeat(auto-fill, minmax(132px, 1fr));
  gap: 9px; padding: 16px;
}
.shot { position: relative; }
.shot img {
  width: 100%; aspect-ratio: 4/3; object-fit: cover; display: block;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--cream-2); cursor: zoom-in;
}
.shot img:hover { border-color: var(--brass); }
/* Always visible: a hover-only 21px target was effectively undeletable. */
.shot .rm {
  position: absolute; top: 6px; right: 6px;
  background: rgba(27,30,32,.72); color: #fff; border: 1px solid rgba(255,255,255,.35);
  border-radius: 4px; width: 26px; height: 26px; cursor: pointer;
  font-size: 16px; line-height: 1; padding: 0; opacity: .85;
}
.shot .rm:hover { opacity: 1; background: var(--danger); border-color: var(--danger); }

/* Ticking a photo into the report. Top-LEFT, opposite the delete button: the
   two are a few pixels apart on a small tile and one of them is destructive.

   Only while the report panel is open. A tick box on every photo all the time
   reads as something to do, and there is nothing for it to do — people asked
   what they were for. The panel sits directly before the grid, so its open
   state can drive them. */
.prdrop:not([open]) ~ .photogrid .pick { display: none; }
.shot .pick {
  position: absolute; top: 6px; left: 6px; z-index: 2;
  display: grid; place-items: center; width: 26px; height: 26px;
  background: rgba(27,30,32,.72); border: 1px solid rgba(255,255,255,.35);
  border-radius: 4px; cursor: pointer;
}
.shot .pick input { width: 15px; height: 15px; margin: 0; cursor: pointer; accent-color: var(--brass); }
/* The whole tile reads as chosen, not just the 15px box. */
.shot:has(.pick input:checked) img { border-color: var(--brass); box-shadow: 0 0 0 2px var(--brass); }
.shot .pick:has(input:checked) { background: var(--brass); border-color: var(--brass); }

/* ---------- the photo report panel ---------- */

/* Inset to match the Link toggle directly above it — 16px, the same as
   details.linkdrop. Flush against the panel edge it read as a stray line
   rather than as a control belonging to the tab. */
.prdrop { padding: 0 16px 14px; }
.prdrop > summary { list-style: none; cursor: pointer; padding: 12px 0 0; }
.prdrop > summary::-webkit-details-marker { display: none; }
.prdrop .inner {
  margin-top: 10px; padding: 14px;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--cream-2);
}
.prform .two { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 10px; }
.prform label { display: block; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted); margin-bottom: 3px; }
.prform input[type=text], .prform select { width: 100%; }
.propts { display: flex; flex-wrap: wrap; gap: 16px; margin: 12px 0 4px; }
.propts label {
  display: flex; align-items: center; gap: 6px; margin: 0;
  font-size: 12.5px; font-weight: 400; text-transform: none; letter-spacing: 0;
  color: var(--ink); cursor: pointer;
}
.propts input { accent-color: var(--brass); }
.prgo { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-top: 12px; }
.prgo .spacer { flex: 1; }
.prgo .muted { font-size: 12px; }
.prnote { margin: 10px 0 0; font-size: 11.5px; color: var(--muted); }


/* ---------- photo viewer ---------- */

/* A column: toolbar on top, stage taking the rest. The stage has a definite
   height (flex child of a fixed-height column), which is what lets the image's
   max-height actually resolve. The old version centred the image in a CSS grid,
   where the row track sizes to the item — so `max-height: 100%` had nothing
   definite to resolve against, the browser dropped it, and tall photos ran off
   the bottom of a fixed overlay with no way to scroll. */
.lightbox {
  position: fixed; inset: 0; z-index: 200;
  background: rgba(20,22,24,.94);
  display: flex; flex-direction: column;
}

.lbbar {
  display: flex; align-items: center; gap: 6px;
  padding: 9px 12px; background: rgba(0,0,0,.45);
  border-bottom: 1px solid rgba(255,255,255,.12); flex: none;
}
.lbbar .lbspacer { flex: 1; }
.lbbar .lbname {
  color: #e9e5dc; font-size: 13px; font-weight: 600;
  max-width: 40vw; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.lbbar button, .lbbar .lbdl {
  background: rgba(255,255,255,.08); border: 1px solid rgba(255,255,255,.18);
  color: #f2eee4; border-radius: var(--radius); cursor: pointer;
  font: inherit; font-size: 14px; line-height: 1; padding: 7px 11px;
  text-decoration: none;
  transition: background-color .12s ease, border-color .12s ease, transform .08s ease;
}
.lbbar button:hover, .lbbar .lbdl:hover {
  background: var(--brass); border-color: var(--brass); color: #1b1e20;
  transform: translateY(-1px); text-decoration: none;
}
.lbbar button:active, .lbbar .lbdl:active { transform: translateY(0); }
.lbbar button:disabled { opacity: .35; cursor: default; transform: none; background: rgba(255,255,255,.08); }
.lbbar button:disabled:hover { background: rgba(255,255,255,.08); border-color: rgba(255,255,255,.18); color: #f2eee4; }
.lbbar [data-close]:hover { background: var(--danger); border-color: var(--danger); color: #fff; }
.lbbar .lbzoom {
  color: #cfc9bd; font-size: 12.5px; font-variant-numeric: tabular-nums;
  min-width: 46px; text-align: center;
}

/* Toolbar, then a row: nav strip, stage, nav strip. The stage still gets a
   definite height, which is what keeps the image fitting. */
.lbbody { flex: 1; min-height: 0; display: flex; align-items: stretch; }

/* Full-height targets at the far edges. Big enough to hit without aiming, and
   clear of the photo so you are never clicking over what you are looking at. */
.lbnav {
  flex: none; width: 96px; border: 0; cursor: pointer;
  background: transparent; color: rgba(255,255,255,.45);
  font-size: 46px; line-height: 1; padding: 0;
  display: flex; align-items: center; justify-content: center;
  transition: background-color .14s ease, color .14s ease;
}
.lbnav span { display: block; transform: translateY(-2px); }
.lbnav:hover { color: #fff; }
.lbnav.prev:hover { background: linear-gradient(to right, rgba(176,141,87,.30), transparent); }
.lbnav.next:hover { background: linear-gradient(to left,  rgba(176,141,87,.30), transparent); }
.lbnav:active span { transform: translateY(-2px) scale(.88); }
.lbnav:focus-visible { outline: 2px solid var(--brass); outline-offset: -4px; color: #fff; }

.lbstage { flex: 1; min-width: 0; min-height: 0; position: relative; overflow: hidden; }
/* Absolutely positioned with equal insets and `margin: auto`, rather than
   centred with grid or flex. In grid the auto row track sizes to the image, so
   the image's own `max-height: 100%` resolves against the image and does
   nothing — which is how a 1400px photo rendered at 1400px inside a 751px
   stage. Against a positioned box the percentages resolve against the stage. */
.lbstage img {
  position: absolute; inset: 0; margin: auto;
  width: auto; height: auto;
  /* Not 100%: a little breathing room, without a calc that would fight the
     insets. */
  max-width: 94%; max-height: 94%;
  object-fit: contain; border-radius: 3px;
  transform-origin: center center;
  transition: transform .12s ease;
  user-select: none; -webkit-user-drag: none;
}
.lbstage.zoomed { cursor: grab; }
.lbstage.zoomed img { transition: none; }   /* dragging must track the pointer */
.lbstage.grabbing { cursor: grabbing; }

@media screen and (max-width: 700px) {
  .lbbar .lbname, .lbbar .lbdl { display: none; }
  /* Narrower strips on a phone, or they eat the photo. */
  .lbnav { width: 58px; font-size: 34px; }
}

/* ---------- doc rows ---------- */

.docrow { display: flex; gap: 11px; align-items: center; padding: 11px 16px; border-bottom: 1px solid #ece7dc; }
.docrow:last-of-type { border-bottom: 0; }
.docrow .ico { width: 30px; height: 30px; flex: 0 0 auto; border-radius: 4px;
  background: var(--cream-2); border: 1px solid var(--line); display: grid; place-items: center; font-size: 14px; }
.docrow .main { flex: 1; min-width: 0; }
.docrow .nm { font-weight: 600; font-size: 14px; display: block;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.docrow .sub { font-size: 11.5px; color: var(--muted); }

/* ---------- collapsible link toggle ---------- */

.linktoggle {
  display: inline-flex; align-items: center; gap: 5px; cursor: pointer;
  color: var(--link-blue); font-size: 12.5px; font-weight: 650;
  background: none; border: 0; padding: 0; font-family: inherit;
}
.linktoggle:hover { color: var(--link-blue-dk); text-decoration: underline; }
details.linkdrop { padding: 0 16px 14px; }
details.linkdrop > summary { list-style: none; cursor: pointer; padding: 12px 0 0; }
details.linkdrop > summary::-webkit-details-marker { display: none; }
details.linkdrop[open] > summary .linktoggle { color: var(--link-blue-dk); }
details.linkdrop .inner { padding-top: 11px; }

a.mention {
  color: var(--brass-dk); font-weight: 650; background: #f3ead9;
  border-radius: 3px; padding: 0 3px; text-decoration: none;
}
a.mention:hover { background: #ecdfc4; text-decoration: underline; }

/* ---------- avatars ---------- */

.avatar {
  display: inline-grid; place-items: center; border-radius: 50%;
  object-fit: cover; flex: 0 0 auto; vertical-align: middle;
  border: 1.5px solid var(--paper); box-shadow: 0 0 0 1px var(--line);
}
.avatar.initials { color: #fff; font-weight: 700; letter-spacing: .02em; line-height: 1; }
.crew { display: flex; align-items: center; }
.crew .avatar + .avatar { margin-left: -7px; }
.crew .more { font-size: 11px; color: var(--muted); margin-left: 5px; }

.avatarblock { display: flex; gap: 14px; align-items: center; }
.avatarblock .avatar { width: 68px; height: 68px; font-size: 24px; }

/* ---------- board card, richer ---------- */

.jobcard .addr { color: var(--muted); font-size: 12px; margin-top: 3px; line-height: 1.35; }
.jobcard .foot {
  margin-top: 8px; display: flex; align-items: center; gap: 8px;
  border-top: 1px solid #ece7dc; padding-top: 7px;
}
.jobcard .foot .spacer { flex: 1; }
.days {
  font-size: 11px; font-weight: 700; color: var(--muted);
  background: var(--cream-2); border: 1px solid var(--line);
  border-radius: 9px; padding: 1px 7px; white-space: nowrap;
}
.days.warm { color: var(--warn); background: var(--warn-wash); border-color: #e6cb95; }
.days.hot { color: var(--danger); background: var(--danger-wash); border-color: #e8bdb8; }

/* ---------- notification bell ---------- */

.bellwrap { position: relative; display: flex; align-items: center; }
#bell {
  background: none; border: 0; cursor: pointer; color: #c9c3b6;
  font-size: 17px; line-height: 1; padding: 6px 8px; border-radius: var(--radius);
  position: relative; display: flex; align-items: center;
}
#bell:hover { background: var(--graphite-2); color: var(--cream); }
#bell.has { color: var(--brass); }
#bellcount {
  position: absolute; top: 0; right: 0; background: var(--danger); color: #fff;
  border-radius: 9px; min-width: 16px; height: 16px; font-size: 10px; font-weight: 700;
  display: grid; place-items: center; padding: 0 4px; border: 1.5px solid var(--graphite);
}
/* Something new arrived while you were looking at the page. One swing, pivoting
   from the top the way a bell hangs — enough to catch the corner of your eye,
   and then still, because a thing that keeps moving while you read is a thing
   you learn to resent. The badge and the tab title say it too, so with reduced
   motion switched on nothing is lost by the swing not happening. */
#bell.ring { animation: bellring .9s ease-in-out 1; transform-origin: 50% 15%; }
@keyframes bellring {
  0%, 100% { transform: rotate(0); }
  15% { transform: rotate(16deg); }
  30% { transform: rotate(-14deg); }
  45% { transform: rotate(10deg); }
  60% { transform: rotate(-7deg); }
  75% { transform: rotate(3deg); }
}
@media (prefers-reduced-motion: reduce) {
  #bell.ring { animation: none; }
}

.bellpanel {
  position: absolute; top: calc(100% + 8px); right: 0; width: 380px; max-width: 92vw;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 12px 38px rgba(20,22,24,.28); z-index: 120; overflow: hidden;
  color: var(--ink);
}
.bellhead {
  display: flex; align-items: center; gap: 4px; padding: 9px 12px;
  border-bottom: 1px solid var(--line); background: var(--cream-2);
}
.bellhead a {
  font-size: 12px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); padding: 4px 9px; border-radius: 11px; text-decoration: none;
}
.bellhead a:hover { color: var(--graphite); text-decoration: none; background: #e2dccf; }
.bellhead a.on { background: var(--graphite); color: var(--cream); }
.bellhead .spacer { flex: 1; }
.bellhead .readall { text-transform: none; letter-spacing: 0; font-weight: 600; color: var(--link-blue); }

.belllist { max-height: 62vh; overflow-y: auto; }
.bellrow {
  display: flex; gap: 10px; align-items: flex-start; padding: 12px;
  border-bottom: 1px solid #ece7dc; text-decoration: none; color: inherit;
}
.bellrow:last-child { border-bottom: 0; }
.bellrow:hover { background: #f7f4ec; text-decoration: none; }
.bellrow.unseen { background: #fdfaf3; }
.bellrow .ic {
  width: 32px; height: 32px; flex: 0 0 auto; border-radius: var(--radius);
  background: var(--cream-2); border: 1px solid var(--line);
  display: grid; place-items: center; font-size: 14px;
}
.bellrow .bd { flex: 1; min-width: 0; }
.bellrow .t { display: block; font-weight: 650; font-size: 14px; line-height: 1.3; }
.bellrow .meta { display: block; font-size: 12px; color: var(--muted); margin-top: 2px; }
.bellrow .ex {
  display: block; margin-top: 7px; padding: 8px 10px; font-size: 13px; line-height: 1.45;
  background: var(--cream); border: 1px solid var(--line); border-radius: var(--radius);
  max-height: 92px; overflow: hidden;
}
.bellrow .ex .m { color: var(--link-blue); font-weight: 650; }
/* The row holds a link and a button side by side, so the padding moves onto
   the link and the row itself just lays them out. */
.bellrow { padding: 0; align-items: stretch; }
.bellrow .go {
  display: flex; gap: 10px; align-items: flex-start; flex: 1; min-width: 0;
  padding: 12px 4px 12px 12px; text-decoration: none; color: inherit;
}
.bellrow .go:hover { text-decoration: none; }

/* The unread dot is the read/unread control. Filled means unread and clicking
   marks it read; hollow means read and clicking puts it back. The hit area is
   deliberately larger than the dot — 9px is a fine indicator and a poor
   target. */
.bellrow .dot {
  flex: 0 0 auto; width: 30px; align-self: stretch; padding: 0; margin: 0;
  border: 0; background: none; cursor: pointer; position: relative;
}
.bellrow .dot::before {
  content: ''; position: absolute; top: 17px; left: 50%; margin-left: -4.5px;
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--link-blue); border: 2px solid var(--link-blue);
  box-sizing: border-box; transition: background .12s ease, border-color .12s ease;
}
.bellrow .dot.read::before { background: transparent; border-color: #c9c2b4; }
.bellrow .dot:hover::before { border-color: var(--link-blue); }
.bellrow .dot.read:hover::before { background: rgba(47,109,183,.18); }
.bellrow .dot:disabled { cursor: default; opacity: .5; }
.bellrow .dot:focus-visible { outline: 2px solid var(--link-blue); outline-offset: -3px; }
.bellempty { padding: 30px 16px; text-align: center; color: var(--muted); font-size: 13.5px; }

/* days-in-system chip on the job header */
.agechip {
  display: inline-flex; flex-direction: column; align-items: flex-end;
  border-left: 2px solid var(--brass); padding-left: 11px; line-height: 1.15;
}
.agechip .v { font-size: 19px; font-weight: 680; font-variant-numeric: tabular-nums; }
.agechip .k {
  font-size: 10px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--muted); font-weight: 700;
}

/* ---------- assignee chips ---------- */

.assignrow { display: flex; flex-wrap: wrap; gap: 7px; align-items: center; position: relative; }
.chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--cream-2); border: 1px solid var(--line);
  border-radius: 15px; padding: 3px 5px 3px 4px; font-size: 13px; max-width: 100%;
}
.chip .nm { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.chip .avatar { width: 21px !important; height: 21px !important; font-size: 9px !important; }
.chip .x {
  background: none; border: 0; cursor: pointer; color: var(--muted);
  font-size: 15px; line-height: 1; padding: 0 3px 0 1px; border-radius: 50%;
}
.chip .x:hover { color: var(--danger); }
.chip.none { background: none; border: 0; color: var(--muted); font-size: 13px; padding-left: 0; }

.addbtn {
  width: 27px; height: 27px; border-radius: 50%; cursor: pointer;
  border: 1.5px solid var(--brass); background: var(--paper); color: var(--brass-dk);
  font-size: 17px; line-height: 1; display: grid; place-items: center; padding: 0;
  font-family: inherit;
}
.addbtn:hover { background: var(--brass); color: #fff; }

.peoplepop {
  position: absolute; top: calc(100% + 7px); left: 0; z-index: 90;
  width: 250px; background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 10px 30px rgba(20,22,24,.22); overflow: hidden;
}
.peoplepop input[type=search] {
  border: 0; border-bottom: 1px solid var(--line); border-radius: 0;
  padding: 9px 12px; font-size: 14px;
}
.peoplepop input[type=search]:focus { outline: none; border-color: var(--brass); }
.peoplelist { max-height: 244px; overflow-y: auto; padding: 4px 0; }
.peoplelist button {
  display: flex; align-items: center; gap: 8px; width: 100%; text-align: left;
  background: none; border: 0; cursor: pointer; font: inherit; font-size: 14px;
  padding: 7px 12px; color: var(--ink);
}
.peoplelist button:hover { background: var(--cream-2); }
.peoplelist button .tick { margin-left: auto; color: var(--link-blue); font-weight: 700; visibility: hidden; }
.peoplelist button.on .tick { visibility: visible; }
.peoplelist .noresult { padding: 14px 12px; color: var(--muted); font-size: 13px; }

/* days-in-system, top right of a board card */
.jobcard { position: relative; }
.jobcard .age {
  position: absolute; top: 8px; right: 9px;
  font-size: 10.5px; font-weight: 700; color: var(--muted);
  background: var(--cream); border: 1px solid var(--line);
  border-radius: 9px; padding: 0 6px; line-height: 17px;
}
.jobcard .who { padding-right: 44px; }

/* ---------- in-page confirmation ---------- */

.confirmwrap {
  position: fixed; inset: 0; background: rgba(20,22,24,.55); z-index: 300;
  display: grid; place-items: center; padding: 24px;
}
.confirmbox {
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 18px 50px rgba(20,22,24,.35); padding: 22px; max-width: 380px; width: 100%;
  border-top: 3px solid var(--danger);
}
.confirmbox p { margin: 0 0 18px; font-size: 15px; line-height: 1.45; }
.confirmbox .row { display: flex; gap: 8px; justify-content: flex-end; }
.btn.danger-solid {
  background: var(--danger); border-color: var(--danger); color: #fff; font-weight: 600;
}
.btn.danger-solid:hover { background: #83301f; border-color: #83301f; }

/* ---------- search typeahead ---------- */

.tawrap { position: relative; }
.tamenu {
  position: absolute; top: calc(100% + 4px); left: 0; right: 0; z-index: 80;
  /* Said outright, not inherited. The top bar's box hangs its menu inside the
     dark bar, where the inherited colour is cream — on the menu's paper
     background that left every result name a pale ghost of itself, readable
     only where the bold match happened to override it. */
  color: var(--ink);
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(20,22,24,.2); overflow: hidden; max-height: 340px; overflow-y: auto;
}
.tarow {
  display: flex; gap: 9px; align-items: center; padding: 9px 12px;
  border-bottom: 1px solid #ece7dc; text-decoration: none; color: inherit;
}
.tarow:last-child { border-bottom: 0; }
.tarow:hover, .tarow.on { background: var(--cream-2); text-decoration: none; }
.tarow .dot { width: 8px; height: 8px; border-radius: 50%; flex: 0 0 auto; }
.tarow .bd { flex: 1; min-width: 0; }
.tarow .n { display: block; font-size: 14px; font-weight: 600; }
.tarow .n b, .tarow .s b { color: var(--brass-dk); }
.tarow .s {
  display: block; font-size: 12px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.tarow .st {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  color: var(--muted); background: var(--cream-2); border: 1px solid var(--line);
  border-radius: 9px; padding: 1px 7px; white-space: nowrap;
}
.tanone { padding: 14px 12px; color: var(--muted); font-size: 13px; }
/* Only the combined search sends more than one kind, so this appears only in
   the top bar menu — the page search boxes look exactly as they did. */
.tagroup {
  padding: 7px 12px 4px; font-size: 10.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); background: var(--cream-2);
  border-bottom: 1px solid var(--line);
}

/* A count column should sit next to its label, not drift to the far edge. */
th.tight, td.tight { width: 1%; white-space: nowrap; text-align: left; font-variant-numeric: tabular-nums; }

/* ---------- long lists scroll instead of stretching the page ---------- */

.scrollnotes { max-height: 62vh; overflow-y: auto; }
.scrollfeed { max-height: 320px; overflow-y: auto; }
.scrollnotes::-webkit-scrollbar, .scrollfeed::-webkit-scrollbar,
.belllist::-webkit-scrollbar, .peoplelist::-webkit-scrollbar { width: 9px; }
.scrollnotes::-webkit-scrollbar-thumb, .scrollfeed::-webkit-scrollbar-thumb,
.belllist::-webkit-scrollbar-thumb, .peoplelist::-webkit-scrollbar-thumb {
  background: #d3ccbd; border-radius: 5px; border: 2px solid var(--paper);
}

/* Staged uploads: a row per file with a description box, shown before anything
   is sent so a mistaken drop can be abandoned and each file can be labelled
   while the person still remembers what they picked. */
.upstage {
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper); margin-top: 12px; overflow: hidden;
}
.upstage__head {
  padding: 9px 14px; background: var(--cream); border-bottom: 1px solid var(--line);
  font: 700 11px "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: .12em; text-transform: uppercase; color: var(--muted);
}
.upstage__row {
  display: flex; gap: 10px; align-items: center;
  padding: 9px 14px; border-bottom: 1px solid var(--line);
}
.upstage__row .nm {
  flex: 0 1 200px; min-width: 0; font-size: 13.5px; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.upstage__row .sz {
  flex: 0 0 auto; font-size: 12px; color: var(--muted);
  font-variant-numeric: tabular-nums;
}
.upstage__row input { flex: 1; min-width: 0; padding: 5px 9px; font-size: 13px; }
.upstage__foot { display: flex; gap: 8px; padding: 11px 14px; }

/* Shown when an upload stopped part way through. The files are still held in
   the page, so the rest can go without picking them again — which matters
   because re-dropping the whole set would put a second copy of everything that
   already landed onto the job. */
.upretry {
  display: flex; flex-wrap: wrap; gap: 8px; align-items: center;
  margin-top: 10px; padding: 11px 14px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: #fbf7ee;
}

/* The money panel is two pots, not one list. Supplement and recoverable
   depreciation are separate money with separate lifecycles, and running them
   together as one column of rows invites reading a total across both.
   One base, two accents. Neither is the alarm red used for overdue and errors:
   nothing in here is wrong, and a panel that cries wolf gets ignored. */
.moneyblock {
  border: 1px solid var(--mb-line);
  border-left: 4px solid var(--mb-accent);
  border-radius: var(--radius);
  background: var(--mb-wash);
  padding: 12px 14px 4px;
  margin-top: 14px;
}
.moneyblock:first-child { margin-top: 0; }

.moneyblock__head {
  font: 700 10.5px "IBM Plex Mono", ui-monospace, monospace;
  letter-spacing: .13em; text-transform: uppercase;
  color: var(--mb-head);
  margin-bottom: 10px;
  display: flex; align-items: center; gap: 7px;
}
.moneyblock__head::before {
  content: ""; width: 7px; height: 7px; border-radius: 50%;
  background: var(--mb-accent); flex: none;
}
.moneyblock .kv dt { color: var(--mb-head); opacity: .82; }

/* Supplement: plum. The money Armor actually earns its fee on. */
.moneyblock--supp {
  --mb-accent: #7a4fa3;
  --mb-line:   #d5c4e4;
  --mb-wash:   #f8f4fc;
  --mb-head:   #5d3a80;
}
/* Recoverable depreciation: rust. A separate pot, still owed. */
.moneyblock--rd {
  --mb-accent: #b0532a;
  --mb-line:   #dcbfae;
  --mb-wash:   #fdf4ef;
  --mb-head:   #8f3f1c;
}

.rdcheck {
  display: flex; align-items: center; gap: 8px; margin: 0 0 10px;
  font-size: 13.5px; color: var(--ink); text-transform: none;
  letter-spacing: 0; font-weight: 400;
}
.rdcheck input { margin: 0; accent-color: #b0532a; }
.rdyes { color: var(--good); font-weight: 700; }
.rdno  { color: #b0532a; font-weight: 650; }

/* Where the flat RD fee stands, under the figures. "Released" alone does not
   say whether anybody charged for it, and that is the question worth answering
   without opening the invoice list. */
.rdstate {
  font-size: 12.5px; line-height: 1.5; color: var(--muted);
  margin: 2px 0 12px; padding-top: 10px;
  border-top: 1px solid var(--mb-line);
}
.rdstate a { color: var(--mb-head); font-weight: 650; }
/* Owed and not yet billed, so it carries the control to do something about it.
   Wraps rather than scrolls: the select can be long on a claim with several
   drafts open. */
.rdstate--due {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  color: var(--ink);
}
.rdstate--due select {
  font: inherit; padding: 4px 7px; max-width: 100%;
  border: 1px solid var(--mb-line); border-radius: 5px; background: #fff;
}

/* The question asked at the moment the box is ticked. Indented under it so it
   reads as part of that decision rather than a new section. */
.rdbill {
  margin: 0 0 12px; padding: 10px 12px;
  background: #fff; border: 1px solid var(--mb-line); border-radius: 6px;
}
.rdbill__q {
  margin: 0 0 8px; font-size: 12.5px; font-weight: 650; color: var(--mb-head);
}
.rdbill__opt {
  display: flex; align-items: flex-start; gap: 8px;
  font-size: 13px; line-height: 1.45; color: var(--ink);
  margin-bottom: 6px; cursor: pointer;
  /* These are sentences, not field labels. The edit form uppercases every
     label in it, which turns "Don't bill it yet" into shouting — the same
     reason .rdcheck opts out just above. */
  text-transform: none; letter-spacing: 0; font-weight: 400;
}
.rdbill__opt:last-child { margin-bottom: 0; }
.rdbill__opt input { margin: 2px 0 0; accent-color: #b0532a; flex: none; }

/* The other card on this claim, in its own panel above Stage. It is a fact you
   need on arrival — the RD is being worked separately, the supplement lives on
   the other card — so it is a row you can hit rather than a line to read. */
.claimjob {
  display: flex; align-items: baseline; gap: 9px;
  padding: 8px 10px; margin-bottom: 7px;
  border: 1px solid var(--line); border-radius: 6px;
  background: #fbf7ee; text-decoration: none;
}
.claimjob:hover { border-color: #c8a86a; background: #f7efdf; }
.claimjob__name { font-weight: 650; color: #6f5726; font-size: 13.5px; }
.claimjob__stage {
  font-size: 11.5px; color: var(--muted); margin-left: auto; white-space: nowrap;
}
/* A proposal, not a link. It states what matched and leaves the answer to a
   person — a claim number is typed by hand, and a money rule should not hang
   on a typo. */
.claimpropose {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px;
  font-size: 12.5px; line-height: 1.45; color: var(--ink);
  margin-bottom: 8px;
}
.claimpropose:last-child { margin-bottom: 0; }
.claimpropose a { color: #6f5726; font-weight: 650; }
/* The fee rule restated down in the RD block. Quieter than the states above it:
   it explains, it does not ask for anything. */
.rdstate--note { border-top: 0; padding-top: 0; margin-top: -4px; }

/* The fee is derived, so it explains itself rather than inviting a correction
   nobody can make. */
.feerule {
  font-size: 12px; line-height: 1.45; color: var(--muted);
  margin: 0 0 13px;
}
.feenote {
  display: block; font-size: 11.5px; color: var(--muted);
  text-transform: none; letter-spacing: 0; font-weight: 400;
}

/* A document's description, read-only on the row. Editing lives behind the row
   menu: a live input sitting on every row is one stray click from being wiped,
   and nobody notices until they go looking for it. */
.docrow .desc {
  display: block; margin-top: 4px; font-size: 13px; line-height: 1.4;
  font-weight: 650; color: var(--body, var(--ink));
}
.docrow .desc.muted { color: var(--muted); font-style: italic; font-weight: 400; }

.docedit {
  flex: 1 0 100%; margin-top: 10px; padding: 12px 14px;
  background: var(--cream); border: 1px solid var(--line);
  border-radius: var(--radius);
  display: grid; gap: 10px;
  grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
}
.docedit label {
  display: block; font-size: 11px; letter-spacing: .09em;
  text-transform: uppercase; color: var(--muted); font-weight: 600;
}
.docedit input { margin-top: 4px; font-size: 13.5px; }
.docedit .acts { grid-column: 1 / -1; display: flex; gap: 8px; }

/* The row menu opens downward out of its row, so a panel that clips its own
   corners swallows it. */
.docrow { flex-wrap: wrap; }

/* Contractor logo. Checkerboard behind it so a transparent PNG — which most
   logos are — reads as transparent rather than as a white box that will look
   wrong on a letterhead. */
.logobox {
  display: flex; align-items: center; justify-content: center;
  padding: 14px; margin-bottom: 12px; border-radius: var(--radius);
  border: 1px solid var(--line);
  background-color: var(--paper);
  background-image:
    linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%),
    linear-gradient(45deg, #eee 25%, transparent 25%, transparent 75%, #eee 75%);
  background-size: 14px 14px;
  background-position: 0 0, 7px 7px;
}
.logobox img { max-width: 100%; max-height: 110px; display: block; }
.logoform { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.logoform input[type=file] { flex: 1; min-width: 0; font-size: 12.5px; }

/* The header mark, small, beside the contractor name. */
.clientlogo {
  height: 34px; width: auto; max-width: 130px;
  object-fit: contain; margin-right: 4px;
}

/* ---------- credentials ---------- */

.cred { border: 1px solid var(--line); border-radius: var(--radius); background: var(--cream); margin-bottom: 11px; }
.credtop { display: flex; align-items: center; gap: 9px; padding: 9px 13px; border-bottom: 1px solid var(--line); }
.credlabel { font-weight: 700; font-size: 13px; letter-spacing: .02em; }
.credtop .spacer { flex: 1; }
.credfields { display: grid; grid-template-columns: 88px 1fr; gap: 7px 12px; margin: 0; padding: 12px 13px; font-size: 13.5px; }
.credfields dt { color: var(--muted); font-size: 11px; text-transform: uppercase; letter-spacing: .06em; font-weight: 700; padding-top: 3px; }
.credfields dd { margin: 0; display: flex; align-items: center; gap: 7px; flex-wrap: wrap; min-width: 0; }
.credfields dd .ext, .credfields dd .mono { overflow-wrap: anywhere; }
.credfields .pw { letter-spacing: .06em; }
.copy {
  background: var(--paper); border: 1px solid var(--line); border-radius: 3px;
  font: inherit; font-size: 11px; font-weight: 650; color: var(--muted);
  padding: 1px 7px; cursor: pointer;
}
.copy:hover { border-color: var(--brass); color: var(--brass-dk); }
.copy.done { color: var(--good); border-color: var(--good); }
.crededit { padding: 13px; }
.credactions { display: flex; gap: 7px; align-items: center; margin-top: 12px; }
.credactions .spacer { flex: 1; }

/* ---------- document viewer ---------- */

.docview { position: fixed; inset: 0; background: rgba(20,22,24,.86); z-index: 220; display: grid; place-items: center; padding: 26px; }
.docframe {
  background: var(--paper); border-radius: var(--radius); width: min(1000px, 100%);
  height: min(88vh, 100%); display: flex; flex-direction: column; overflow: hidden;
  box-shadow: 0 20px 60px rgba(0,0,0,.45);
}
.docframe header { display: flex; align-items: center; gap: 8px; padding: 10px 13px; border-bottom: 1px solid var(--line); background: var(--cream-2); }
.docframe header .nm { font-weight: 650; font-size: 14px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.docframe header .spacer { flex: 1; }
.docframe .stage { flex: 1; background: #55585b; display: grid; place-items: center; overflow: auto; }
.docframe .stage iframe { width: 100%; height: 100%; border: 0; background: #fff; }
.docframe .stage img { max-width: 100%; max-height: 100%; object-fit: contain; }
.docframe .noprev { background: var(--paper); padding: 34px; border-radius: var(--radius); text-align: center; max-width: 380px; }
.docframe .noprev p { margin: 0 0 8px; }

/* ---------- admin-only management area ---------- */

.lockbar {
  background: #f3ece0; border-bottom: 1px solid var(--line);
  padding: 11px 16px; font-size: 13px; color: #6b5628; line-height: 1.5;
}
.lockbar strong { color: #4d3d17; }
.tabs a[href*="tab=mgmt"].on { box-shadow: inset 0 -2px 0 var(--brass); }

.rolechip {
  font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
  border-radius: 9px; padding: 2px 8px; border: 1px solid var(--line); background: var(--cream-2);
}
.rolechip.r-admin { background: #efe2c9; border-color: #d9c69c; color: #6b5320; }
.rolechip.r-manager { background: #dfe8e2; border-color: #b9cdc0; color: #33553f; }

/* logo drops in at src/public/logo.svg; the wordmark stands in until then */
a.brand { display: flex; align-items: center; gap: 9px; text-decoration: none; }
a.brand:hover { text-decoration: none; }
.brandmark { height: 34px; width: auto; display: block; }
.brand .slash { color: var(--brass); }

.rolechip.r-finance { background: #dde5ef; border-color: #b6c6da; color: #2f4a68; }
.rolechip.r-estimator { background: #ece7db; border-color: #d3c9b3; color: #5b5140; }
.rolechip.r-claim_handler { background: #e8e2ee; border-color: #cabfd8; color: #4c3f61; }

/* ---------- tables that outgrow their panel ---------- */

/* A panel is a fixed-width box; a wide table inside it used to spill past the
   border rather than scroll. Wrap any wide table in .tablescroll. */
.tablescroll { overflow-x: auto; -webkit-overflow-scrolling: touch; }
.tablescroll > table { min-width: 100%; }

/* A report can run to hundreds of rows. Left alone that makes one enormous
   page where the schedule controls underneath are a long way from the data,
   and the column headings scroll out of sight exactly when you need them to
   read a number. The table gets its own scroll and the headings stay put. */
.reportscroll { max-height: 62vh; overflow-y: auto; }
.reportscroll thead th {
  position: sticky; top: 0; z-index: 2;
  background: var(--brass-wash);
}

/* Column rules. Without them a wide report is a field of numbers and it is
   genuinely hard to be sure which column a figure belongs to once you are a
   few rows down, which is the moment a money report gets misread. */
.reportscroll th + th,
.reportscroll td + td { border-left: 1px solid var(--line); }

/* Totals pinned to the bottom of the scroller, so the sum stays visible while
   you read the rows above it. */
.reportscroll tfoot td {
  position: sticky; bottom: 0; z-index: 2;
  background: var(--cream-2);
  border-top: 2px solid var(--brass);
  font-weight: 700; color: var(--ink);
}

.memberstable td, .memberstable th { white-space: nowrap; }
.memberstable td:nth-child(2) { max-width: 260px; overflow: hidden; text-overflow: ellipsis; }
.memberstable td.actions { text-align: right; width: 1%; }
.nowrap { white-space: nowrap; }
.rolepick { width: auto; min-width: 132px; padding: 4px 7px; font-size: 13px; }

/* Panels clip their own corners so nothing can visually escape the rounding.
   .popout opts out: anything that opens an absolutely-positioned menu inside a
   panel gets clipped by this and lands invisible inside the panel's own box.
   That is what happened to the assignee picker — the search field opened, and
   the results were drawn underneath the panel edge where nobody could see
   them. A panel with a popup in it cannot also clip. */
.panel { overflow: hidden; }
.panel.popout { overflow: visible; }
/* A table row menu opens downward out of its row. Inside a panel that
   clips, it lands underneath the panel edge instead of on top of it,
   which is what made the task list kebab look like it opened inside
   the box. Any panel holding a row menu has to stop clipping too. */
.panel:has(.rowmenu) { overflow: visible; }
/* Same problem, same fix, for the address suggestions. Marking each panel
   `popout` by hand is how the address field ended up clipped while the
   assignee picker next to it was fine — one got remembered and one did not.
   Matching on the menu itself means any panel that grows a typeahead stops
   clipping without anyone having to notice. */
.panel:has(.tamenu) { overflow: visible; }

/* ---------- admin role preview ---------- */

.previewbar {
  background: #f3ead9; border: 1px solid #e0cfa6; color: #6b5320;
  border-radius: var(--radius); padding: 10px 14px; margin-bottom: 14px; font-size: 13.5px;
}
.previewbar strong { color: #4d3d17; }
.previewbar a { margin-left: 8px; font-weight: 650; }

.rolepeek { display: flex; gap: 5px; align-items: center; flex-wrap: wrap; margin-bottom: 16px; }
.rolepeek .lbl {
  font-size: 11px; text-transform: uppercase; letter-spacing: .08em;
  color: var(--muted); font-weight: 700; margin-right: 4px;
}
.rolepeek a {
  font-size: 12.5px; font-weight: 650; padding: 4px 11px; border-radius: 13px;
  border: 1px solid var(--line); background: var(--paper); color: var(--muted); text-decoration: none;
}
.rolepeek a:hover { border-color: var(--brass); color: var(--brass-dk); text-decoration: none; }
.rolepeek a.on { background: var(--graphite); border-color: var(--graphite); color: var(--cream); }

/* ---------- column money subtotal + hidden columns ---------- */

.colmoney {
  display: flex; align-items: baseline; gap: 8px;
  padding: 6px 12px; border-bottom: 1px solid var(--line);
  background: var(--paper); font-size: 11px; color: var(--muted);
}
.colmoney .v { margin-left: auto; font-weight: 700; color: var(--graphite); font-variant-numeric: tabular-nums; }

.col.closedcol { background: #e9e5da; }
.col.closedcol > header { background: #e9e5da; color: var(--muted); }
.hiddencards { padding: 34px 14px; text-align: center; }
.hiddencards p { margin: 0 0 11px; font-size: 12.5px; color: var(--muted); line-height: 1.45; }

.jobcard .amt {
  margin-top: 4px; font-size: 13.5px; font-weight: 680;
  font-variant-numeric: tabular-nums; color: var(--graphite);
}
.jobcard .badge {
  display: inline-flex; align-items: center; gap: 4px;
  font-size: 10.5px; color: var(--muted); background: var(--cream-2);
  border: 1px solid var(--line); border-radius: 9px; padding: 0 6px; line-height: 16px;
  white-space: nowrap; font-variant-numeric: tabular-nums;
}
.jobcard .badge svg { flex: none; display: block; }

/* ---------- column ordering ---------- */

.collist { padding: 5px 0; }
.colrow {
  display: flex; align-items: center; gap: 10px; padding: 9px 14px;
  border-bottom: 1px solid #ece7dc; background: var(--paper); cursor: grab;
}
.colrow:hover { background: #f7f4ec; }
.colrow.dragging { opacity: .4; }
.colrow .grip { color: #cfc7b6; letter-spacing: -4px; font-size: 15px; cursor: grab; }
.colrow .swatch { width: 11px; height: 11px; border-radius: 50%; flex: 0 0 auto; }
.colrow .nm { font-weight: 600; font-size: 14px; }
.colrow .spacer { flex: 1; }
.coledit { padding: 14px; background: var(--cream); border-bottom: 1px solid var(--line); }

/* ---------- row action menus ---------- */

.rowmenu { position: relative; display: inline-block; }
.kebab {
  background: none; border: 1px solid transparent; border-radius: var(--radius);
  cursor: pointer; color: var(--muted); font-size: 17px; line-height: 1;
  padding: 2px 7px; font-family: inherit;
}
.kebab:hover { background: var(--cream-2); border-color: var(--line); color: var(--graphite); }
.menulist {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 60;
  min-width: 172px; background: var(--paper); border: 1px solid var(--line);
  border-radius: var(--radius); box-shadow: 0 10px 28px rgba(20,22,24,.22);
  overflow: hidden; padding: 4px 0; text-align: left;
}
.menulist.up { top: auto; bottom: calc(100% + 4px); }
.menulist a,
.menulist button {
  display: block; width: 100%; text-align: left; padding: 7px 13px;
  font: inherit; font-size: 13.5px; color: var(--ink); background: none;
  border: 0; cursor: pointer; text-decoration: none; white-space: nowrap;
}
.menulist a:hover, .menulist button:hover { background: var(--cream-2); text-decoration: none; }
.menulist button.danger { color: var(--danger); }
.menulist button.danger:hover { background: #f7ecea; }
.menulist .sep { height: 1px; background: var(--line); margin: 4px 0; }
.menulist form { margin: 0; }

/* Dim a row by colour, never by opacity. Opacity applies to every descendant,
   so a dropdown opened inside a faded row renders see-through and nothing —
   not z-index, not a background — can bring it back. */
tr.dimmed td,
tr.dimmed td a,
tr.dimmed td strong { color: var(--muted); font-weight: 500; }
tr.dimmed .avatar { filter: grayscale(1); opacity: .6; }
tr.dimmed .menulist,
tr.dimmed .menulist a,
tr.dimmed .menulist button { color: var(--ink); font-weight: 400; }
tr.dimmed .menulist button.danger { color: var(--danger); }

/* ---------- sign-in branding ---------- */

.loginbrand {
  display: flex; flex-direction: column; align-items: center; gap: 10px;
  margin-bottom: 22px;
}
.loginbrand img { height: 76px; width: auto; display: block; }
.loginbrand .wordmark {
  font-weight: 700; letter-spacing: .16em; text-transform: uppercase;
  font-size: 13px; color: var(--graphite);
}
.loginbrand .slash { color: var(--brass); }

/* ---------- pagination ---------- */

.pager {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap;
  padding: 11px 16px; border-top: 1px solid var(--line);
}
.pager .spacer { flex: 1; }
.pager .pg {
  min-width: 28px; text-align: center; padding: 4px 8px; border-radius: var(--radius);
  border: 1px solid var(--line); background: var(--paper); color: var(--muted);
  font-size: 13px; font-weight: 600; text-decoration: none;
}
.pager .pg:hover { border-color: var(--brass); color: var(--brass-dk); text-decoration: none; }
.pager .pg.on { background: var(--graphite); border-color: var(--graphite); color: var(--cream); }
.pager .gap { color: var(--muted); padding: 0 2px; }
.pager .btn.off { opacity: .4; pointer-events: none; }

/* ---------- vault folders ---------- */

.folders { padding: 6px; display: grid; gap: 6px; }

.folder {
  position: relative;
  display: flex; align-items: center; gap: 12px;
  padding: 12px 13px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper);
}
.folder { transition: border-color .12s ease, background-color .12s ease, box-shadow .12s ease; }
.folder:hover {
  border-color: var(--brass); background: var(--brass-wash);
  box-shadow: 0 2px 10px rgba(23,26,29,.07);
}
.folder .fico { font-size: 22px; line-height: 1; flex: none; }
.folder .fmain { flex: 1; min-width: 0; display: grid; gap: 3px; }
.folder .fname { font-weight: 650; font-size: 14.5px; color: var(--ink); text-decoration: none; }
.folder:hover .fname { color: var(--brass-dk); }

/* The card is a div, not a link, so the kebab inside it is valid markup and
   clickable. The name link is stretched back over the whole card instead, so
   the card still opens the folder — with the kebab lifted above it. */
.fname.stretch::after { content: ''; position: absolute; inset: 0; border-radius: inherit; }
.folder .rowmenu, .folder .fcount { position: relative; z-index: 1; }

.folder .fdesc {
  font-size: 12.5px; color: var(--muted);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.folder .fcount {
  flex: none; min-width: 30px; text-align: center; font-weight: 650; font-size: 13px;
  color: var(--muted); background: var(--cream); border: 1px solid var(--line);
  border-radius: 999px; padding: 2px 9px;
}
.fwho { display: flex; align-items: center; gap: 4px; flex-wrap: wrap; }

/* Role checkboxes: the access list, on the folder forms. */
.chks { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 6px; }
.chk {
  display: inline-flex; align-items: center; gap: 5px;
  padding: 4px 9px; border: 1px solid var(--line); border-radius: 999px;
  background: var(--paper); font-size: 12.5px; cursor: pointer; user-select: none;
}
.chk:hover { border-color: var(--brass); }
.chk input { margin: 0; accent-color: var(--brass); }
/* Admin is checked and disabled — a folder no one can administer strands its files. */
.chk.fixed { cursor: default; color: var(--muted); background: var(--cream); }
.chk.fixed:hover { border-color: var(--line); }

.foldedit {
  padding: 12px 13px; margin-top: -2px;
  border: 1px solid var(--brass); border-radius: var(--radius);
  background: var(--cream);
}
.foldedit .row1 { display: grid; grid-template-columns: 1fr 1.4fr; gap: 7px; }
.foldedit .row2 {
  display: flex; align-items: center; gap: 5px; flex-wrap: wrap; margin-top: 8px;
}
.foldedit .row2 .lbl { font-size: 12.5px; color: var(--muted); margin-right: 2px; }
.foldedit .row2 .spacer { flex: 1; }

/* Breadcrumb back to the shelf, in the page heading. */
.pagehead .back { color: var(--muted); text-decoration: none; }
.pagehead .back:hover { color: var(--brass-dk); }
.pagehead .crumbsep { color: var(--line); margin: 0 2px; }

@media screen and (max-width: 720px) {
  .foldedit .row1 { grid-template-columns: 1fr; }
}

/* ---------- confirmation banner ---------- */

/* Sticky, because half the things that raise one redirect to an anchor —
   /jobs/12#money — and the browser jumps straight past a banner sitting at the
   top of the page. The message was being written and never read. */
.flash {
  position: sticky; top: 10px; z-index: 40;
  display: flex; align-items: center; gap: 11px;
  padding: 11px 14px; margin: 0 0 18px;
  border: 1px solid var(--good); border-left-width: 4px;
  border-radius: var(--radius); background: var(--good-wash);
  color: #2c5c36; font-size: 14px; font-weight: 550;
  box-shadow: var(--shadow);
}
.flash .ico {
  flex: none; width: 20px; height: 20px; border-radius: 50%;
  background: var(--good); color: #fff; font-weight: 700; font-size: 12px;
  display: grid; place-items: center; line-height: 1;
}
.flash .x {
  margin-left: auto; background: none; border: 0; cursor: pointer;
  font-size: 20px; line-height: 1; color: inherit; opacity: .55; padding: 0 2px;
}
.flash .x:hover { opacity: 1; }
.flash.bad { border-color: var(--danger); background: var(--danger-wash); color: #7e2a21; }
.flash.bad .ico { background: var(--danger); }

/* ---------- report builder ---------- */

.chk.picked { background: var(--brass-wash); border-color: var(--brass); color: var(--brass-dk); font-weight: 600; }
.chk .dot { width: 8px; height: 8px; border-radius: 50%; display: inline-block; }
/* Filter lists (22 stages) get their own scroll rather than pushing the form
   off the screen. */
.chks.scrolly { max-height: 190px; overflow-y: auto; padding: 2px; }

/* A wide report has to scroll inside its panel — the page itself must never
   scroll sideways. */
.tablescroll { overflow-x: auto; }

.pwmatch {
  margin: 6px 0 0; font-size: 12.5px; color: var(--danger);
}

/* ---------- show/hide password ---------- */

.pwwrap { position: relative; display: block; }
.pwwrap input { padding-right: 62px; }
.pwshow {
  position: absolute; right: 5px; top: 50%; transform: translateY(-50%);
  background: none; border: 0; cursor: pointer; padding: 4px 7px;
  font: inherit; font-size: 12px; font-weight: 650; color: var(--muted);
  text-transform: uppercase; letter-spacing: .06em; border-radius: var(--radius);
}
.pwshow:hover { color: var(--brass-dk); background: var(--brass-wash); }
.pwshow:focus-visible { outline: 2px solid var(--brass); outline-offset: 1px; }

/* ---------- document preview toolbar ---------- */

/* These are .btn.ghost, whose hover fills with --cream-2 — which is exactly the
   colour of the toolbar behind them, so hovering changed nothing visible. Give
   them their own surface and the same brass hover the rest of the app uses. */
.docframe header .btn {
  background: var(--paper); border-color: var(--line); color: var(--graphite);
  font-weight: 600;
  transition: background-color .12s ease, border-color .12s ease,
              color .12s ease, box-shadow .12s ease, transform .08s ease;
}
.docframe header .btn:hover {
  background: var(--brass-wash); border-color: var(--brass); color: var(--brass-dk);
  transform: translateY(-1px);
  box-shadow: 0 2px 8px rgba(23,26,29,.14);
}
.docframe header .btn:active { transform: translateY(0); box-shadow: none; }
.docframe header .btn:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; }

/* Close is the one that discards what you are looking at, so it reads red on
   hover rather than brass — the same distinction the row menus make. */
.docframe header [data-close]:hover {
  background: var(--danger-wash); border-color: var(--danger); color: var(--danger);
}

/* A `.body` inside a tab panel had NO padding rule at all — `.panel .body`
   only matches inside `.panel`, and a tab panel is `.tabpanel`. So the note
   compose box sat hard against the panel edge. This is the missing rule, not an
   override of an existing one; the earlier `.panel .body.notecompose` attempt
   could never have matched. */
.tabpanel > .body { padding: 16px; }

/* And a little extra for the compose box specifically, so the label lines up
   with the tab labels above it rather than sitting inside them. */
.tabpanel > .body.notecompose { padding: 15px 22px 18px; }

/* The CompanyCam link at the top of the Photos tab. A contractor's whole photo
   set lives behind it while the uploads below are usually a few stragglers, so
   it gets weight rather than sitting as another quiet row. */
.linkrow.big {
  margin: 0 0 14px; padding: 13px 16px;
  border: 1px solid var(--brass-lt); border-left: 4px solid var(--brass);
  border-radius: var(--radius); background: var(--brass-wash);
  transition: border-color .12s ease, box-shadow .12s ease;
}
.linkrow.big:hover { border-color: var(--brass); box-shadow: 0 2px 10px rgba(23,26,29,.08); }
.linkrow.big .ico { background: var(--paper); border-color: var(--brass-lt); }
.linkrow.big a.ext { font-size: 15.5px; }

/* A contractor's name in the page subtitle is a link, but at 13px in muted grey
   it read as plain text — you only discovered it by hovering. Gold, a little
   heavier, and carrying its underline at rest so it announces itself as
   clickable before the cursor gets there. */
.pagehead .sub a {
  color: var(--brass-dk); font-weight: 650; font-size: 14px;
  text-decoration: underline;
  text-decoration-color: var(--brass-lt);
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
  transition: color .12s ease, text-decoration-color .12s ease;
}
.pagehead .sub a:hover {
  color: var(--brass); text-decoration-color: var(--brass);
  text-decoration-thickness: 2px;
}

/* The claim number gets read down the phone to carriers, so it is set large
   enough to find at a glance and to keep your place in while speaking. Tabular
   figures stop the digits shifting width, and selecting the number picks up the
   number alone rather than the label with it. */
.claimno { display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap; }
.claimno .k { color: var(--muted); font-size: 12px; text-transform: uppercase;
              letter-spacing: .04em; }
.claimno .v { font-size: 25px; font-weight: 650; line-height: 1.25;
              letter-spacing: .02em; font-variant-numeric: tabular-nums;
              user-select: all; word-break: break-all; }

/* Choosing a stage moves the job, so the select should fill the panel rather
   than leave a gap where the Move button used to sit. */
.autostage select { flex: 1; min-width: 0; }
.stagemoving { font-size: 12.5px; color: var(--muted); white-space: nowrap; }

/* ---------- long notes fold to three lines ---------- */

/* A mask rather than a gradient overlay: the note has one background when read
   and another when unseen, and a solid overlay would have to know which. Fading
   the text itself works on both and needs no colour at all. */
.note .body.clamped {
  max-height: calc(14.5px * 1.55 * 3);
  overflow: hidden;
  cursor: pointer;
  -webkit-mask-image: linear-gradient(to bottom, #000 58%, rgba(0,0,0,.15) 88%, transparent 100%);
          mask-image: linear-gradient(to bottom, #000 58%, rgba(0,0,0,.15) 88%, transparent 100%);
}

.notemore {
  /* Centred: the fade runs the full width of the note, so the invitation to
     open it belongs under the middle of it rather than tucked at one edge. */
  display: flex; align-items: center; justify-content: center; gap: 5px;
  margin: 5px auto 0; padding: 3px 12px;
  background: none; border: 1px solid transparent; border-radius: 999px;
  color: var(--brass-dk); font: inherit; font-size: 12.5px; font-weight: 650;
  letter-spacing: .01em; cursor: pointer;
}
.notemore:hover { color: var(--brass-dk); border-color: var(--brass); background: var(--cream-2); }
.notemore:focus-visible { outline: 2px solid var(--link-blue); outline-offset: 1px; }
.notemore svg { transition: transform .15s ease; }
.notemore.open svg { transform: rotate(180deg); }

/* Editing swaps the body for a textarea. The fold control belongs to the body,
   so it goes with it — without this it sits under the edit box offering to
   expand something that is not on screen. */
.note [data-noteedit="view"][hidden] ~ .notemore { display: none; }

/* ---------- task description and inline edit ---------- */

/* The description carries what the task is for. Under the title, quieter, so
   the list still scans as a list of things to do rather than a wall of prose. */
/* Darker than the "Assigned to" line above it. Both were --muted, which ran the
   two together into one grey block — the description is content and the
   assignment is a label, and they should not read as the same thing. Still
   short of the title, so the order stays title, who, detail. */
.taskdesc {
  display: block; margin-top: 4px; font-size: 12.5px; line-height: 1.5;
  color: var(--graphite-2); white-space: pre-wrap;
}
.task.done .taskdesc { text-decoration: none; }

/* The row is a flex line; the edit form takes the whole width of it rather than
   being squeezed into the column the title occupied. */
.task { flex-wrap: wrap; }
.task.editing { background: var(--cream); }
.taskform { flex: 0 0 100%; margin: 6px 0 2px; display: grid; gap: 8px; }
.taskform label {
  display: block; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted); margin-bottom: 3px;
}
.taskform .two { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.taskform textarea { resize: vertical; }
.taskform .acts { display: flex; gap: 8px; }

/* ---------- task priority and stacked detail ---------- */

/* Title, then who it is for, then what it is for — each on its own line. The
   assignee used to be a tag sitting inline after the title, which wrapped
   mid-name in a narrow panel and put half of "Hunter Huntsman" on each line. */
.task .title .tt { display: block; font-weight: 650; color: var(--graphite); }
.task.done .title .tt { font-weight: 600; }
.taskwho {
  display: block; margin-top: 2px; font-size: 12.5px; color: var(--muted);
}
/* The name never splits. "Hunter" on one line and "Huntsman" on the next is
   the exact thing this was meant to fix — the label can wrap instead.
   Brass and clickable, like every other person's name in here: it goes to their
   profile, and the underline on hover is what says so before you click. */
.taskwho a {
  color: var(--brass-dk); font-weight: 700; white-space: nowrap;
  text-decoration: none;
}
.taskwho a:hover, .taskwho a:focus-visible { text-decoration: underline; }
.task.done .taskwho a { color: var(--muted); }
/* No user id to link to — still the name, just not clickable. */
.taskwho b { color: var(--graphite); font-weight: 700; white-space: nowrap; }

/* Only tasks somebody deliberately marked carry a flag. Colouring all of them
   would tell you nothing. */
.prio {
  flex: 0 0 auto; align-self: flex-start;
  font-size: 10.5px; font-weight: 800; text-transform: uppercase;
  letter-spacing: .06em; padding: 2px 9px; border-radius: 999px;
  border: 1px solid transparent; white-space: nowrap;
}
/* Red, green, blue. Orange was the first choice for medium and it sank into the
   brass and cream the whole interface is built from — a warning colour that
   does not stand out is not doing its job. These three are far enough apart to
   read at a glance and none of them is a house colour. */
.prio.high   { color: #fff; background: var(--danger); }
.prio.medium { color: #fff; background: var(--good); }
.prio.low    { color: #fff; background: var(--link-blue); }

/* The tickbox picks up the same colour, so the priority is legible from the
   left edge when you are scanning down a column of them. */
.task.p-high   .tickbox { border-color: var(--danger); }
.task.p-medium .tickbox { border-color: var(--good); }
.task.p-low    .tickbox { border-color: var(--link-blue); }

/* A finished task is history. Leaving it shouting HIGH in red keeps drawing the
   eye to something nobody has to do any more. */
.task.done .prio { opacity: .45; }
.task.done .tickbox { border-color: var(--line); }

/* A shortened login URL is short, but "short" depends on the host and a row
   should never be at the mercy of one. Ellipsis is the backstop; the full URL
   is on the link's title and the Copy button either way. */
.credfields .ext {
  display: inline-block; max-width: 30ch; overflow: hidden;
  text-overflow: ellipsis; white-space: nowrap; vertical-align: bottom;
}

/* ---------- supplement rounds ---------- */

.moneyblock__head .spacer { flex: 1; }

.roundtable { width: 100%; border-collapse: collapse; font-size: 13px; }
.roundtable th, .roundtable td { padding: 5px 0; text-align: left; vertical-align: top; }
.roundtable thead th {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .07em;
  color: var(--mb-head); opacity: .8; border-bottom: 1px solid var(--mb-line);
  padding-bottom: 4px;
}
.roundtable .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.roundtable tbody tr + tr td { border-top: 1px solid var(--mb-line); }
.roundtable tbody td:first-child { padding-right: 10px; }

.roundtable .rseq { font-weight: 700; color: var(--mb-head); }
.roundtable .rdate { display: block; font-size: 11.5px; color: var(--muted); }
/* The note is why the round exists — which line items came back — so it wraps
   rather than being cut off. */
.roundtable .rnote { display: block; font-size: 11.5px; color: var(--graphite-2); }

.roundtable tfoot th, .roundtable tfoot td {
  border-top: 2px solid var(--mb-line); padding-top: 6px;
  font-weight: 700; color: var(--mb-head);
}
.roundtable tfoot th { font-size: 11px; text-transform: uppercase; letter-spacing: .06em; }

.roundform { display: grid; gap: 8px; margin: 10px 0 6px; }
.roundform .two { display: grid; grid-template-columns: 1fr 1fr; gap: 8px; }
.roundform label {
  display: block; font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--mb-head); opacity: .85; margin-bottom: 3px;
}
.roundform .acts { display: flex; gap: 8px; }

/* Two independent records of one number disagreeing. Amber rather than red:
   it is very often a round nobody has entered yet, not a mistake — worth
   noticing, not worth alarming anyone. */
.mismatch {
  display: block; margin-top: 3px;
  font-size: 11.5px; font-weight: 650; line-height: 1.35;
  color: var(--warn); font-variant-numeric: tabular-nums;
}
.roundtable .driftrow td { border-top: 0; padding-top: 2px; }
.roundtable .driftrow .mismatch { margin-top: 0; }

/* ---------- invoices ---------- */

.roundacts { white-space: nowrap; }
.roundacts form { display: inline; }
.invmake { color: var(--brass-dk); font-size: 11.5px; font-weight: 700; margin-right: 6px; }
.invmake:hover { color: var(--brass); text-decoration: underline; }
.invlink {
  color: var(--brass-dk); font-size: 11.5px; font-weight: 700; margin-right: 6px;
  text-decoration: none;
}
.invlink:hover { color: var(--brass); text-decoration: underline; }

/* The invoice document.
   Brass and graphite, the house colours, because it goes out with the logo on
   it. A banded header, a ruled table and one heavy bar for the balance — the
   shape every invoice worth looking at uses, and the only three things a
   contractor actually reads: who it is from, what for, how much. */
.doc {
  background: #fff; color: #1b1e21;
  border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: var(--shadow); max-width: 820px; margin: 0 0 18px; overflow: hidden;
}

.doc__band {
  background: var(--graphite); color: #fff;
  padding: 30px 34px; display: flex; align-items: center; gap: 24px; flex-wrap: wrap;
}
/* The branding is the first thing anyone should see. It was competing with the
   contact details for the same visual weight and losing. */
.doc__brand { display: flex; align-items: center; gap: 16px; }
.doc__brand img { height: 62px; width: auto; display: block; }
/* Direct child only. This was catching every span inside the header, so the
   contractor's phone number and email were being set at 27px alongside the
   company name they were meant to sit quietly under. */
.doc__brand > span {
  font-size: 27px; font-weight: 750; letter-spacing: -.015em; line-height: 1.1;
}
.doc__contact {
  margin: 0 0 0 auto; display: flex; gap: 28px; flex-wrap: wrap;
  font-size: 11.5px; line-height: 1.5;
}
.doc__contact dt {
  color: var(--brass-lt); font-size: 9.5px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .1em; margin-bottom: 2px;
}
.doc__contact dd { margin: 0; color: #e9e5dc; }

.doc__stamp {
  display: flex; align-items: flex-end; justify-content: space-between;
  gap: 24px; padding: 26px 34px 18px; flex-wrap: wrap;
}
/* Boxes, so it is obvious at a glance where one thing stops and the next
   starts, rather than four paragraphs of text running together. */
.doc__cards { display: flex; gap: 14px; padding: 0 34px 16px; flex-wrap: wrap; }
.doc__cards .card { flex: 1; min-width: 220px; }

/* The property, on its own line above the table. Stacked, not in a row with
   anything else, so nothing in a money column can look attached to it. */
.doc__property {
  padding: 0 34px 16px; display: flex; flex-direction: column; gap: 1px;
}
.doc__property .doc__label { margin-bottom: 4px; }
.card {
  border: 1px solid #e2ddd2; border-radius: 4px;
  background: #fbfaf7; padding: 14px 16px;
}
.doc__label {
  font-size: 9.5px; font-weight: 800; text-transform: uppercase; letter-spacing: .16em;
  color: var(--brass-dk); margin-bottom: 5px;
}
.doc__who { font-size: 16px; font-weight: 750; letter-spacing: -.01em; }
.doc__who.sm { font-size: 14.5px; }
.doc__sub { font-size: 12.5px; color: #55595e; line-height: 1.5; }

.doc__word {
  font-size: 34px; font-weight: 800; letter-spacing: .16em;
  color: var(--brass); line-height: 1;
}
.doc__no {
  font-size: 11px; letter-spacing: .22em; color: #7b7f84;
  margin: 4px 0 14px; font-variant-numeric: tabular-nums;
}
.doc__no sup { font-size: 8px; }
.doc__meta { margin: 0; display: grid; gap: 4px; justify-items: end; }
.doc__meta div { display: flex; gap: 12px; align-items: baseline; }
.doc__meta dt { font-size: 10px; text-transform: uppercase; letter-spacing: .1em; color: #8a8e93; }
.doc__meta dd { margin: 0; font-size: 12.5px; font-weight: 650; font-variant-numeric: tabular-nums; }

.doc__lines { width: 100%; border-collapse: collapse; font-size: 13px; }
.doc__lines thead th {
  background: var(--brass); color: #fff;
  font-size: 10px; font-weight: 800; text-transform: uppercase; letter-spacing: .12em;
  padding: 10px 14px; text-align: left;
}
.doc__lines thead th:first-child { padding-left: 34px; }
.doc__lines thead th:last-child  { padding-right: 34px; }
.doc__lines .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.doc__lines tbody td { padding: 12px 14px; vertical-align: top; }
.doc__lines tbody td:first-child { padding-left: 34px; }
.doc__lines tbody td:last-child  { padding-right: 34px; font-weight: 700; }
/* Banded rows. On a document that gets printed and emailed on, a rule between
   every line is heavier than it needs to be. */
.doc__lines tbody tr:nth-child(odd) { background: #f7f5f0; }
.doc__lines .li { display: block; font-weight: 700; }
.doc__lines .ld {
  display: block; font-size: 11.5px; color: #62666b; line-height: 1.5; margin-top: 2px;
  max-width: 46ch;
}

.doc__foot { display: flex; gap: 14px; padding: 22px 34px 28px; flex-wrap: wrap; align-items: flex-start; }
.doc__breakdown { flex: 1; min-width: 260px; display: grid; gap: 12px; }
.doc__working {
  font-size: 12.5px; line-height: 1.65; white-space: pre-wrap;
  font-variant-numeric: tabular-nums;
}
.doc__note { font-size: 12px; line-height: 1.6; color: #4a4e53; white-space: pre-wrap; }

.doc__totals { min-width: 270px; padding-bottom: 14px; }
.doc__totals dl { margin: 0 0 12px; display: grid; gap: 6px; }
.doc__totals dl div { display: flex; justify-content: space-between; gap: 24px; font-size: 13px; }
.doc__totals dt { color: #62666b; }
.doc__totals dd { margin: 0; font-weight: 650; font-variant-numeric: tabular-nums; }
/* Credit settled against this document. Named rather than folded into the
   amount paid, because the contractor did not pay it and, if they query the
   difference, the number here is what they look up. */
.doc__totals .credit dt, .doc__totals .credit dd { color: var(--danger); }

.doc__due {
  background: var(--graphite); color: #fff;
  display: flex; justify-content: space-between; align-items: baseline; gap: 20px;
  padding: 12px 18px; border-radius: 3px;
  font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .12em;
}
.doc__due .v {
  color: var(--brass-lt); font-size: 20px; letter-spacing: 0;
  font-variant-numeric: tabular-nums;
}

.card.pay { display: flex; flex-direction: column; align-items: flex-start; gap: 8px; }
.paybtn {
  display: inline-block; background: var(--brass); color: #fff;
  padding: 9px 20px; border-radius: 3px; text-decoration: none;
  font-size: 12px; font-weight: 800; text-transform: uppercase; letter-spacing: .1em;
}
.paybtn:hover { background: var(--brass-dk); color: #fff; text-decoration: none; }
/* Shown even with nowhere to point yet: a contractor who cannot find a pay
   button assumes they missed it, and asks. */
.paysoon { font-size: 12px; color: #8a8e93; font-style: italic; }

.doc__rule {
  border-top: 3px solid var(--brass);
  background: #f7f5f0; padding: 11px 34px;
  font-size: 11px; color: #62666b; text-align: center;
}

.invedit { display: grid; gap: 12px; }
.invedit .three { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.invstatus { display: flex; gap: 9px; align-items: center; margin-top: 16px;
             padding-top: 14px; border-top: 1px solid var(--line); }

/* What comes out of the printer is the document and nothing else. */
/* The paper has no margin, so the browser has nowhere to print its own header
   and footer — the date and time, the tab title ("… · Armor CRM") and this
   page's web address. On a document going to an insurer under a contractor's
   name, all of that told the reader which software wrote it. The margin moves
   inside the page instead (body padding below), so nothing sits at the very
   edge of the paper where a printer cannot reach.

   .flash too: after an RD release is raised the page says "RD requested set
   to $X on <homeowner>", and that banner would have printed across the top
   of the document. */
@page { size: letter; margin: 0; }
@media print {
  .noprint, .topbar, .panel.noprint, footer, .flash { display: none !important; }
  body { background: #fff; padding: 0.45in 0.5in; }
  main.wrap { padding: 0; margin: 0; max-width: none; }
  .doc { border: 0; box-shadow: none; max-width: none; margin: 0; border-radius: 0; }

  /* One sheet of letter paper, laid out the way it looks on screen.
     Paper is narrower than the screen version — 7.5in inside the margins,
     against 820px — so the footer's two columns used to wrap: the totals
     dropped under the notes and, on a full RD release, onto a page of their
     own. Here the columns may not wrap, the totals bar is a little tighter so
     it fits beside the notes, and the vertical spacing is trimmed so the
     longest RD release there is (four lines, a price breakdown and the full
     standard note) fits on one sheet with room to spare.

     Every selector starts `.doc` on purpose. The RD-release rules
     (.doc--theirs …, .doc__biglogo) come later in this file, and a print rule
     of equal weight would lose to them. */
  .doc .doc__band, .doc.doc--theirs .doc__band { padding: 18px 30px; }
  .doc .doc__biglogo { height: 96px; }
  .doc .doc__brand img { height: 52px; }
  .doc .doc__stamp { padding: 16px 30px 10px; }
  .doc .doc__word { font-size: 28px; }
  .doc .doc__cards, .doc .doc__property { padding: 0 30px 10px; }
  .doc .card { padding: 10px 14px; }
  .doc .doc__lines thead th { padding: 8px 12px; }
  .doc .doc__lines tbody td { padding: 8px 12px; }
  .doc .doc__lines thead th:first-child, .doc .doc__lines tbody td:first-child { padding-left: 30px; }
  .doc .doc__lines thead th:last-child, .doc .doc__lines tbody td:last-child { padding-right: 30px; }
  .doc .doc__foot { flex-wrap: nowrap; gap: 12px; padding: 14px 30px 10px; }
  .doc .doc__breakdown { min-width: 0; gap: 8px; }
  .doc .doc__totals { flex: 0 0 auto; min-width: 0; padding-bottom: 10px; }
  .doc .doc__working { font-size: 11.5px; line-height: 1.5; }
  .doc .doc__note { font-size: 11px; line-height: 1.45; }
  .doc .doc__due { padding: 10px 14px; font-size: 10px; letter-spacing: .08em; gap: 14px; }
  .doc .doc__due .v { font-size: 18px; }
  .doc .doc__rule { padding: 8px 30px; }
  /* If a document ever is too long for one sheet, it breaks between blocks —
     never through the totals, a card, or a line item. */
  .doc .doc__totals, .doc .doc__due, .doc .card, .doc .doc__lines tr { break-inside: avoid; }
  /* Backgrounds are what make this document look like anything, so keep them. */
  .doc__band, .doc__band::before, .doc__lines thead th, .doc__due, .doc__rule,
  .card, .paybtn, .doc__lines tbody tr:nth-child(odd) { -webkit-print-color-adjust: exact; print-color-adjust: exact; }
}

/* ---------- settings ---------- */
.setgrid { display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr)); gap: 14px; }
.setgrid .wide { grid-column: 1 / -1; }
.setgrid .hint { display: block; margin-top: 4px; font-size: 11.5px; color: var(--muted); }

/* ---------- RD release form ---------- */
.rdrelease summary {
  cursor: pointer; font-weight: 650; font-size: 13.5px; color: var(--brass-dk);
  padding: 4px 0;
}
.rdrelease[open] summary { margin-bottom: 12px; }
.rdform { display: grid; gap: 12px; }
.rdform .three { display: grid; grid-template-columns: repeat(3, 1fr); gap: 10px; }
.rdform label {
  display: block; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted); margin-bottom: 3px;
}
.rdform textarea { resize: vertical; }
.rdform .acts { display: flex; gap: 8px; }
.rdform .opt {
  font-weight: 600; text-transform: none; letter-spacing: 0;
  color: var(--brass-dk); font-size: 10px;
}
.rdform .hint { display: block; margin-top: 4px; font-size: 11px; color: var(--muted); }

/* ---------- an RD release wears the contractor's colours ---------- */

/* The whole document swaps palette. Armor's brass and graphite are Armor's; a
   release going to an insurer under someone else's letterhead should not carry
   them at all. --brand comes from the contractor record, defaulting to a navy
   blue because that is what most roofing contractors use. */
.doc--theirs { --brand: #1b3a6b; }

/* The band is white with a diagonal wedge of the contractor's colour across the
   left. The logo then sits on white whatever colour it is — Exchange Roofing's
   mark is navy on navy otherwise, and the roof in it simply disappears. */
.doc--theirs .doc__band {
  background: #fff; position: relative; overflow: hidden;
  padding: 26px 34px; align-items: center;
}
.doc--theirs .doc__band::before {
  content: ''; position: absolute; inset: 0;
  background: var(--brand);
  clip-path: polygon(0 0, 60% 0, 44% 100%, 0 100%);
}
.doc--theirs .doc__brand,
.doc--theirs .doc__biglogo { position: relative; z-index: 1; }
.doc--theirs .doc__word { color: var(--brand); }
.doc--theirs .doc__label { color: var(--brand); }
.doc--theirs .doc__lines thead th { background: var(--brand); }
.doc--theirs .doc__due { background: var(--brand); }
.doc--theirs .doc__due .v { color: #fff; }
.doc--theirs .doc__rule { border-top-color: var(--brand); }
.doc--theirs .doc__contact dt { color: rgba(255,255,255,.72); }

/* The logo does the work in the header, because the contractor's name is
   almost always inside it. The typed name shrinks to a block of contact
   details on the left rather than competing with it. */
.doc__whoblock { display: flex; flex-direction: column; gap: 1px; font-size: 12px; line-height: 1.5; }
.doc__whoblock .n { font-size: 17px; font-weight: 750; letter-spacing: -.01em; margin-bottom: 3px; }
.doc__biglogo {
  margin-left: auto; height: 132px; width: auto; max-width: 340px;
  object-fit: contain; display: block;
}

/* Brand colour picker: the swatch, a hex box, and the screen dropper. */
.colorrow { display: flex; gap: 7px; align-items: center; margin-top: 7px; }
.hexbox { width: 96px; font-family: "IBM Plex Mono", ui-monospace, monospace; font-size: 12.5px; }

/* Invoice list: where each kind gets raised. */
.makerow { display: flex; gap: 14px; flex-wrap: wrap; }
.make {
  flex: 1; min-width: 250px; display: flex; flex-direction: column; gap: 6px;
  align-items: flex-start; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--cream); padding: 12px 14px;
}
.make strong { font-size: 13.5px; }
.make .muted { font-size: 12px; line-height: 1.45; }
.invedit .hint { display: block; margin-top: 4px; font-size: 11.5px; color: var(--muted); }

/* ---------- invoices on a job, grouped by kind ---------- */

/* Each kind is its own block with real space around it. Sharing one continuous
   run of rows made a fee Armor is owed and a release the contractor sends to an
   insurer read as one list of six things, which is exactly what the split was
   meant to stop. The gap does more work here than the heading does. */
.invgroup {
  margin: 16px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper); overflow: hidden;
}
/* flow-root, so the first group's top margin stays INSIDE the panel instead of
   collapsing out through it. Without this the margin escapes and the green bar
   ends up flush against the panel edge — white below the last group, none above
   the first, which is what it looked like. */
.invtab { display: flow-root; }

/* The heading carries the colour, not a tag inside it. Everything on this page
   is cream and brass, so a green strip and a blue one are the fastest way to
   tell at a glance which of the two you are looking at — a small coloured chip
   inside an otherwise identical block was not doing it. */
.invgroup__head {
  display: flex; align-items: baseline; gap: 12px; flex-wrap: wrap;
  padding: 10px 14px; color: #fff;
}
.invgroup__head .k {
  font-size: 11px; font-weight: 800; text-transform: uppercase; letter-spacing: .11em;
}
.invgroup__head .s { font-size: 12.5px; color: rgba(255,255,255,.86); }
.invgroup__head .spacer { flex: 1; }
/* On the coloured bar, so it is outlined rather than filled — a solid button
   on a solid bar is two blocks fighting. */
.invadd {
  border: 1px solid rgba(255,255,255,.55); background: rgba(255,255,255,.12);
  color: #fff; border-radius: 3px; padding: 3px 11px; cursor: pointer;
  font: inherit; font-size: 11.5px; font-weight: 700; letter-spacing: .03em;
}
.invadd:hover { background: rgba(255,255,255,.22); border-color: #fff; }
.invadd:focus-visible { outline: 2px solid #fff; outline-offset: 1px; }
.invgroup .rdform { padding: 14px; border-top: 1px solid var(--line); }

.invgroup--fee { border-color: #b9d6c0; }
.invgroup--fee .invgroup__head { background: var(--good); }
.invgroup--rd { border-color: #b6d2dd; }
.invgroup--rd .invgroup__head { background: var(--info); }

/* Dotted, in the gap between them. A solid rule reads as another edge of the
   blocks it sits between; a dotted one reads as a break. */
.invsplit {
  border: 0; border-top: 2px dotted var(--line);
  margin: 22px 16px; height: 0;
}
.invgroup__none { margin: 0; padding: 14px; color: var(--muted); font-size: 13px; }
.invtable { width: 100%; border-collapse: collapse; font-size: 13px; }
.invtable th, .invtable td { padding: 8px 14px; text-align: left; }
.invtable thead th {
  font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); border-bottom: 1px solid var(--line);
}
.invtable .num { text-align: right; font-variant-numeric: tabular-nums; }
.invtable tbody tr + tr td { border-top: 1px solid #ece7dc; }

/* An invoice row, outlined by what it is and what state it is in.

   Paid is green because that is the question you open the tab to answer. A
   credit note is red because it is the one row in a list headed "Armor fee"
   that is money going the other way.

   Sent, draft and void stay unfilled. Sent and draft are simply waiting and
   are most of the list — colouring them would leave nothing uncoloured to
   notice. Void was orange for about an hour and had to come back out: rows
   already go brass on hover (`tbody tr:hover`, #fbf2e0) and --warn-wash is
   #fcf1de, so a voided row looked permanently hovered and every real hover
   nearby looked voided. Its orange chip says it on its own, and the muted
   text below carries the rest without spending a colour. */
.invtable tr.is-paid   { --rowfill: var(--good-wash);   --rowline: #b9d6c0; --rowhover: #dfeee4; }
.invtable tr.is-credit { --rowfill: var(--danger-wash); --rowline: #e8bdb8; --rowhover: #f7dedb; }

.invtable tr.is-paid td,
.invtable tr.is-credit td {
  background: var(--rowfill);
  border-top: 1px solid var(--rowline);
  border-bottom: 1px solid var(--rowline);
}
.invtable tr.is-paid td:first-child,
.invtable tr.is-credit td:first-child { border-left: 1px solid var(--rowline); }
.invtable tr.is-paid td:last-child,
.invtable tr.is-credit td:last-child { border-right: 1px solid var(--rowline); }

/* These rows out-specify the brass hover, so they need their own or they are
   the only rows on the page that do not answer the mouse. Deeper in their own
   colour rather than brass: turning green on hover would read as the state
   changing. */
.invtable tr.is-paid:hover td,
.invtable tr.is-credit:hover td { background: var(--rowhover); }

/* The figure on a credit row is negative, and reads as one. */
.invtable tr.is-credit .amt { color: var(--danger); font-weight: 650; }

/* Void keeps no fill — just greyed text, so it reads as struck through without
   competing with hover. The orange chip in the status column does the naming. */
.invtable tr.is-void td { color: var(--muted); }


/* Editing a round, in the row under it. */
.rndedit { color: var(--mb-head); font-size: 11.5px; font-weight: 700; margin-right: 8px; }
.rndedit:hover { color: var(--brass-dk); }
.roundedit td { padding: 10px 0 14px !important; }
.roundedit form { display: grid; gap: 9px; }
.roundedit .three { display: grid; grid-template-columns: 1fr 1fr 1fr; gap: 8px; }
.roundedit label {
  display: block; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--mb-head); opacity: .85; margin-bottom: 3px;
}
.roundedit .acts { display: flex; gap: 8px; }
.roundedit__warn {
  margin: 0; font-size: 11.5px; line-height: 1.5; color: var(--warn); font-weight: 600;
}

/* The brand colour, with the logo beside it. The screen picker can only sample
   what is on the screen, so the thing being sampled has to be here. */
.brandpick { display: flex; gap: 12px; align-items: flex-start; }
.brandpick__logo {
  flex: 0 0 auto; width: 104px; height: 78px;
  border: 1px solid var(--line); border-radius: var(--radius);
  /* Checkerboard: a white logo on white is invisible, and a lot of them are
     transparent PNGs. */
  background:
    linear-gradient(45deg, #e9e5dc 25%, transparent 25%, transparent 75%, #e9e5dc 75%) 0 0/14px 14px,
    linear-gradient(45deg, #e9e5dc 25%, transparent 25%, transparent 75%, #e9e5dc 75%) 7px 7px/14px 14px,
    #fff;
  display: grid; place-items: center; padding: 5px; overflow: hidden;
}
.brandpick__logo img { max-width: 100%; max-height: 100%; display: block; }
.brandpick__ctl { flex: 1; min-width: 0; }
.brandpick .colorrow { margin-top: 7px; }
.brandpick .hint { display: block; margin-top: 4px; font-size: 11.5px; color: var(--muted); line-height: 1.45; }
.brandpick .hint input { margin-right: 5px; vertical-align: -1px; }

/* Billing address, shown only when it is a different address.
   No script: the tick box is a sibling of what it reveals, so plain CSS can
   do it, and a form whose billing fields depend on a file loading is a form
   that sometimes cannot record a billing address. */
.billchk { margin: 0 6px 0 0; vertical-align: -1px; }
.billtoggle {
  display: inline; text-transform: none; font-size: 14px; letter-spacing: 0;
  color: var(--ink); font-weight: 500; cursor: pointer;
}
.billsame { font-size: 13px; margin: 8px 0 0; }
.billchk:not(:checked) ~ .billfields { display: none; }
.billchk:checked ~ .billsame { display: none; }

/* ---------- editing the lines on a fee invoice ---------- */
.linesedit { margin-bottom: 18px; padding-bottom: 18px; border-bottom: 1px solid var(--line); }
.linesedit h3 {
  margin: 0 0 10px; font-size: 11px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .08em; color: var(--muted);
}
.linestable { width: 100%; border-collapse: collapse; font-size: 13px; margin-bottom: 14px; }
.linestable th, .linestable td { padding: 7px 8px; text-align: left; vertical-align: top; }
.linestable thead th {
  font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
  color: var(--muted); border-bottom: 1px solid var(--line);
}
.linestable .num { text-align: right; font-variant-numeric: tabular-nums; white-space: nowrap; }
.linestable tbody tr + tr td { border-top: 1px solid #ece7dc; }
.linestable .ld { display: block; font-size: 11.5px; color: var(--muted); margin-top: 2px; }
.addline { display: grid; gap: 9px; }
.addline .row { display: grid; grid-template-columns: 2fr 1fr 1fr; gap: 9px; }
.addline .row .wide { grid-column: span 1; }
.addline label {
  display: block; font-size: 10.5px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted); margin-bottom: 3px;
}
.addline .creditbox { grid-column: span 2; }
.addline .creditbox label {
  text-transform: none; letter-spacing: 0; font-size: 13px; font-weight: 600;
  color: var(--graphite); display: flex; align-items: center; gap: 7px;
}
.addline .hint { display: block; margin-top: 4px; font-size: 11.5px; color: var(--muted); line-height: 1.45; }
.addline .acts { display: flex; gap: 8px; }

/* The tooltip. Fixed and on <body>, so a scrolling board column cannot clip it. */
.tip {
  position: fixed; z-index: 200; pointer-events: none;
  background: var(--graphite); color: var(--cream);
  font-size: 12px; font-weight: 600; line-height: 1.35;
  padding: 5px 9px; border-radius: 4px; max-width: 240px;
  box-shadow: 0 6px 18px rgba(20,22,24,.28);
  /* Hidden until it has been positioned, so it never flashes at 0,0 — but by
     visibility, not a fade. A transitioned opacity depends on animation frames
     actually running, and in a throttled or background tab it can sit at 0
     forever: a tooltip that is sometimes invisible is worse than no tooltip. */
  visibility: hidden;
}
.tip.on { visibility: visible; }

/* ---------- editing a PDF ---------- */

.pdfedit .panel + .panel { margin-top: 14px; }
.pdfedit__lede { margin: 0 0 12px; font-size: 12.5px; }

/* One row per date value. The whole row is the label, so the tick target is
   the row rather than a 15px box beside it. */
.dategroup {
  display: flex; align-items: center; gap: 12px;
  padding: 9px 11px; margin-bottom: 7px; cursor: pointer;
  border: 1px solid var(--line); border-radius: var(--radius); background: var(--paper);
}
.dategroup:hover { border-color: var(--brass); }
.dategroup input { accent-color: var(--brass); width: 15px; height: 15px; margin: 0; }
.dategroup .v {
  font-variant-numeric: tabular-nums; font-weight: 700; font-size: 14px;
  min-width: 88px;
}
.dategroup .w { display: flex; flex-direction: column; gap: 1px; flex: 1; min-width: 0; }
.dategroup .ctx { font-size: 12.5px; font-weight: 650; }
.dategroup .pg { font-size: 11.5px; color: var(--muted); }

/* A date that records when something happened. Still changeable, never by
   accident — it reads as held back rather than as an error. */
.dategroup--held { background: var(--danger-wash); border-color: #e8bdb8; }
.dategroup--held .ctx { color: var(--danger); }

.pdfedit__to { display: flex; align-items: center; gap: 10px; margin-top: 14px; }
.pdfedit__to label {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted); white-space: nowrap;
}
.pdfedit__to input { width: 140px; font-variant-numeric: tabular-nums; }
.pdfedit__to .muted { font-size: 11.5px; }

.pdfedit__unlock,
.pdfedit__opt {
  display: flex; align-items: flex-start; gap: 9px; cursor: pointer;
  font-size: 12.5px; line-height: 1.5;
}
.pdfedit__unlock { margin-top: 12px; color: var(--muted); }
.pdfedit__opt + .pdfedit__opt { margin-top: 11px; }
.pdfedit__opt span span { display: block; font-size: 12px; margin-top: 2px; }
.pdfedit__unlock input, .pdfedit__opt input { margin: 2px 0 0; accent-color: var(--brass); }

.pdfedit__where { display: flex; align-items: center; gap: 10px; margin-top: 12px; }
.pdfedit__where label {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .06em; color: var(--muted);
}

.pdfedit__go { display: flex; gap: 10px; align-items: center; margin-top: 16px; }

/* The global `label` rule uppercases and letter-spaces every label on the site,
   which is right for a field caption above an input and wrong for these: they
   are sentences and rows of information, not captions. Same trap the document
   edit form hit. */
.dategroup, .pdfedit__opt, .pdfedit__unlock {
  text-transform: none; letter-spacing: 0; font-size: 12.5px;
}
.dategroup .ctx, .dategroup .pg, .dategroup .v,
.pdfedit__opt strong, .pdfedit__opt span span {
  text-transform: none; letter-spacing: 0;
}

/* The document beside the controls. Wider than the usual side column because
   a page of an estimate at anything less is unreadable, and the controls do
   not need the room. */
.pdfwork {
  display: grid; grid-template-columns: minmax(0, 1.15fr) minmax(340px, 0.85fr);
  gap: 18px; align-items: start;
}
@media screen and (max-width: 1080px) { .pdfwork { grid-template-columns: 1fr; } }

/* Sticky, so the page you jumped to stays put while you work down the form. */
.pdfwork__view {
  position: sticky; top: 16px;
  border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper); overflow: hidden;
}
@media screen and (max-width: 1080px) { .pdfwork__view { position: static; } }

.pdfwork__meta {
  display: flex; align-items: center; gap: 12px; flex-wrap: wrap;
  padding: 9px 12px; font-size: 12px;
  border-bottom: 1px solid var(--line); background: var(--cream-2);
}
.pdfwork__meta .spacer { flex: 1; }

.pdfwork__frame {
  display: block; width: 100%; height: calc(100vh - 210px); min-height: 460px;
  border: 0; background: var(--cream-2);
}
@media screen and (max-width: 1080px) { .pdfwork__frame { height: 70vh; } }

/* A page reference you can press. It sits inside a <label>, so pdfpreview.js
   stops the click reaching the checkbox. */
.pgjump {
  background: none; border: 0; padding: 0; margin: 0; cursor: pointer;
  font: inherit; color: var(--link-blue); text-decoration: underline;
  text-underline-offset: 2px;
}
.pgjump:hover { color: var(--link-blue-dk); }
.pgjump.on { color: var(--brass-dk); font-weight: 700; }

/* Find-and-mark on the Edit PDF screen. */
.findbar { display: flex; gap: 8px; }
.findbar input { flex: 1; min-width: 0; }
.markhow { margin-top: 14px; padding-top: 12px; border-top: 1px solid var(--line); }

/* ---------- the PDF drawn in the page, so it can be marked ---------- */

/* The frame scrolls its own pages; the canvases are a fixed size because the
   text layer over each one is positioned from the same viewport, and the two
   only agree while nothing re-measures underneath them. */
.pdfwork__frame { overflow: auto; padding: 10px; background: var(--cream-2); }
.pdfpage {
  position: relative; margin: 0 auto 12px; background: #fff;
  box-shadow: 0 1px 4px rgba(20,22,24,.16);
}
.pdfpage canvas { display: block; }

/* One transparent span per text run, sitting on its glyphs. Invisible, but
   selectable — which is the whole point.
   Built by pdf.js's TextLayer, which measures each run and squeezes the span
   horizontally onto the printed letters; these rules are what it needs to land
   where it says. transform-origin must be 0% 0%, or that squeeze pulls every
   run away from its own left edge. */
.pdftext {
  position: absolute; inset: 0; overflow: clip;
  line-height: 1; opacity: 1; cursor: text;
  text-align: initial; forced-color-adjust: none; transform-origin: 0 0;
  -webkit-text-size-adjust: none; text-size-adjust: none;
}
/* The BR too, not only the span. pdf.js puts a <br> after every run that ends
   a line, and with no rule of its own it sits in normal flow at the top-left
   of the page — a stack of little selectable bars down the left margin, which
   is what they looked like. Worse, being in flow it gave the selection a path
   through the whole layer, so a drag that started anywhere but exactly on a
   word highlighted the entire page. */
.pdftext :is(span, br) {
  position: absolute; white-space: pre; transform-origin: 0% 0%;
  color: transparent; user-select: text;
}


/* The words sit ABOVE the guard div below, and this is not optional.
   Without it the guard — which comes last in the layer and is deliberately
   unselectable — covered every word the moment a drag began: the pointer
   turned into an arrow, the drag selected nothing, and only a double-click
   still worked, because that lands before the guard is in play. */
.pdftext > :not(.markedContent),
.pdftext .markedContent span:not(.markedContent) { z-index: 1; }
.pdftext span.markedContent { top: 0; height: 0; }

/* Stops a drag running to the end of the page.
   Normally it sits below the content (inset: 100% 0 0). While a selection is
   being made it covers the page from the anchor down, so the browser has
   somewhere to end the selection that is not "everything after this". Moved
   next to the selection anchor by pdfmark.js, exactly as pdf.js's own viewer
   does it. */
.pdftext .endOfContent {
  display: block; position: absolute; inset: 100% 0 0;
  z-index: 0; cursor: default; user-select: none;
}
.pdftext.selecting .endOfContent { top: 0; }
.pdftext ::selection { background: rgba(192,143,62,.35); }

/* The four highlight colours, in one place: the swatches on the toolbar, the
   chips in the list and the stripe on the page all read from these. */
:root {
  --mk-amber: #ffc100;
  --mk-red:   #e23b2e;
  --mk-green: #2fa84f;
  --mk-blue:  #2f6fa8;
}

/* Already marked, shown on the page before Apply is pressed. */
.pdftext .marked { background: color-mix(in srgb, var(--markcolour, var(--mk-amber)) 55%, transparent); }

/* Marked for redaction: OUTLINED, not filled. The outline leaves the words
   readable so you can check you marked the right thing; hovering shows what
   the finished page will look like. Solid black by default hid the one thing
   you need to see while deciding — which is the way round Acrobat has it. */
.pdftext .marked--redact {
  background: transparent;
  outline: 2px solid var(--danger); outline-offset: 1px;
}
.pdftext .marked--redact:hover { background: #000; outline-color: #000; }

/* Follows the selection. */
.markbar {
  position: fixed; z-index: 60; display: flex; gap: 6px;
  padding: 6px; border-radius: var(--radius);
  background: var(--graphite); box-shadow: 0 6px 18px rgba(20,22,24,.35);
}
.markbar .btn { padding: 5px 11px; font-size: 12.5px; }
.markbar .btn.danger { background: var(--danger); border-color: var(--danger); color: #fff; }
.markbar .btn:disabled { opacity: .45; cursor: not-allowed; }

.marklist { display: grid; gap: 7px; }
.markrow {
  display: flex; align-items: center; gap: 9px; font-size: 12.5px;
  padding: 7px 10px; border: 1px solid var(--line); border-radius: var(--radius);
  background: var(--paper);
}
.markrow .t { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

details.findmore { margin-top: 14px; }
details.findmore > summary { list-style: none; cursor: pointer; font-size: 12.5px; }
details.findmore > summary::-webkit-details-marker { display: none; }
details.findmore .inner { margin-top: 11px; }

/* Swatches on the floating toolbar, and the chip beside a mark in the list. */
.mkdot {
  width: 20px; height: 20px; padding: 0; border-radius: 50%;
  border: 2px solid rgba(255,255,255,.45); cursor: pointer;
}
.mkdot:hover { border-color: #fff; }
.mkchip {
  width: 13px; height: 13px; border-radius: 3px; flex: 0 0 auto;
  border: 1px solid rgba(20,22,24,.25);
}
.markbar .sep { width: 1px; align-self: stretch; background: rgba(255,255,255,.25); margin: 0 3px; }

/* Zoom, in the strip above the document. */
.pdfzoom { display: flex; align-items: center; gap: 4px; }
.pdfzoom button {
  width: 24px; height: 22px; padding: 0; line-height: 1;
  border: 1px solid var(--line); background: var(--paper); border-radius: 4px;
  cursor: pointer; font-size: 14px;
}
.pdfzoom button:disabled { opacity: .4; cursor: not-allowed; }
.pdfzoom .z { font-size: 11.5px; color: var(--muted); min-width: 34px; text-align: center; }

/* A save option that is not available right now — greyed, but still readable,
   because the reason it is off is written underneath it. */
.pdfedit__opt.is-off { opacity: .55; cursor: not-allowed; }
.pdfedit__opt.is-off input { cursor: not-allowed; }

/* Search results, inside the folded-away panel. */
.findout { margin-top: 12px; }
.markbar--inline {
  position: static; display: inline-flex; margin-bottom: 10px;
}
.findlist { display: grid; gap: 4px; font-size: 12px; }
.findlist > div { display: flex; gap: 8px; align-items: baseline; }
.findlist .pg {
  flex: 0 0 auto; font-variant-numeric: tabular-nums; color: var(--muted);
  font-size: 11px; min-width: 22px;
}
.findlist .t { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }

/* Billed, earned, never collected. Red like Outstanding, because it is the
   same kind of bad news — but it sits apart from it, since nobody is chasing
   this one any more. */
.finstat--off { background: var(--danger-wash); border-color: #e8bdb8; }
.finstat--off .v { color: var(--danger); }

/* ---------- what the estimates say ----------
   A third reading of the same money, taken from the documents rather than
   typed. Quieter than the blocks above it until it has something to report. */
.estcheck { margin-top: 12px; }
.esttable { width: 100%; border-collapse: collapse; font-size: 12.5px; margin-top: 8px; }
.esttable td { padding: 5px 0; border-bottom: 1px solid var(--line); vertical-align: top; }
.esttable tr:last-child td { border-bottom: 0; }
.esttable td.num {
  text-align: right; white-space: nowrap; font-variant-numeric: tabular-nums;
  font-weight: 650; padding-left: 12px;
}
.esttable td.num.gain { color: var(--good); }
.estsub {
  margin-top: 12px; font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .07em; color: var(--muted);
}
.estnote { font-size: 11px; color: var(--muted); }
.estflag { font-size: 11px; color: var(--danger); }
.estverdict {
  margin: 12px 0 0; padding: 9px 11px; font-size: 12.5px; line-height: 1.45;
  border-radius: var(--radius); border: 1px solid var(--line);
}
.estverdict--ok  { background: var(--good-wash);   border-color: #b9d6c0; }
.estverdict--off { background: var(--danger-wash); border-color: #e8bdb8; color: var(--danger); }

/* ---------- the Estimates tab ----------
   Unfinished work, admin-only, deliberately roomier than a panel corner
   because the line-item comparison is going to live here too. */
.estlab { padding: 18px 16px 20px; }
.estlab__head { display: flex; gap: 18px; align-items: flex-start; }
.estlab__head > div { flex: 1; min-width: 0; }
.estlab h3 { margin: 0 0 5px; font-size: 16px; }
.estlab .muted { margin: 0; font-size: 12.5px; max-width: 62ch; }
.estlab__note {
  margin-top: 20px; padding: 11px 13px; font-size: 12.5px; line-height: 1.5;
  background: var(--warn-wash); border: 1px solid #e6cb95; border-radius: var(--radius);
  color: #6b5320;
}
.estlab .esttable { font-size: 13px; margin-top: 16px; }

/* ---------- the job header on a phone ----------

   The header is one wrapping row: name, contractor, buttons, then the two age
   chips. On a 390px screen a long contractor — "Beekman Charter School under
   Rice Roofing & Construction" — filled the row by itself, and the chips fell
   onto a line each. Two numbers that are meant to be read side by side ended up
   in what looked like separate sections of the page.

   So on a phone the parts are given their own rows deliberately rather than
   left to wrap wherever the name happens to end: title, contractor, buttons,
   and the chips sharing one row, half each, however long the name is. */
@media screen and (max-width: 760px) {
  .pagehead--job { align-items: flex-start; gap: 9px 12px; }
  .pagehead--job h1 { flex: 1 0 100%; font-size: 20px; }
  .pagehead--job .sub { flex: 1 0 100%; }
  .pagehead--job .spacer { display: none; }
  .pagehead--job > .btn,
  .pagehead--job > form { order: 3; }
  .pagehead--job .agechips { order: 4; flex: 1 0 100%; display: flex; gap: 12px; }
  .pagehead--job .agechip {
    flex: 1 1 0; min-width: 0;
    align-items: flex-start; padding-left: 9px;
  }
  .pagehead--job .agechip .v { font-size: 17px; }
  .pagehead--job .agechip .k { font-size: 9.5px; letter-spacing: .06em; }
  .pagehead--job .pagehead__addrline { order: 5; margin-top: 0; gap: 6px 12px; }
}

/* ---------- back, in the installed app ----------
   Hidden by default and unhidden by back.js only where there is no browser
   chrome to provide one. Sized for a thumb: 38px is the smallest square that
   is comfortably hit without looking at it. */
.topback {
  display: none; flex: none; width: 38px; height: 34px; margin-left: -4px;
  align-items: center; justify-content: center;
  background: none; border: 0; cursor: pointer;
  color: #d7dade; font-size: 26px; line-height: 1; padding: 0 0 3px;
  border-radius: var(--radius);
}
.topback:active { background: #33383c; }
/* back.js unhides it only in the installed app, so no width rule is needed —
   an iPad on the home screen has no back arrow either. */
.topback:not([hidden]) { display: inline-flex; order: 0; }

/* ---------- the document preview on a phone ----------

   Full screen, because a 26px inset border on a 390px screen spends a seventh
   of the width on a shadow. The pages are drawn by docview.js as canvases,
   stacked and scrolled — iOS will not scroll a PDF inside an iframe, so page
   two was unreachable without downloading the file. */
.docframe .stage.pages {
  display: block; padding: 10px; background: #55585b;
  -webkit-overflow-scrolling: touch;
}
.docpage {
  width: 100%; margin: 0 auto 10px; background: #fff;
  box-shadow: 0 1px 4px rgba(0,0,0,.3);
}
.docpage canvas { display: block; }
.docload { color: #e7e9eb; font-size: 13px; padding: 20px; text-align: center; }
.docframe header .docback { font-weight: 650; }
.docframe header .docback span { font-size: 17px; line-height: 1; margin-right: 1px; }

@media screen and (max-width: 760px) {
  .docview { padding: 0; }
  .docframe { width: 100%; height: 100%; max-height: none; border-radius: 0; }
  .docframe header { padding: 8px 10px; gap: 6px; }
  .docframe header .nm { font-size: 12.5px; }
  /* Room for two buttons and a name; "New tab" is the one that goes, since
     Back and Download are what a phone actually needs. */
  .docframe header [data-tab] { display: none; }
}

/* ---------- what a document is ----------
   The label earns its place on the row: an unlabelled document is one the
   estimates cannot read, so it is called out rather than left blank. The
   scope and supplement colours are the two that get compared — they are read
   as a pair, so they read as a pair. */
.doclabel {
  display: inline-block; margin: 2px 0 0; padding: 1px 8px;
  font-size: 11px; font-weight: 700; letter-spacing: .03em;
  border-radius: 10px; border: 1px solid transparent; text-transform: uppercase;
}
.doclabel--ins_scope { background: #eaf2fa; border-color: #c5dbef; color: var(--link-blue-dk); }
.doclabel--our_supplement { background: var(--brass-wash); border-color: #e0cfa8; color: var(--brass-dk); }
.doclabel--carrier_response { background: var(--warn-wash); border-color: #e6cb95; color: #6b5320; }
.doclabel--other { background: var(--cream-2); border-color: var(--line); color: var(--muted); }
.doclabel--none { background: transparent; border-color: #e6cb95; color: #8a6d24; }

.docedit__what { display: flex; gap: 6px; }
.docedit__what select { flex: 1; min-width: 0; }
.docedit__what input[data-kindno] { width: 66px; flex: none; }

/* The upload box, where saying what it is cannot be skipped. */
.upstage__row .what { display: flex; gap: 5px; align-items: center; }
.upstage__row .what select { max-width: 170px; }
.upstage__row .what input[data-no] { width: 58px; }
.upstage__say { font-size: 12px; color: var(--danger); font-weight: 600; }

/* The estimates table names the label first and the filename second: which
   round this is matters more than what somebody called the file. */
.esttable .estname { font-weight: 650; }
.esttable .estfile { color: var(--muted); font-size: 11.5px; margin-left: 6px; }

/* The two figures people quote — the latest round and the whole supplement —
   sit above the round-by-round list and read heavier than it. */
.esttable--head { margin-top: 12px; border-top: 2px solid var(--line); }
.esttable--head td { padding-top: 9px; padding-bottom: 9px; font-size: 14px; }
.esttable--head .estfile { display: block; margin: 2px 0 0; font-weight: 500; }

/* ---------- priority and expedited ----------
   The size of a job, off its supplement ask, and whether it has been paid to
   jump the queue. Solid chips, not outlines: on a board of forty cards the
   point is to be found by colour before anything is read.

   Blue, green, red, by Hunter's brackets — under $10k, $10k to $40k, $40k and
   over. Expedited is the one that must never be missed, so it is dark with a
   bolt, and it sits first on the card. */
.jtags { display: flex; flex-wrap: wrap; gap: 4px; margin: 5px 0 1px; }
.jtags--head { margin: 0; align-self: center; }
.jtag {
  display: inline-flex; align-items: center; gap: 3px;
  padding: 1px 7px; border-radius: 9px;
  font-size: 10.5px; font-weight: 750; letter-spacing: .04em; text-transform: uppercase;
  line-height: 1.55; color: #fff; white-space: nowrap;
}
.jtag--standard { background: #2f6fa8; }
.jtag--medium   { background: #3f8a4f; }
.jtag--high     { background: #b8372b; }
.jtag--expedited { background: #22262a; color: #ffc94a; }
.jtag--expedited::before { content: "⚡"; font-size: 10px; }
.jtags--head .jtag { font-size: 11px; padding: 2px 9px; }

/* The Expedite button when it is on: it reads as a state, not an invitation. */
.btn.ghost.btn--on { border-color: #22262a; background: #22262a; color: #ffc94a; }
.btn.ghost.btn--on:hover { background: #33383c; }

/* The two age counts as one unit, so they wrap together or not at all. */
.agechips { display: inline-flex; gap: 16px; flex: none; }

/* ---------- a task's description, folded ----------
   "Description" with a chevron that turns when it opens. Small and muted when
   closed, so a list of twenty tasks stays a list; the description itself reads
   at full weight once asked for. */
.taskmore { margin-top: 3px; }
.taskmore > summary {
  display: inline-flex; align-items: center; gap: 4px; cursor: pointer;
  font-size: 12px; font-weight: 650; color: var(--link-blue);
  list-style: none; user-select: none;
}
.taskmore > summary::-webkit-details-marker { display: none; }
.taskmore > summary::after {
  content: ""; width: 6px; height: 6px; margin-top: -3px;
  border-right: 1.6px solid currentColor; border-bottom: 1.6px solid currentColor;
  transform: rotate(45deg); transition: transform .15s ease;
}
.taskmore[open] > summary::after { transform: rotate(-135deg); margin-top: 3px; }
.taskmore > summary:hover { color: var(--link-blue-dk); }
.taskmore > summary:focus-visible { outline: 2px solid var(--brass); outline-offset: 2px; border-radius: 3px; }
.taskmore .taskdesc { margin-top: 5px; }
.task.done .taskmore > summary { color: var(--muted); }
@media (prefers-reduced-motion: reduce) { .taskmore > summary::after { transition: none; } }

/* ---------- not yet invoiced ----------
   The billing run's list. The tab goes amber while anything is waiting, so it
   gets noticed from the Invoices page without being opened. */
.tabs a.tab--todo.has { color: #8a6d24; }
.tabs a.tab--todo.has .c { background: #f3dfa6; color: #6b5320; }
.unbilled__sum {
  display: flex; gap: 28px; flex-wrap: wrap; margin: 0 0 16px; padding: 14px 18px;
  background: var(--paper); border: 1px solid var(--line); border-radius: var(--radius);
}
.unbilled__sum > div { display: flex; flex-direction: column; }
.unbilled__sum .v { font-size: 22px; font-weight: 700; font-variant-numeric: tabular-nums; }
.unbilled__sum .k { font-size: 11px; text-transform: uppercase; letter-spacing: .07em; color: var(--muted); font-weight: 700; }
.unbilled h2 a { color: inherit; }
.unbilled__total { font-size: 13px; font-weight: 650; color: var(--graphite-2); font-variant-numeric: tabular-nums; }
.unbilled .jtag { margin-left: 6px; vertical-align: 1px; }
.unbilled__age {
  display: inline-block; margin-left: 6px; padding: 0 6px; border-radius: 8px;
  font-size: 11px; font-weight: 700; background: var(--cream-2); color: var(--muted);
}
.unbilled__age.warm { background: #f3dfa6; color: #6b5320; }
.unbilled__age.old { background: var(--danger-wash); color: var(--danger); }

/* ---------- the money panel, block by block ---------- */

/* Estimate: slate green. A third pot beside the plum supplement and the rust
   depreciation — separate work, separate price, so it reads as its own thing
   rather than a row in either of the others. */
.moneyblock--est {
  --mb-accent: #2f6f6a;
  --mb-line:   #bfd9d6;
  --mb-wash:   #f1f8f7;
  --mb-head:   #1f524e;
}

/* Every block now carries its own Edit. The panel heading has the same control
   (.panel > h2 .panelact) and this one was never styled — the rounds' "+ Add"
   has been rendering as a browser default button in the middle of a typeset
   header. Same shape, in the block's own colour. */
.moneyblock__head .panelact {
  background: none; border: 0; padding: 0; cursor: pointer;
  font: inherit; color: var(--mb-head);
  text-transform: uppercase; letter-spacing: .12em;
}
.moneyblock__head .panelact:hover { text-decoration: underline; }

.mbform .two { display: grid; grid-template-columns: 1fr 1fr; gap: 10px; margin-bottom: 11px; }
@media screen and (max-width: 620px) {
  .mbform .two { grid-template-columns: 1fr; }
}
/* The block's own padding stops 4px short, which is right under a list of
   figures and too tight under a row of buttons. */
.mbacts { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; padding-bottom: 8px; }

/* A folder inside a folder. Indented by an inline margin (the depth is data, not
   a class), and drawn a shade quieter than a top-level one with a rule down its
   left so the indent reads as "inside this" rather than as a stray row.
   The inline edit form is not indented with it: it is a form, and lining it up
   with the row above cost 22px of an input on a phone. */
.folder--in {
  margin-left: calc(var(--fdepth, 1) * 22px);
  background: var(--cream);
  box-shadow: inset 3px 0 0 var(--line);
}
.folder--in .fname { font-size: 13.5px; }
.folder--in:hover { box-shadow: inset 3px 0 0 var(--brass), 0 2px 10px rgba(23,26,29,.07); }
@media screen and (max-width: 620px) {
  /* A smaller step per level, not one flat indent: three levels at 22px leaves
     no room for a name, and flattening them would put a city at the same level
     as its state. */
  .folder--in { margin-left: calc(var(--fdepth, 1) * 10px); }
}

/* What the contractor's own system calls this job, under the claim details.
   Not a carrier field, so it is set apart: a rule above it, its own colour, and
   one line whoever the contractor is — "In Rice Roofing and Construction's
   system" ran to two and pushed the panel down. */
.crmline {
  display: flex; align-items: baseline; gap: 8px;
  margin-top: 14px; padding-top: 11px;
  border-top: 1px solid var(--line);
  font-size: 13px; min-width: 0;
}
.crmline .k {
  color: var(--info); font-weight: 650; white-space: nowrap;
  font-size: 11px; text-transform: uppercase; letter-spacing: .07em;
}
.crmline .v {
  color: var(--info); font-weight: 600;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
