:root {
  --blue: #8FC1F2;
  --blue-mid: #5B96D8;
  --blue-deep: #1C3A63;
  --ink: #0A0F1C;
  --ink-deep: #050810;
  --white: #F6F8FB;
  --gray: #96A2B3;

  --surface: #FFFFFF;
  --border: #E2E8F1;
  --success: #2F9E63;
  --danger: #C4443B;
  --warning: #C08A2E;
}

* { margin: 0; padding: 0; box-sizing: border-box; }

/* =====================================================================
   The filing declarations — /upload-bill.html and /upload-cheque.html.

   Four statements, one box each, all required before the document can be
   filed. Separate boxes on purpose: each names a different mistake that
   has to be looked for separately, and one "everything is correct" box is
   ticked without looking at any of them. Grouped under a heading so the
   set reads as a single act rather than four stray controls.

   .verify-confirm was already the confirmation row on both pages; keeping
   it means all four rows look alike, which is the point.
   ===================================================================== */
/* A decision on this document taken from another stage — the Group Controller
   sending a payment back. Under the chain, not in it: the chain lists this
   document's own levels, and a step for a role that does not sit on it reads as
   if that role were part of the route. */
.chain-intervention {
  margin-top: 10px; border-left: 3px solid var(--danger); border-radius: 0 8px 8px 0;
  background: rgba(196,68,59,0.05); padding: 10px 14px;
}
.chain-intervention-head {
  font-size: 12.5px; font-weight: 600; color: var(--danger);
}
.chain-intervention-what { font-size: 12.5px; color: var(--gray); line-height: 1.55; margin-top: 2px; }

/* "You are correcting a document that was sent back" — the standing fact of the
   visit, said at the top of the upload step where the work starts. */
.correcting-banner {
  border: 1px solid var(--blue-mid); border-left-width: 3px; border-radius: 10px;
  background: #F7FAFE; padding: 14px 16px; margin-bottom: 18px;
}
.correcting-title { font-family: 'Outfit', sans-serif; font-size: 14.5px; font-weight: 600; color: var(--blue-deep); }
.correcting-line { font-size: 12.5px; color: var(--gray); line-height: 1.6; margin-top: 2px; }

.attest-block {
  border: 1px solid var(--border); border-radius: 12px;
  background: #FBFCFE; padding: 14px 16px 6px; margin: 18px 0;
}
.attest-title {
  font-family: 'Tektur', monospace; font-size: 10px; letter-spacing: 0.12em;
  text-transform: uppercase; color: var(--gray); margin-bottom: 10px;
}
/* Inside the block the rows lose their own frame — the block is the frame. */
.attest-block .verify-confirm {
  border: 0; background: transparent; border-radius: 0;
  padding: 0 0 12px; margin: 0;
}

body {
  font-family: 'Work Sans', sans-serif;
  background: var(--white);
  color: var(--ink);
  min-height: 100vh;
}

a { color: var(--blue-mid); }

/* =====================================================================
   Auth pages (login / reset password) — dark hero, ported from the
   Gramans Portal "Coming Soon" reference.
   ===================================================================== */
body.auth-page {
  width: 100%; height: 100vh;
  background: radial-gradient(120% 90% at 68% 30%, #12213C 0%, var(--ink) 45%, var(--ink-deep) 100%);
  overflow: hidden;
  position: relative;
}

.auth-page #net { position: fixed; inset: 0; width: 100%; height: 100%; display: block; opacity: 0; animation: fadeIn 2.2s ease-out 0.2s forwards; }

.auth-page .grid-floor {
  position: fixed; left: 0; right: 0; bottom: 0; height: 52%;
  background-image:
    repeating-linear-gradient(to top, rgba(143,193,242,0.14) 0 1px, transparent 1px 64px),
    repeating-linear-gradient(to right, rgba(143,193,242,0.10) 0 1px, transparent 1px 90px);
  -webkit-mask-image: linear-gradient(to top, rgba(0,0,0,0.9), transparent);
  mask-image: linear-gradient(to top, rgba(0,0,0,0.9), transparent);
  transform: perspective(560px) rotateX(62deg);
  transform-origin: bottom;
  opacity: 0;
  animation: fadeIn 2.4s ease-out 0.3s forwards;
  pointer-events: none;
}

.auth-page .glow {
  position: fixed; width: 56vw; height: 56vw; max-width: 820px; max-height: 820px;
  right: -10vw; top: 6vh; border-radius: 50%;
  background: radial-gradient(circle, rgba(91,150,216,0.30) 0%, rgba(91,150,216,0) 70%);
  filter: blur(10px);
  opacity: 0;
  animation: fadeIn 2.6s ease-out 0.2s forwards;
  pointer-events: none;
}

.auth-page .cube-scene { position: fixed; inset: 0; perspective: 1400px; pointer-events: none; }
.auth-page .cube-orbit { position: absolute; transform-style: preserve-3d; opacity: 0; animation: cubeFadeIn 1.6s ease-out forwards, spin 26s linear infinite; }
.auth-page .cube-orbit--a { --s: 120px; left: 66%; top: 26%; animation-delay: 0.6s, 0.6s; animation-duration: 1.6s, 34s; }
.auth-page .cube-orbit--b { --s: 64px; left: 81%; top: 44%; animation-delay: 0.9s, 0.9s; animation-duration: 1.6s, 22s; animation-direction: normal, reverse; }
.auth-page .cube-orbit--c { --s: 46px; left: 60%; top: 52%; animation-delay: 1.1s, 1.1s; animation-duration: 1.6s, 18s; }
.auth-page .cube-orbit--d { --s: 32px; left: 87%; top: 22%; animation-delay: 1.3s, 1.3s; animation-duration: 1.6s, 14s; animation-direction: normal, reverse; }
.auth-page .cube { position: relative; width: var(--s); height: var(--s); transform-style: preserve-3d; animation: float 7s ease-in-out infinite; }
.auth-page .cube-orbit--b .cube { animation-duration: 8.5s; }
.auth-page .cube-orbit--c .cube { animation-duration: 6s; }
.auth-page .cube-orbit--d .cube { animation-duration: 9.5s; }
.auth-page .face { position: absolute; width: var(--s); height: var(--s); border: 1px solid rgba(143,193,242,0.45); background: linear-gradient(135deg, rgba(127,182,240,0.16), rgba(20,36,58,0.20)); }
.auth-page .face.front { transform: translateZ(calc(var(--s) / 2)); }
.auth-page .face.back { transform: translateZ(calc(var(--s) / -2)) rotateY(180deg); }
.auth-page .face.right { transform: translateX(calc(var(--s) / 2)) rotateY(90deg); }
.auth-page .face.left { transform: translateX(calc(var(--s) / -2)) rotateY(-90deg); }
.auth-page .face.top { transform: translateY(calc(var(--s) / -2)) rotateX(90deg); background: linear-gradient(135deg, rgba(159,208,255,0.28), rgba(127,182,240,0.14)); }
.auth-page .face.bottom { transform: translateY(calc(var(--s) / 2)) rotateX(-90deg); background: linear-gradient(135deg, rgba(20,36,58,0.30), rgba(10,18,30,0.30)); }

.auth-content {
  position: relative; z-index: 5;
  height: 100vh;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  text-align: center;
  padding: 24px;
}

.auth-logo {
  display: flex; align-items: center; gap: 22px;
  font-family: 'Libre Baskerville', serif;
  font-size: clamp(20px, 3vw, 30px);
  color: var(--white);
  letter-spacing: 3.5px;
  margin-bottom: 36px;
  opacity: 0;
  animation: fadeIn 0.9s ease-out 0.3s forwards;
}
.auth-logo-divider { width: 2px; height: 30px; background: var(--blue); box-shadow: 0 0 12px rgba(143,193,242,0.8); }

.auth-card {
  position: relative; z-index: 5;
  width: 100%; max-width: 380px;
  opacity: 0;
  animation: riseIn 0.8s cubic-bezier(.22,.68,.32,1) 0.5s forwards;
}

.auth-card h1 {
  font-family: 'Outfit', sans-serif; font-weight: 600;
  font-size: clamp(24px, 3.4vw, 32px);
  color: var(--white);
  margin-bottom: 8px;
}
.auth-card p.subtitle { color: var(--gray); font-size: 14px; margin-bottom: 28px; line-height: 1.5; }

.auth-field { margin-bottom: 18px; text-align: left; }
.auth-field label {
  display: block; font-family: 'Tektur', sans-serif; font-size: 11px; letter-spacing: 1.8px;
  text-transform: uppercase; color: var(--blue); margin-bottom: 8px;
}
.auth-field input {
  width: 100%; padding: 13px 16px;
  background: rgba(10, 15, 28, 0.5);
  border: 1px solid rgba(143,193,242,0.28);
  border-radius: 10px;
  color: var(--white);
  font-family: 'Work Sans', sans-serif;
  font-size: 15px;
  transition: border-color 0.2s, box-shadow 0.2s;
}
.auth-field input:focus {
  outline: none;
  border-color: var(--blue);
  box-shadow: 0 0 0 3px rgba(143,193,242,0.18), 0 0 18px rgba(143,193,242,0.25);
}
.auth-field input::placeholder { color: rgba(150,162,179,0.6); }

.auth-btn {
  width: 100%; padding: 14px;
  background: linear-gradient(135deg, var(--blue-mid), var(--blue-deep));
  color: var(--white);
  border: none; border-radius: 10px;
  font-family: 'Work Sans', sans-serif; font-weight: 600; font-size: 15px;
  letter-spacing: 0.4px;
  cursor: pointer;
  transition: transform 0.15s, box-shadow 0.15s;
  box-shadow: 0 6px 20px rgba(28,58,99,0.4);
}
.auth-btn:hover { transform: translateY(-1px); box-shadow: 0 8px 24px rgba(28,58,99,0.55); }
.auth-btn:disabled { opacity: 0.6; cursor: not-allowed; transform: none; }

.auth-links { margin-top: 20px; text-align: center; }
.auth-links a { color: var(--blue); font-size: 13.5px; text-decoration: none; }
.auth-links a:hover { text-decoration: underline; }

.auth-error {
  background: rgba(196,68,59,0.15);
  border: 1px solid rgba(196,68,59,0.4);
  color: #F3B4AF;
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 13.5px;
  margin-bottom: 18px;
  display: none;
}
.auth-error.show { display: block; }

.auth-success {
  background: rgba(47,158,99,0.15);
  border: 1px solid rgba(47,158,99,0.4);
  color: #9EE3BC;
  padding: 10px 14px;
  border-radius: 8px;
  font-size: 13.5px;
  margin-bottom: 18px;
  display: none;
}
.auth-success.show { display: block; }

.auth-footer {
  position: fixed; left: 0; right: 0; bottom: 22px;
  text-align: center;
  font-size: 12px; letter-spacing: 0.3px;
  color: rgba(150,162,179,0.55);
  z-index: 5;
}

@keyframes fadeIn { to { opacity: 1; } }
@keyframes riseIn { to { opacity: 1; transform: translateY(0); } }
@keyframes cubeFadeIn { to { opacity: 1; } }
@keyframes spin { from { transform: rotateX(0deg) rotateY(0deg); } to { transform: rotateX(360deg) rotateY(360deg); } }
@keyframes float { 0%, 100% { margin-top: 0px; } 50% { margin-top: -16px; } }

/* =====================================================================
   App shell (light theme) — dashboards, forms, tables
   ===================================================================== */
body.app-page { background: var(--white); }

.app-shell { display: flex; min-height: 100vh; }

.sidenav {
  width: 232px; flex-shrink: 0;
  background: var(--blue-deep);
  color: var(--white);
  display: flex; flex-direction: column;
  padding: 22px 0;
}
.sidenav-logo {
  padding: 0 22px 22px;
  border-bottom: 1px solid rgba(255,255,255,0.12);
  margin-bottom: 14px;
}
.sidenav-logo img {
  display: block; width: 100%; height: auto;
  /* Source mark is black wordmark + light-blue divider on a transparent
     ground; crush to solid black then invert so it reads as a clean white
     mark against the dark sidenav, instead of showing a light logo plate. */
  filter: brightness(0) invert(1);
}
.sidenav-logo .subtitle {
  display: block;
  font-family: 'Tektur', sans-serif;
  font-size: 10.5px; letter-spacing: 1.8px;
  text-transform: uppercase;
  color: var(--blue);
  margin-top: 10px;
}
.sidenav a {
  display: flex; align-items: center; gap: 10px;
  padding: 11px 22px;
  color: rgba(246,248,251,0.75);
  text-decoration: none;
  font-size: 14px;
  border-left: 3px solid transparent;
  transition: background 0.15s, color 0.15s;
}
.sidenav a:hover { background: rgba(255,255,255,0.06); color: var(--white); }
.sidenav a.active { color: var(--white); border-left-color: var(--blue); background: rgba(143,193,242,0.1); font-weight: 500; }

/* Nav icons (NAV_ICONS in js/app.js). currentColor throughout, so the icon
   dims and brightens with its own link rather than carrying a second colour
   system that would drift out of step with it. */
.sidenav a .icon {
  flex: 0 0 18px; width: 18px; height: 18px;
  display: flex; align-items: center; justify-content: center;
  opacity: 0.72;
  transition: opacity 0.15s;
}
.sidenav a .icon svg { width: 18px; height: 18px; display: block; }
.sidenav a:hover .icon { opacity: 0.92; }
/* The active row is the one place an icon is allowed full strength — it marks
   where you are, and the pale blue is the same accent as the left rule. */
.sidenav a.active .icon { opacity: 1; color: var(--blue); }
.sidenav a:focus-visible { outline: 2px solid var(--blue); outline-offset: -2px; }

/* A nav entry with nothing behind it yet — Cheque Process before any bill has
   cleared billing approval (see NAV_GATES in js/app.js). Dimmed rather than
   hidden: the step still exists and belongs in the sequence, it just cannot be
   started yet, and a link that disappears and reappears is harder to trust than
   one that is visibly waiting. Hovering says why. */
.sidenav a.nav-shut {
  color: rgba(246,248,251,0.32);
  cursor: not-allowed;
}
.sidenav a.nav-shut .icon { opacity: 0.3; }
.sidenav a.nav-shut:hover { background: none; color: rgba(246,248,251,0.32); }
/* Deliberately NOT pointer-events: none. That would take the hover with it, and
   the hover is where the explanation lives — a control you cannot click and
   cannot ask about is just broken. The click itself is blocked in JS instead,
   which also catches Enter on a focused link (keyboard activation fires a
   click, which pointer-events would not have stopped either). */
.sidenav .nav-section { margin-top: auto; }
.sidenav .nav-user {
  padding: 14px 22px; border-top: 1px solid rgba(255,255,255,0.12);
  font-size: 13px; color: rgba(246,248,251,0.6);
}
.sidenav .nav-user strong { color: var(--white); display: block; font-weight: 500; margin-bottom: 2px; }
.sidenav .logout-btn {
  display: block; width: calc(100% - 44px); margin: 10px 22px 0;
  padding: 8px; background: rgba(255,255,255,0.08); border: none; border-radius: 6px;
  color: var(--white); font-size: 12.5px; cursor: pointer; text-align: center;
}
.sidenav .logout-btn:hover { background: rgba(255,255,255,0.16); }

/* Out of office, set by the person themselves — nobody else knows their
   schedule. Sits directly under the user's own name, and while they are away
   the state shows on every page: forgetting to switch it off is the failure
   that matters, and a state you have to go looking for is one nobody checks. */
.sidenav .ooo-state {
  padding: 10px 22px 0; font-size: 12px; line-height: 1.5;
  display: flex; flex-direction: column; gap: 3px; align-items: flex-start;
}
.sidenav .ooo-state .ooo-detail { color: rgba(246,248,251,0.6); }
/* An amber hairline while genuinely away, so it reads as a live state rather
   than a setting. Absent for merely-booked leave, which needs no attention. */
.sidenav .ooo-away {
  border-left: 3px solid var(--warning, #e0a800);
  margin-left: -3px; padding-left: 22px;
}
.sidenav .ooo-btn {
  display: block; width: calc(100% - 44px); margin: 10px 22px 0;
  padding: 7px; background: transparent; border: 1px solid rgba(255,255,255,0.22);
  border-radius: 6px; color: rgba(246,248,251,0.85); font-size: 12px;
  font-family: inherit; cursor: pointer; text-align: center;
}
.sidenav .ooo-btn:hover { background: rgba(255,255,255,0.10); color: var(--white); }

.main-area { flex: 1; display: flex; flex-direction: column; min-width: 0; }

.topbar {
  height: 64px; flex-shrink: 0;
  background: var(--surface);
  border-bottom: 1px solid var(--border);
  display: flex; align-items: center; justify-content: space-between;
  padding: 0 28px;
}
.topbar h1 {
  font-family: 'Libre Baskerville', serif;
  font-size: 20px; color: var(--blue-deep); font-weight: 700;
}
.topbar .badge-tag {
  font-family: 'Tektur', sans-serif; font-size: 10.5px; letter-spacing: 1.6px;
  text-transform: uppercase; color: var(--blue-mid);
  border: 1px solid var(--blue); border-radius: 20px; padding: 4px 12px;
}

.content { flex: 1; padding: 28px; overflow-y: auto; }

.card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 22px;
  margin-bottom: 20px;
}
.card h2 {
  font-family: 'Libre Baskerville', serif;
  font-size: 17px; color: var(--blue-deep); margin-bottom: 16px;
}

.card-head { display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap; margin-bottom: 16px; }
.card-head h2 { margin-bottom: 0; }

.search-bar {
  display: flex; align-items: center; gap: 8px;
  background: var(--white); border: 1px solid var(--border); border-radius: 22px;
  padding: 6px 8px 6px 14px;
  transition: border-color 0.15s, box-shadow 0.15s;
}
.search-bar:focus-within { border-color: var(--blue-mid); box-shadow: 0 0 0 3px rgba(91,150,216,0.15); }
.search-bar-icon { width: 15px; height: 15px; color: var(--gray); flex-shrink: 0; }
.search-bar:focus-within .search-bar-icon { color: var(--blue-mid); }
.search-bar input[type="search"] {
  border: none; background: transparent; outline: none;
  width: 220px; max-width: 40vw;
  font-family: 'Work Sans', sans-serif; font-size: 13.5px; color: var(--ink);
}
.search-bar input[type="search"]::placeholder { color: var(--gray); }

.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 16px; margin-bottom: 20px; }
/* A table row that carries an action the viewer is expected to take — used for
   bills that have cleared approval and are waiting on a cheque. Tinted rather
   than badged alone, so the actionable rows are findable in a long list. */
.row-actionable > td { background: rgba(47,158,99,0.06); }
.row-actionable > td:first-child { box-shadow: inset 3px 0 0 var(--success); }

.stat-tile { background: var(--surface); border: 1px solid var(--border); border-radius: 12px; padding: 18px; }
.stat-tile .label { font-size: 12px; text-transform: uppercase; letter-spacing: 1px; color: var(--gray); margin-bottom: 8px; }
.stat-tile .value { font-family: 'Outfit', sans-serif; font-size: 28px; font-weight: 600; color: var(--blue-deep); }
/* A tile holding a formatted amount rather than a count. "₱12,463.39" is ten
   characters where a count is one or two, and at 28px it runs past the 160px
   the grid guarantees a tile. Tabular figures so a column of them lines up. */
.stat-tile .value.money { font-size: 21px; font-variant-numeric: tabular-nums; }

.btn {
  display: inline-flex; align-items: center; gap: 6px;
  padding: 9px 18px;
  border-radius: 8px; border: none;
  font-family: 'Work Sans', sans-serif; font-size: 13.5px; font-weight: 500;
  cursor: pointer;
  transition: opacity 0.15s, transform 0.1s;
}
.btn:active { transform: scale(0.98); }
.btn-primary { background: var(--blue-mid); color: var(--white); }
.btn-primary:hover { opacity: 0.9; }
.btn-outline { background: transparent; border: 1px solid var(--blue-mid); color: var(--blue-mid); }
.btn-outline:hover { background: rgba(91,150,216,0.08); }
.btn-danger { background: var(--danger); color: var(--white); }
.btn-danger:hover { opacity: 0.9; }
.btn-sm { padding: 6px 12px; font-size: 12.5px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }

.form-row { margin-bottom: 16px; }
.form-row label { display: block; font-size: 13px; font-weight: 500; color: var(--blue-deep); margin-bottom: 6px; }
.form-row input, .form-row select, .form-row textarea {
  width: 100%; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: 8px;
  font-family: 'Work Sans', sans-serif; font-size: 14px;
  background: var(--surface); color: var(--ink);
}
.form-row input:focus, .form-row select:focus, .form-row textarea:focus {
  outline: none; border-color: var(--blue-mid); box-shadow: 0 0 0 3px rgba(91,150,216,0.15);
}
.form-row textarea { resize: vertical; min-height: 90px; }
.form-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 0 16px; }
.form-hint { font-size: 12px; color: var(--gray); margin-top: 4px; }

/* PRF No. stamp — a corner serial number, echoing a paper form's numbered
   stub. Dashed while no number is assigned yet (a fresh, unsaved request);
   solid once the server hands back a real, permanent PRF No. */
.prf-header {
  display: flex; justify-content: space-between; align-items: flex-start; gap: 20px;
  padding-bottom: 18px; margin-bottom: 22px; border-bottom: 1px solid var(--border);
}
.prf-header-copy h2 { margin-bottom: 4px; }
.prf-stamp {
  flex-shrink: 0; text-align: right;
  border: 1.5px dashed var(--blue-mid); border-radius: 10px;
  padding: 10px 18px; min-width: 168px;
}
.prf-stamp.assigned { border-style: solid; border-color: var(--blue-deep); }
.prf-stamp-label {
  display: block; font-family: 'Tektur', sans-serif; font-size: 10.5px; letter-spacing: 1.8px;
  text-transform: uppercase; color: var(--blue-mid);
}
.prf-stamp-value {
  display: block; font-family: 'Outfit', sans-serif; font-size: 25px; font-weight: 600;
  color: var(--blue-deep); letter-spacing: 0.3px; font-variant-numeric: tabular-nums; margin-top: 2px;
}
.prf-stamp-hint { display: block; font-size: 11px; color: var(--gray); margin-top: 3px; }

/* Particulars — a real invoice-style line-item table instead of bare inputs. */
.particulars-table { border: 1px solid var(--border); border-radius: 10px; overflow: hidden; margin-bottom: 10px; }
.particulars-head, .particulars-row {
  display: grid; grid-template-columns: 1fr 150px 40px; gap: 10px; align-items: center;
  padding: 10px 14px;
}
.particulars-head {
  background: var(--white); border-bottom: 1px solid var(--border);
  font-family: 'Tektur', sans-serif; font-size: 10px; letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--gray);
}
.particulars-head span:nth-child(2) { text-align: right; }
.particulars-row { border-bottom: 1px solid var(--border); }
.particulars-row:last-child { border-bottom: none; }
.particulars-row input.li-amount { text-align: right; }

/* Particulars holds a description, not a word — "General Construction Progress
   Billing, two storey building at 110 Street…" or a whole QuickBooks memo. As a
   single-line input everything past the right edge was invisible WHILE STILL
   TYPING IT, so it is a textarea that grows with its content instead. The
   height is set by growParticulars in /js/app.js; these rules only decide where
   it starts and how it wraps.
   Rows align to the top so the amount and ✕ stay level with the FIRST line as
   the description grows past it, rather than drifting down the middle. */
.particulars-row { align-items: start; }
.particulars-row .remove-line { height: 42px; }
.particulars-row textarea.li-particulars {
  min-height: 0;        /* beats the 90px floor on .form-row textarea */
  height: 42px;         /* one line, level with the amount box beside it */
  line-height: 1.45;
  resize: vertical;     /* still draggable, for a very long one */
  overflow-y: hidden;   /* it grows instead of scrolling inside itself */
}
.particulars-total {
  display: flex; justify-content: flex-end; align-items: baseline; gap: 10px;
  padding: 12px 14px; background: var(--white); border-top: 1px solid var(--border);
  font-size: 13px; color: var(--gray); text-transform: uppercase; letter-spacing: 0.6px;
}
.particulars-total strong {
  font-family: 'Outfit', sans-serif; font-size: 19px; font-weight: 600;
  color: var(--blue-deep); font-variant-numeric: tabular-nums;
}

table { width: 100%; min-width: max-content; border-collapse: collapse; }
thead th {
  text-align: left; font-size: 11.5px; text-transform: uppercase; letter-spacing: 0.6px;
  color: var(--gray); padding: 10px 12px; border-bottom: 1px solid var(--border);
}
tbody td { padding: 12px; border-bottom: 1px solid var(--border); font-size: 14px; }
tbody tr:hover { background: rgba(91,150,216,0.04); }
tbody tr:last-child td { border-bottom: none; }
.table-wrap { overflow-x: auto; }
.table-empty { padding: 32px; text-align: center; color: var(--gray); font-size: 14px; }

.badge {
  display: inline-block; padding: 4px 11px; border-radius: 20px;
  font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.4px;
}
.badge-draft { background: #EEF1F5; color: var(--gray); }
.badge-pending { background: rgba(192,138,46,0.14); color: var(--warning); }

/* Under the Cheque badge on a request list: who has signed it so far.
   A caption, not a second status — the badge is the answer, this is the
   detail behind it, so it sits quiet and grey beneath. */
.cheque-signers {
  display: block; margin-top: 3px; font-size: 11px; line-height: 1.35;
  color: var(--gray); max-width: 210px; white-space: normal;
}
.badge-approved { background: rgba(47,158,99,0.14); color: var(--success); }
.badge-rejected { background: rgba(196,68,59,0.14); color: var(--danger); }
.badge-paid { background: rgba(91,150,216,0.16); color: var(--blue-deep); }
.badge-cancelled { background: #EEF1F5; color: var(--gray); }

.filters { display: flex; gap: 12px; flex-wrap: wrap; margin-bottom: 18px; align-items: end; }
.filters .form-row { margin-bottom: 0; min-width: 150px; }

.toast {
  position: fixed; bottom: 24px; right: 24px; z-index: 100;
  background: var(--ink); color: var(--white);
  padding: 12px 20px; border-radius: 8px; font-size: 13.5px;
  box-shadow: 0 8px 24px rgba(0,0,0,0.25);
  opacity: 0; transform: translateY(10px);
  transition: opacity 0.2s, transform 0.2s;
  pointer-events: none;
  max-width: 360px;
}
.toast.show { opacity: 1; transform: translateY(0); }
.toast.error { background: var(--danger); }
.toast.success { background: var(--success); }

.modal-overlay {
  position: fixed; inset: 0; background: rgba(10,15,28,0.5);
  display: none; align-items: center; justify-content: center; z-index: 90; padding: 20px;
}
.modal-overlay.show { display: flex; }
.modal {
  background: var(--surface); border-radius: 12px; padding: 26px;
  width: 100%; max-width: 460px; max-height: 88vh; overflow-y: auto;
}
/* For forms carrying a decision list rather than plain fields — the role
   picker needs the extra width to keep each role on one line. */
.modal-wide { max-width: 540px; }
.modal h2 { font-family: 'Libre Baskerville', serif; font-size: 18px; color: var(--blue-deep); margin-bottom: 18px; }
.modal-actions { display: flex; justify-content: flex-end; gap: 10px; margin-top: 20px; }

/* Inline option row (payment methods, and any other short radio set).
   flex-shrink:0 + nowrap is load-bearing: without it a two-word option like
   "Bank Transfer" gets squeezed and breaks mid-phrase. */
.role-checks { display: flex; gap: 10px 18px; flex-wrap: wrap; }
.role-checks label {
  display: flex; align-items: center; gap: 6px;
  font-size: 13.5px; font-weight: 400; color: var(--ink);
  flex-shrink: 0; white-space: nowrap;
}

/* =====================================================================
   Role picker (Users modal)
   Roles are grouped by where they act in a PRF's life — administration,
   the request itself, then the bill/cheque documents — which is the same
   three-stage route the Approval Routing dashboard shows. The grouping is
   the structure, so the list teaches the pipeline while it's being
   assigned instead of presenting nine equivalent-looking switches.
   ===================================================================== */
.role-picker { display: grid; gap: 16px; }
.role-group { display: grid; gap: 6px; }
.role-group-label {
  display: flex; align-items: center; gap: 10px;
  font-family: 'Tektur', sans-serif; font-size: 9.5px; letter-spacing: 1.5px;
  text-transform: uppercase; color: var(--gray);
}
/* Rule runs to the end of the row, so each group reads as a band. */
.role-group-label::after { content: ''; flex: 1; height: 1px; background: var(--border); }

/* Border is transparent at rest — the group rules already carry the
   structure, so boxing all nine options would double up on it. It resolves
   on hover and selection, which makes the roles a user actually granted the
   thing that stands out when re-reading the form. Kept in the box model at
   all times so nothing shifts by a pixel when it appears. */
/* Scoped under .role-picker on purpose, and it must stay that way. The picker
   lives inside a .form-row, and `.form-row label { display: block }` (one class
   + one element) outranks a bare `.role-option` (one class) — which flattened
   every option into a checkbox stacked above its own name. Two classes wins.
   The margin reset is part of the same fix: .form-row label adds 6px below
   each label, which here is 6px between every role. */
.role-picker .role-option {
  position: relative;
  display: grid; grid-template-columns: auto 1fr; gap: 0 10px; align-items: start;
  padding: 8px 10px; margin-bottom: 0;
  border: 1px solid transparent; border-radius: 9px;
  cursor: pointer;
  transition: border-color 0.15s, background 0.15s;
}
.role-option:hover { background: rgba(91,150,216,0.05); }
.role-option:has(input:checked) { border-color: var(--blue-mid); background: rgba(91,150,216,0.07); }

/* Native control kept in the DOM (focusable, form-readable) but visually
   replaced, so the checkbox matches the app's own blue instead of the OS. */
.role-option input { position: absolute; opacity: 0; width: 0; height: 0; }
.role-option .box {
  width: 16px; height: 16px; margin-top: 1px; flex-shrink: 0;
  border: 1.5px solid var(--border); border-radius: 4px; background: var(--surface);
  display: grid; place-items: center;
  transition: background 0.15s, border-color 0.15s;
}
.role-option .box::after {
  content: ''; width: 9px; height: 5px; margin-top: -2px;
  border-left: 2px solid var(--white); border-bottom: 2px solid var(--white);
  transform: rotate(-45deg) scale(0.5); opacity: 0;
  transition: opacity 0.12s, transform 0.12s;
}
.role-option input:checked + .box { background: var(--blue-mid); border-color: var(--blue-mid); }
.role-option input:checked + .box::after { opacity: 1; transform: rotate(-45deg) scale(1); }
.role-option input:focus-visible + .box { box-shadow: 0 0 0 3px rgba(91,150,216,0.32); }

.role-option .name { font-size: 13.5px; font-weight: 500; color: var(--ink); line-height: 1.35; }
.role-option .desc { grid-column: 2; font-size: 11.5px; color: var(--gray); line-height: 1.4; margin-top: 2px; }
/* The picker's own label sits in a .form-row too, so it inherits the same
   block/margin treatment the options had to escape — here it is wanted. */
.role-picker { margin-top: 2px; }

@media (prefers-reduced-motion: reduce) {
  .role-option, .role-option .box, .role-option .box::after { transition: none; }
}

.levels-editor { border: 1px solid var(--border); border-radius: 10px; padding: 14px; margin-bottom: 10px; }
.level-row { margin-bottom: 14px; padding-bottom: 14px; border-bottom: 1px solid var(--border); }
.level-row:last-child { margin-bottom: 0; padding-bottom: 0; border-bottom: none; }
.level-row-top { display: flex; gap: 10px; align-items: center; }
.level-row .level-num {
  width: 26px; height: 26px; border-radius: 50%; background: var(--blue-deep); color: var(--white);
  display: flex; align-items: center; justify-content: center; font-size: 12px; flex-shrink: 0;
}
.level-row-top select, .level-row-top input { flex: 1; }
.level-row-top .remove-level { flex: none; }
.level-row-categories { margin-top: 10px; padding-left: 36px; }
.level-row-categories .field-caption {
  display: block; font-family: 'Tektur', sans-serif; font-size: 10px; letter-spacing: 1.4px;
  text-transform: uppercase; color: var(--gray); margin-bottom: 7px;
}
.category-picker {
  display: flex; flex-wrap: wrap; gap: 7px;
  max-height: 132px; overflow-y: auto; padding: 2px;
}
.chip {
  appearance: none; -webkit-appearance: none; cursor: pointer;
  border: 1px solid var(--border); background: var(--surface); color: var(--ink);
  font-family: 'Work Sans', sans-serif; font-size: 12.5px; font-weight: 500; line-height: 1.2;
  padding: 6px 13px; border-radius: 20px; white-space: nowrap;
  transition: background 0.15s ease, border-color 0.15s ease, color 0.15s ease;
}
.chip:hover { border-color: var(--blue-mid); }
.chip:focus-visible { outline: 2px solid var(--blue-mid); outline-offset: 2px; }
.chip.active { background: var(--blue-mid); border-color: var(--blue-mid); color: var(--white); }
.chip-all { font-weight: 600; border-style: dashed; }
.chip-all.active {
  background: rgba(91,150,216,0.16); border-style: solid; border-color: var(--blue-mid); color: var(--blue-deep);
}

.timeline { list-style: none; }
.timeline li { display: flex; gap: 14px; padding: 12px 0; border-bottom: 1px solid var(--border); }
.timeline li:last-child { border-bottom: none; }
.timeline .dot { width: 10px; height: 10px; border-radius: 50%; margin-top: 5px; flex-shrink: 0; }
.timeline .dot.approved { background: var(--success); }
.timeline .dot.rejected { background: var(--danger); }
.timeline .dot.pending { background: var(--warning); }
.timeline .meta { font-size: 12px; color: var(--gray); margin-top: 2px; }
.timeline .comment { font-size: 13.5px; margin-top: 6px; background: var(--white); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; }

.routing-subtitle { font-size: 13.5px; color: var(--gray); margin-bottom: 4px; }
.routing-chain-label { font-size: 12.5px; font-weight: 600; text-transform: uppercase; letter-spacing: 0.03em; color: var(--blue-deep); margin-bottom: 16px; }
.routing-chain-label .range { color: var(--gray); font-weight: 500; text-transform: none; letter-spacing: 0; }

.route-steps { list-style: none; }
.route-step { display: flex; gap: 16px; position: relative; padding-bottom: 22px; }
.route-step:last-child { padding-bottom: 0; }
.route-step::before {
  content: ''; position: absolute; left: 15px; top: 32px; bottom: 0; width: 2px; background: var(--border);
}
.route-step:last-child::before { display: none; }
.route-step.done::before { background: var(--success); }

.route-num {
  width: 32px; height: 32px; border-radius: 50%; flex-shrink: 0; z-index: 1;
  display: flex; align-items: center; justify-content: center; font-size: 13px; font-weight: 700;
  border: 2px solid var(--border); background: var(--white); color: var(--gray);
}
.route-step.approved .route-num { background: var(--success); border-color: var(--success); color: var(--white); }
.route-step.rejected .route-num { background: var(--danger); border-color: var(--danger); color: var(--white); }
.route-step.cancelled .route-num { background: var(--gray); border-color: var(--gray); color: var(--white); }
.route-step.current .route-num {
  background: var(--blue-mid); border-color: var(--blue-mid); color: var(--white);
  box-shadow: 0 0 0 4px rgba(91,150,216,0.22);
}

.route-body { flex: 1; padding-top: 3px; }
.route-role { font-family: 'Libre Baskerville', serif; font-size: 15px; color: var(--ink); }
.route-approver { font-size: 13.5px; color: var(--blue-deep); margin-top: 1px; }
.route-status { font-size: 12.5px; color: var(--gray); margin-top: 4px; }
.route-step.approved .route-status { color: var(--success); }
.route-step.rejected .route-status { color: var(--danger); }
.route-step.cancelled .route-status { color: var(--gray); font-weight: 600; }
.route-step.current .route-status { color: var(--blue-mid); font-weight: 600; }
.route-comment { font-size: 13.5px; margin-top: 8px; background: var(--white); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; }

/* Approval-routing stages: one PRF's whole journey (request approval →
   billing approval → cheque process) as three labelled blocks inside the
   single Approval Routing card, so it reads as one continuous route rather
   than three unrelated widgets. */
.stage-block { padding-top: 18px; margin-top: 18px; border-top: 1px solid var(--border); }
.stage-block:first-of-type { padding-top: 10px; margin-top: 10px; border-top: none; }
.stage-label {
  display: flex; align-items: center; gap: 9px; flex-wrap: wrap;
  font-family: 'Libre Baskerville', serif; font-size: 14.5px; color: var(--blue-deep);
  margin-bottom: 14px;
}
.stage-label .stage-num {
  width: 22px; height: 22px; border-radius: 50%; flex-shrink: 0;
  display: flex; align-items: center; justify-content: center;
  background: var(--blue-deep); color: var(--white);
  font-family: 'Work Sans', sans-serif; font-size: 11.5px; font-weight: 600;
}
.stage-label .stage-hint {
  font-family: 'Tektur', sans-serif; font-size: 9.5px; letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--gray);
}

/* One bill or cheque inside its stage, with its own chain underneath. */
.doc-stage { border: 1px solid var(--border); border-radius: 10px; padding: 14px 16px; margin-bottom: 12px; }
.doc-stage:last-child { margin-bottom: 0; }
.doc-stage-head {
  display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
  padding-bottom: 12px; margin-bottom: 12px; border-bottom: 1px solid var(--border);
}
.doc-stage-title { font-family: 'Outfit', sans-serif; font-size: 15.5px; font-weight: 600; color: var(--blue-deep); }
.doc-stage .meta { font-size: 12px; color: var(--gray); margin-top: 3px; }

/* A withdrawn bill/cheque stays on the record — it's part of the PRF's history
   — but recedes so the live document is still the one the eye lands on. */
.doc-stage.is-cancelled { background: var(--white); opacity: 0.72; }
.doc-stage.is-cancelled .doc-stage-title { color: var(--gray); text-decoration: line-through; }

/* =====================================================================
   Source-document preview + magnifier (upload-bill.html, upload-cheque.html)
   The sidebar preview renders at ~300px, which is far too small to check a
   cheque's small print against the form. Moving the pointer across it opens a
   loupe over the patch underneath — magnified from a high-resolution render of
   the page, not from this thumbnail, so the figures stay legible.
   ===================================================================== */
.pdf-preview-figure {
  display: block; position: relative; width: 100%;
}
.pdf-preview-figure canvas { display: block; width: 100%; height: auto; }
.pdf-preview-zoom {
  position: absolute; left: 0; right: 0; bottom: 0;
  padding: 8px 10px; font-size: 12px; font-weight: 500;
  color: var(--white); background: rgba(10,15,28,0.74);
  opacity: 0; transition: opacity 0.15s ease; pointer-events: none;
}
.pdf-preview-figure:hover .pdf-preview-zoom { opacity: 1; }

/* The sheet lift: hovering a small preview raises the whole page beside it.
   Paper, not chrome — a white sheet on a hairline edge with the shadow of
   something held just off the desk, and a caption in the same small-caps
   utility face the panel label uses. Inert by design: it is read while the
   pointer rests on the preview, never moved into. */
.doc-sheet {
  position: fixed; z-index: 210; pointer-events: none;
  opacity: 0; visibility: hidden;
  transform: scale(0.97);
  transition: opacity 0.14s ease, transform 0.14s cubic-bezier(.2,.7,.3,1), visibility 0s linear 0.14s;
}
.doc-sheet.show { opacity: 1; visibility: visible; transform: scale(1); transition-delay: 0s; }
.doc-sheet-page {
  background: var(--surface);
  border: 1px solid var(--border);
  box-shadow: 0 22px 48px rgba(10,15,28,0.30), 0 2px 6px rgba(10,15,28,0.14);
}
.doc-sheet-page canvas { display: block; }
.doc-sheet-name {
  margin-top: 8px; text-align: center;
  font-family: 'Tektur', sans-serif; font-size: 10px; letter-spacing: 1.5px;
  text-transform: uppercase; color: var(--blue-deep);
}

@media (prefers-reduced-motion: reduce) {
  .doc-sheet { transition: opacity 0.01s linear, visibility 0s; transform: none; }
  .doc-sheet.show { transform: none; }
}

/* =====================================================================
   Full-page document overlay — now used only by the attachment dossier on
   /new-request.html (see buildDossierLightbox in js/attachment-preview.js).

   The upload wizards used to share it: their sidebar preview opened here on
   click. That preview is magnified in place by the loupe above instead, so
   nothing on those two pages opens this any more. It stays because the
   dossier still builds it by hand from these class names.
   ===================================================================== */
.doc-lightbox {
  position: fixed; inset: 0; z-index: 200; display: none;
  flex-direction: column; background: rgba(5,8,16,0.92);
}
.doc-lightbox.show { display: flex; }
.doc-lightbox-bar {
  display: flex; align-items: center; justify-content: space-between; gap: 16px; flex-wrap: wrap;
  padding: 12px 18px; background: rgba(10,15,28,0.9); border-bottom: 1px solid rgba(255,255,255,0.12);
}
.doc-lightbox-title {
  font-family: 'Tektur', sans-serif; font-size: 11px; letter-spacing: 1.6px;
  text-transform: uppercase; color: var(--blue);
}
.doc-lightbox-actions { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
.doc-lightbox-actions .btn {
  background: transparent; color: var(--white); border-color: rgba(255,255,255,0.35);
}
.doc-lightbox-actions .btn:hover:not(:disabled) { background: rgba(255,255,255,0.12); }
.doc-lightbox-actions .btn:disabled { opacity: 0.35; cursor: default; }
.doc-lightbox-page { font-size: 12.5px; color: var(--white); min-width: 74px; text-align: center; }
.doc-lightbox-stage {
  flex: 1; overflow: auto; padding: 20px;
  display: flex; align-items: flex-start; justify-content: center;
}
.doc-lightbox-stage canvas,
.doc-lightbox-stage img {
  display: block;
  background: var(--surface); border-radius: 4px; box-shadow: 0 18px 50px rgba(0,0,0,0.5);
  cursor: zoom-in; max-width: none;
}
.doc-lightbox-stage.zoomed canvas,
.doc-lightbox-stage.zoomed img { cursor: zoom-out; }

/* =====================================================================
   Attachment dossier (/new-request.html) — the paper being collected
   alongside the form, in the space to the right of it.

   The mistake this panel exists to catch is attaching the wrong scan,
   and the only way anyone catches that is by looking at it. So the
   panel is built to be looked at: sheets at paper proportions, large
   enough to recognise across the desk, each carrying the numbered index
   tab of a physical file divider, and one click to full size. A list of
   filenames would fit in a quarter of the room and be useless.
   ===================================================================== */
.request-layout {
  display: grid;
  /* The panel takes whatever the form leaves rather than a fixed narrow
     column: on a laptop that is one sheet wide, on a desktop three. Capped
     overall so it stops growing on a very wide monitor instead of stranding
     the form on the far left of a 2560px screen. */
  grid-template-columns: minmax(0, 760px) minmax(296px, 1fr);
  gap: 22px; align-items: start; max-width: 1360px;
}
/* No-op while the two-column track above already caps the form at 760px, and
   the whole point below 1120px, where the panel drops underneath and the form
   would otherwise stretch to the full window. A text field a thousand pixels
   wide is harder to read, not easier. */
.request-layout > .card { max-width: 760px; }

/* Recessed rather than raised: the folder the sheets sit in, not another card. */
.dossier {
  position: sticky; top: 22px;
  background: #EEF3F9; border: 1px solid var(--border); border-radius: 12px; padding: 16px;
}
.dossier-head {
  display: flex; align-items: baseline; justify-content: space-between; gap: 12px;
  padding-bottom: 12px; margin-bottom: 14px; border-bottom: 1px solid var(--border);
}
.dossier-title {
  font-family: 'Tektur', sans-serif; font-size: 10.5px; letter-spacing: 1.8px;
  text-transform: uppercase; color: var(--blue-mid);
}
.dossier-count {
  font-family: 'Outfit', sans-serif; font-size: 15px; font-weight: 600;
  color: var(--blue-deep); font-variant-numeric: tabular-nums;
}
/* Held to a readable measure so the copy doesn't stretch across the full
   width of a wide panel. */
.dossier-empty {
  max-width: 30ch; margin: 0 auto; text-align: center; padding: 34px 6px;
  color: var(--gray); font-size: 12.5px; line-height: 1.55;
}
.dossier-empty strong { display: block; color: var(--ink); font-size: 13.5px; font-weight: 600; margin-bottom: 5px; }

/* Sheets tile to fill the panel — one column on a narrow screen, three or
   four when there is room, which is the point of putting them here. */
.dossier-sheets {
  list-style: none; display: grid; gap: 12px;
  grid-template-columns: repeat(auto-fill, minmax(184px, 1fr));
}
.sheet-slot { position: relative; animation: sheet-drop 0.26s cubic-bezier(0.22, 0.61, 0.36, 1) both; }

/* The index tab down the left edge is this panel's one memorable element:
   a numbered divider in a physical file, reading bottom-to-top the way a
   real tab does. Everything else stays quiet so it can carry the look. */
.sheet {
  position: relative; display: block; width: 100%; text-align: left;
  padding: 0 0 0 26px; font: inherit; color: inherit;
  background: var(--surface); border: 1px solid var(--border); border-radius: 8px;
  overflow: hidden; cursor: zoom-in;
  transition: transform 0.18s ease, box-shadow 0.18s ease, border-color 0.18s ease;
}
.sheet-tab {
  position: absolute; left: 0; top: 0; bottom: 0; width: 26px;
  display: flex; align-items: center; justify-content: center;
  background: var(--blue-deep); transition: background 0.18s ease;
}
.sheet-tab span {
  font-family: 'Tektur', sans-serif; font-size: 11px; letter-spacing: 1.4px; color: var(--blue);
  writing-mode: vertical-rl; transform: rotate(180deg); transition: color 0.18s ease;
}
.sheet-figure {
  position: relative; display: block; background: #F4F7FB; border-bottom: 1px solid var(--border);
}
.sheet-figure canvas, .sheet-figure img {
  display: block; width: 100%; height: 172px; object-fit: cover; object-position: top center;
}
.sheet-blank {
  display: flex; align-items: center; justify-content: center; height: 172px;
  font-size: 12px; color: var(--gray);
}
.sheet-view {
  position: absolute; left: 0; right: 0; bottom: 0; padding: 7px 10px;
  font-size: 11.5px; font-weight: 500; color: var(--white); background: rgba(10, 15, 28, 0.76);
  opacity: 0; transition: opacity 0.15s ease; pointer-events: none;
}
.sheet-meta { display: block; padding: 9px 11px; }
.sheet-name {
  display: block; font-size: 12.5px; font-weight: 500; color: var(--ink);
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.sheet-detail { display: block; font-size: 11px; color: var(--gray); margin-top: 2px; }

.sheet:hover, .sheet:focus-visible {
  transform: translateY(-2px); border-color: var(--blue-mid);
  box-shadow: 0 10px 22px rgba(28, 58, 99, 0.14);
}
.sheet:hover .sheet-tab, .sheet:focus-visible .sheet-tab { background: var(--blue-mid); }
.sheet:hover .sheet-tab span, .sheet:focus-visible .sheet-tab span { color: var(--white); }
.sheet:hover .sheet-view, .sheet:focus-visible .sheet-view { opacity: 1; }
.sheet:focus-visible { outline: 2px solid var(--blue-mid); outline-offset: 2px; }

/* Sits outside the sheet button — a button inside a button is invalid and
   the browser will not deliver its clicks. */
.sheet-remove {
  position: absolute; top: 8px; right: 8px; z-index: 2;
  width: 24px; height: 24px; padding: 0; line-height: 1;
  display: flex; align-items: center; justify-content: center;
  font-size: 12px; color: var(--white); background: rgba(10, 15, 28, 0.6);
  border: 0; border-radius: 50%; cursor: pointer;
  opacity: 0; transition: opacity 0.15s ease, background 0.15s ease;
}
.sheet-slot:hover .sheet-remove, .sheet-remove:focus-visible { opacity: 1; }
.sheet-remove:hover { background: var(--danger); }
.sheet-remove:focus-visible { outline: 2px solid var(--blue-mid); outline-offset: 2px; }

@keyframes sheet-drop {
  from { opacity: 0; transform: translateY(-6px); }
  to { opacity: 1; transform: none; }
}

@media (max-width: 1120px) {
  .request-layout { grid-template-columns: minmax(0, 1fr); }
  .dossier { position: static; }
}
@media (prefers-reduced-motion: reduce) {
  .sheet-slot { animation: none; }
  .sheet, .sheet-tab, .sheet-tab span, .sheet-view, .sheet-remove { transition: none; }
  .sheet:hover, .sheet:focus-visible { transform: none; }
}

/* =====================================================================
   CSV import (shared by every setup page — see /js/import-modal.js)
   The report is the point of this screen, so the upload target stays plain
   and the row statuses carry the only colour.
   ===================================================================== */
.import-columns { border: 1px solid var(--border); border-radius: 9px; margin-bottom: 16px; }
.import-columns summary {
  cursor: pointer; padding: 10px 14px; font-size: 13px; color: var(--blue-deep); font-weight: 500;
}
.import-columns summary:focus-visible { outline: 2px solid var(--blue-mid); outline-offset: -2px; border-radius: 9px; }
.import-columns[open] summary { border-bottom: 1px solid var(--border); }
.import-col-table { width: 100%; border-collapse: collapse; }
.import-col-table td { padding: 7px 14px; font-size: 12.5px; vertical-align: top; border-bottom: 1px solid var(--border); }
.import-col-table tr:last-child td { border-bottom: none; }
.import-col-table code {
  font-family: 'Tektur', monospace; font-size: 11.5px; color: var(--blue-deep);
  background: rgba(91,150,216,0.1); padding: 2px 6px; border-radius: 4px; white-space: nowrap;
}
.import-req { font-size: 11px; color: var(--danger); white-space: nowrap; }
.import-opt { font-size: 11px; color: var(--gray); white-space: nowrap; }

.import-drop {
  border: 1.5px dashed var(--blue-mid); border-radius: 10px;
  padding: 26px 20px; text-align: center; cursor: pointer; background: var(--surface);
  transition: background 0.15s, border-color 0.15s;
}
.import-drop:hover, .import-drop.dragover { background: rgba(91,150,216,0.06); border-color: var(--blue-deep); }
.import-drop:focus-visible { outline: 2px solid var(--blue-mid); outline-offset: 3px; }
.import-drop-title { font-size: 14px; color: var(--blue-deep); font-weight: 500; margin-bottom: 4px; word-break: break-all; }

.import-invite {
  display: flex; align-items: center; gap: 8px; cursor: pointer;
  margin-top: 14px; font-size: 13px; color: var(--ink);
}
.import-invite input { width: auto; }

.import-chips { display: flex; gap: 8px; flex-wrap: wrap; margin-top: 18px; }
.import-chip {
  font-size: 11.5px; font-weight: 600; padding: 4px 11px; border-radius: 20px;
  text-transform: uppercase; letter-spacing: 0.4px;
}
.import-chip.is-new { background: rgba(47,158,99,0.14); color: var(--success); }
.import-chip.is-dup { background: #EEF1F5; color: var(--gray); }
.import-chip.is-err { background: rgba(196,68,59,0.14); color: var(--danger); }

.import-headline { font-size: 13px; line-height: 1.5; margin-top: 12px; padding: 10px 13px; border-radius: 8px; }
.import-headline.ok { background: rgba(91,150,216,0.08); border: 1px solid rgba(91,150,216,0.3); color: var(--blue-deep); }
.import-headline.warn { background: rgba(192,138,46,0.09); border: 1px solid rgba(192,138,46,0.35); color: var(--warning); }
.import-headline.err { background: rgba(196,68,59,0.08); border: 1px solid rgba(196,68,59,0.3); color: var(--danger); }

.import-rows { max-height: 260px; overflow-y: auto; margin-top: 12px; border: 1px solid var(--border); border-radius: 9px; }
.import-rows table { width: 100%; border-collapse: collapse; }
.import-rows th {
  position: sticky; top: 0; background: var(--white); text-align: left;
  font-family: 'Tektur', sans-serif; font-size: 9.5px; letter-spacing: 1.2px; text-transform: uppercase;
  color: var(--gray); padding: 8px 12px; border-bottom: 1px solid var(--border);
}
.import-rows td { padding: 7px 12px; font-size: 12.5px; border-bottom: 1px solid var(--border); vertical-align: top; }
.import-rows tr:last-child td { border-bottom: none; }
.import-rows tr.is-error td { background: rgba(196,68,59,0.05); }
.import-status { font-size: 10.5px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.5px; white-space: nowrap; }
.import-status.is-new { color: var(--success); }
.import-status.is-duplicate { color: var(--gray); }
.import-status.is-error { color: var(--danger); }

/* Groups one approval queue (payment requests / bills / cheques) inside the
   Home dashboard's single "Awaiting Your Approval" card. */
.queue-block { margin-top: 18px; }
.queue-block:first-of-type { margin-top: 6px; }
.queue-block-label {
  display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap;
  font-family: 'Libre Baskerville', serif; font-size: 14px; color: var(--blue-deep);
  margin-bottom: 10px;
}
.queue-block-label span {
  font-family: 'Tektur', sans-serif; font-size: 9.5px; letter-spacing: 1.2px;
  text-transform: uppercase; color: var(--gray);
}

.attachment-list { list-style: none; }
.attachment-list li { padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 13.5px; }
.attachment-list li:last-child { border-bottom: none; }

.empty-state { text-align: center; padding: 48px 20px; color: var(--gray); }
.empty-state .icon { font-size: 32px; margin-bottom: 10px; }

.spinner {
  width: 18px; height: 18px; border: 2px solid rgba(255,255,255,0.4); border-top-color: #fff;
  border-radius: 50%; animation: spin-fast 0.7s linear infinite; display: inline-block;
}
@keyframes spin-fast { to { transform: rotate(360deg); } }

@media (max-width: 860px) {
  .sidenav { width: 72px; }
  .sidenav-logo, .sidenav a span.label, .nav-user, .logout-btn,
  .sidenav .ooo-state, .sidenav .ooo-btn { display: none; }
  /* With the label gone the icon IS the link, so it goes to the middle of the
     rail and gets room to be hit. Before the icons existed these rows rendered
     as blank strips. */
  .sidenav a { justify-content: center; padding: 13px 0; }
  .sidenav a .icon { opacity: 0.85; }
  .form-grid { grid-template-columns: 1fr; }
  .prf-header { flex-direction: column; }
  .prf-stamp { align-self: stretch; text-align: left; }
  .particulars-head { display: none; }
  .particulars-row { grid-template-columns: 1fr 110px 40px; }
  .card-head { flex-direction: column; align-items: stretch; }
  .search-bar input[type="search"] { width: 100%; max-width: none; }
}

/* ---------------------------------------------------------------------
   Charts on the Super Admin dashboard.

   Hand-authored inline SVG rather than a charting library: the pages are
   classic scripts with no bundler, and one dependency for four bar charts
   would be the largest thing the app loads.

   Text in these charts wears TEXT colours, never the series colour. A value
   painted the same blue as its bar reads as part of the mark and stops being
   legible against the page; the coloured mark beside it already carries the
   identity.
   --------------------------------------------------------------------- */
/* Capped at its own viewBox width. Stretched past it, an SVG scales the TEXT
   too — a 760-unit chart in a 1600px card renders every 12.5px label at 26px,
   which is what made the first version of this dashboard tower over the page.
   It may shrink; it may not grow. */
.chart { display: block; width: 100%; max-width: 760px; height: auto; overflow: visible; }
.chart .ch-label { font-family: 'Work Sans', sans-serif; font-size: 12.5px; fill: var(--ink); }
.chart .ch-value {
  font-family: 'Work Sans', sans-serif; font-size: 12.5px; font-weight: 600;
  fill: var(--blue-deep); font-variant-numeric: tabular-nums;
}
.chart .ch-muted {
  font-family: 'Work Sans', sans-serif; font-size: 11.5px;
  fill: var(--gray); font-variant-numeric: tabular-nums;
}
/* Recessive by design — the axis is scaffolding, the bars are the content. */
.chart .ch-axis { stroke: var(--border); stroke-width: 1; }
.chart .ch-grid { stroke: var(--border); stroke-width: 1; stroke-dasharray: 3 4; }

.ch-legend { display: flex; flex-wrap: wrap; gap: 8px 18px; margin-top: 12px; }
.ch-key { display: inline-flex; align-items: center; gap: 7px; font-size: 12.5px; color: var(--gray); }
.ch-key i { width: 11px; height: 11px; border-radius: 3px; flex: none; }

/* Two charts side by side on a wide screen, stacked on a narrow one. Neither
   is readable below about 420px of width. */
.dash-pair { display: grid; grid-template-columns: repeat(auto-fit, minmax(420px, 1fr)); gap: 20px; }
.dash-pair > .card { margin-bottom: 0; }

/* ---------------------------------------------------------------------
   Bar rows, in HTML.

   One grid, two uses: the PRF pipeline (numbered, because a PRF really does
   pass through those four stages in order) and the per-company spend list
   (.pipe-plain, which drops the numbering — companies are not a sequence, and
   numbering them would imply a rank that the sort order already shows).

   Sized in px against the page's own type scale, so the rows stay the same
   height whether the card is 420px or 1600px wide. Only the track flexes.
   --------------------------------------------------------------------- */
.pipe { list-style: none; margin: 4px 0 0; padding: 0; }
.pipe-row {
  display: grid;
  grid-template-columns: 20px minmax(96px, 168px) 1fr 34px 62px;
  align-items: center;
  gap: 0 12px;
  height: 30px;
}
.pipe-plain .pipe-row { grid-template-columns: minmax(56px, 96px) 1fr 66px; }

/* The rail: the stage numbers are joined so the four read as one route rather
   than four unrelated rows. Drawn on the row, not between them, so it cannot
   fall out of step with the list. */
.pipe-step {
  position: relative;
  width: 18px; height: 18px; border-radius: 50%;
  display: inline-flex; align-items: center; justify-content: center;
  font-size: 10.5px; font-weight: 600; font-variant-numeric: tabular-nums;
  color: var(--gray); background: var(--white); border: 1px solid var(--border);
}
.pipe-row + .pipe-row .pipe-step::before {
  content: ''; position: absolute; left: 50%; top: -13px;
  width: 1px; height: 12px; background: var(--border); transform: translateX(-0.5px);
}
.pipe-row:not(.is-clear) .pipe-step { color: var(--blue-deep); border-color: var(--blue-mid); }

.pipe-name {
  font-size: 12.5px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* The track is the zero line every bar is measured from — visible, so an empty
   stage reads as "nothing here" rather than as a missing row. */
.pipe-track { height: 10px; background: var(--border); border-radius: 5px; overflow: hidden; }
.pipe-bar { display: block; height: 100%; border-radius: 0 5px 5px 0; }
.pipe-count {
  font-size: 12.5px; font-weight: 600; color: var(--blue-deep);
  text-align: right; font-variant-numeric: tabular-nums;
}
.pipe-amt {
  font-size: 11.5px; color: var(--gray);
  text-align: right; font-variant-numeric: tabular-nums;
}
.pipe-amt.strong { font-size: 12.5px; font-weight: 600; color: var(--blue-deep); }
/* A stage with nothing waiting recedes rather than disappearing — the row is
   still information. */
.pipe-row.is-clear .pipe-name,
.pipe-row.is-clear .pipe-count { color: var(--gray); font-weight: 500; }

@media (max-width: 560px) {
  .pipe-row { grid-template-columns: 20px minmax(80px, 1fr) 34px 56px; gap: 0 8px; }
  .pipe-row .pipe-track { display: none; }
  .pipe-plain .pipe-row { grid-template-columns: minmax(56px, 1fr) 66px; }
}

/* ---------------------------------------------------------------------
   The form chooser (/new-document.html).

   Four cards, one per kind of paper this office raises. The ACRONYM is the
   signature — set large in the form's own accent, because "CALF" is what staff
   say and what the pad is labelled, while "Cash Advance Liquidation Form" is
   four words nobody reads twice.

   One accent per form, applied to exactly three things: the glyph, its tinted
   plate, and the acronym. Everything else on the card is the page's own ink,
   so four colours on screen still read as one system.
   --------------------------------------------------------------------- */
/* The chooser is a decision screen with one thing on it, so it sits in the
   middle of the frame rather than tucked into the top-left of a page that is
   otherwise empty. */
.picker-stage {
  min-height: calc(100vh - 190px);
  display: flex;
  align-items: center;
  justify-content: center;
}
.form-picker { width: 100%; max-width: 760px; margin-inline: auto; }

/* Two columns, fixed — not auto-fit. auto-fit packs as many as will fit, which
   at this width meant three across and PCV stranded alone on a second row. Four
   forms want a 2×2 block: every card the same size, nothing orphaned. */
.form-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 18px;
  margin-top: 20px;
}
@media (max-width: 620px) {
  .form-grid { grid-template-columns: minmax(0, 1fr); }
  .picker-stage { min-height: 0; display: block; }
}

.form-card {
  position: relative;
  display: grid;
  grid-template-columns: 46px 1fr;
  grid-template-areas:
    'glyph code'
    'glyph name'
    'blurb blurb'
    'tag   tag';
  /* The blurb row absorbs the slack, so every card's action sits on the same
     baseline however long its description runs. Packed to the top instead, the
     four chips landed at four different heights and the row read as ragged. */
  grid-template-rows: auto auto 1fr auto;
  gap: 2px 14px;
  padding: 20px;
  border: 1px solid var(--border);
  border-radius: 14px;
  background: var(--surface);
  text-decoration: none;
  transition: border-color 0.15s, box-shadow 0.15s, transform 0.15s;
}

.form-glyph-wrap {
  grid-area: glyph;
  width: 46px; height: 46px;
  display: grid; place-items: center;
  border-radius: 12px;
  /* The accent at low alpha, so the plate belongs to the card rather than
     sitting on it as a second block of colour. The flat fallback comes first
     for anything without color-mix — the glyph still reads on it, so the card
     degrades to plainer rather than to broken. */
  background: var(--white);
  background: color-mix(in srgb, var(--accent) 11%, transparent);
}
.form-glyph { width: 24px; height: 24px; }

.form-code {
  grid-area: code;
  align-self: end;
  font-family: 'Outfit', sans-serif;
  font-size: 23px; font-weight: 700; letter-spacing: 1.4px; line-height: 1;
}
.form-name {
  grid-area: name;
  align-self: start;
  font-size: 12.5px; font-weight: 500; color: var(--ink);
  line-height: 1.35;
}
.form-blurb {
  grid-area: blurb;
  margin-top: 14px;
  font-size: 12.5px; line-height: 1.55; color: var(--gray);
}
.form-go, .form-chip {
  grid-area: tag;
  margin-top: 16px;
  justify-self: start;
  display: inline-flex; align-items: center; gap: 5px;
  font-size: 12.5px; font-weight: 600;
}
.form-go { color: var(--accent); }
.form-go svg { width: 15px; height: 15px; transition: transform 0.15s; }
.form-chip {
  font-weight: 500; color: var(--gray);
  background: var(--white); border: 1px solid var(--border);
  border-radius: 999px; padding: 3px 11px; font-size: 11.5px;
}

/* Only the card you can actually use invites a click. */
.form-card.is-open { cursor: pointer; }
.form-card.is-open:hover {
  border-color: var(--accent);
  box-shadow: 0 6px 20px rgba(10, 15, 28, 0.09);
  transform: translateY(-2px);
}
.form-card.is-open:hover .form-go svg { transform: translateX(3px); }
.form-card.is-open:focus-visible {
  outline: 2px solid var(--accent); outline-offset: 3px;
}
/* Not built and not permitted both recede, but stay legible — a form you
   cannot raise is still a form you may need to know exists. */
.form-card.is-soon, .form-card.is-locked { background: var(--white); }
.form-card.is-soon .form-code, .form-card.is-locked .form-code { opacity: 0.62; }
.form-card.is-soon .form-glyph-wrap, .form-card.is-locked .form-glyph-wrap { opacity: 0.62; }

.form-note {
  margin-top: 20px; padding: 12px 16px;
  background: var(--white); border-left: 3px solid var(--warning);
  border-radius: 0 8px 8px 0;
  font-size: 13.5px; color: var(--ink);
}

@media (prefers-reduced-motion: reduce) {
  .form-card, .form-go svg { transition: none; }
  .form-card.is-open:hover { transform: none; }
}

/* The compact reading of the same cards, for the requester's Home: no blurb,
   tighter box, four across where there is room. Same grid, same accents — a
   second layout, not a second component. */
.form-grid.is-compact { grid-template-columns: repeat(auto-fit, minmax(196px, 1fr)); gap: 14px; }
.form-card.is-compact {
  grid-template-areas: 'glyph code' 'glyph name' 'tag tag';
  grid-template-rows: auto 1fr auto;
  padding: 16px;
  gap: 1px 12px;
}
.form-card.is-compact .form-glyph-wrap { width: 40px; height: 40px; border-radius: 10px; }
.form-card.is-compact { grid-template-columns: 40px 1fr; }
.form-card.is-compact .form-glyph { width: 21px; height: 21px; }
.form-card.is-compact .form-code { font-size: 20px; }
.form-card.is-compact .form-name { font-size: 11.5px; }
.form-card.is-compact .form-go,
.form-card.is-compact .form-chip { margin-top: 12px; }

/* ---------------------------------------------------------------------
   The "link this document to a PRF" picker.

   Lived in upload-cheque.html's own <style> block until Upload Bill needed the
   same control. Shared now rather than copied: two stylesheets for one
   component is how the two screens come to look subtly different.
   --------------------------------------------------------------------- */
.prf-search-wrap { position: relative; }
.prf-search-results {
  position: absolute; z-index: 5; top: 100%; left: 0; right: 0; margin-top: 4px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 8px;
  box-shadow: 0 8px 20px rgba(10,15,28,0.14); max-height: 220px; overflow-y: auto;
}
.prf-search-item { padding: 10px 14px; cursor: pointer; border-bottom: 1px solid var(--border); font-size: 13.5px; }
.prf-search-item:last-child { border-bottom: none; }
.prf-search-item:hover { background: rgba(91,150,216,0.06); }
.prf-search-item .prf-no { font-weight: 600; color: var(--blue-deep); }
.prf-search-item .meta { color: var(--gray); font-size: 12px; margin-top: 2px; }

/* A result row is a <button> on Upload Bill so it is reachable by keyboard —
   a div with a click handler is invisible to anyone tabbing through the form. */
button.prf-search-item {
  display: block; width: 100%; text-align: left;
  background: none; border: none; border-bottom: 1px solid var(--border);
  font-family: inherit; color: inherit;
}
button.prf-search-item:last-child { border-bottom: none; }
button.prf-search-item:focus-visible { outline: 2px solid var(--blue-mid); outline-offset: -2px; }

.prf-linked-card {
  border: 1.5px solid var(--blue-mid); border-radius: 10px; padding: 14px 16px; margin-bottom: 16px;
  background: rgba(91,150,216,0.06);
}
/* The card's own header row: identity on the left, the way out on the right.
   On the card itself rather than as an inline style on each page, so both
   screens lay it out the same way. */
.prf-linked-head { display: flex; justify-content: space-between; align-items: flex-start; gap: 12px; }
.prf-linked-card .prf-no { font-family: 'Outfit', sans-serif; font-weight: 600; font-size: 17px; color: var(--blue-deep); }
.prf-linked-card .meta-row { font-size: 13px; color: var(--ink); margin-top: 4px; }
.prf-linked-card .change-link { font-size: 12.5px; flex-shrink: 0; }
.prf-linked-card .mismatch-warn {
  margin-top: 10px; font-size: 12.5px; color: var(--warning);
  background: rgba(192,138,46,0.1); border: 1px solid rgba(192,138,46,0.3); border-radius: 6px; padding: 8px 10px;
}
.prf-linked-card .block-warn {
  margin-top: 10px; font-size: 12.5px; color: var(--danger);
  background: rgba(196,68,59,0.08); border: 1px solid rgba(196,68,59,0.3); border-radius: 6px; padding: 8px 10px;
}

/* (The subcategory branch glyph lived here. Replaced by the drawn rail further
   down — a border survives a wrapping name, a leading character does not.) */

/* ---------------------------------------------------------------------
   Categories — a two-level reference list.

   The list is read far more often than it is edited: accounting keeps it in
   step with the QuickBooks chart of accounts and comes here to find a name, not
   to press a button. The layout follows that.

   What was wrong: three columns in a full-width card. `table { width: 100% }`
   hands all the surplus to the widest column, so on a 1660px screen the name
   sat at x=48 and its own Delete button at x=1150 — 1100px of nothing between a
   row and its actions, and a quarter of the table empty to the right of them.
   Constraining the table and letting only the NAME column stretch puts a row
   back within one glance.
   --------------------------------------------------------------------- */
.cat-card { max-width: 880px; }
.cat-table { min-width: 0; }
/* Name absorbs the slack; every other column shrinks to its contents.
   `width: 1%` with nowrap is the table idiom for "as narrow as this can be".

   THE CASH ADVANCE COLUMN NEEDS ITS OWN RULE. These selectors were written for
   a three-column table — name, status, actions — and a fourth column was added
   between status and actions. `nth-child(2)` and `:last-child` still matched,
   so nothing looked broken in the CSS, but column 3 was left with no rule and
   was the only one able to collapse: it shrank to 82px, broke "On the CA form"
   over four lines, and made those seven rows 81px tall against 46px for every
   other. Named explicitly rather than reached for with a range, so inserting a
   fifth column fails visibly here instead of silently squeezing a neighbour. */
.cat-table th:first-child, .cat-table td:first-child { width: 100%; }
.cat-table th:nth-child(2), .cat-table td:nth-child(2) { width: 1%; white-space: nowrap; }
.cat-table th:nth-child(3), .cat-table td:nth-child(3) { width: 1%; white-space: nowrap; }
.cat-table th:last-child, .cat-table td:last-child { width: 1%; white-space: nowrap; text-align: right; }
.cat-table tbody td { padding: 8px 12px; }

.cat-actions { display: inline-flex; gap: 6px; justify-content: flex-end; }

/* A parent and the rows beneath it read as one block. The rail is the only
   thing carrying the hierarchy once a name wraps, and it is drawn on the cell
   rather than between rows so it cannot fall out of step with the list. */
.cat-table tr.is-child td:first-child {
  padding-left: 30px;
  position: relative;
}
.cat-table tr.is-child td:first-child::before {
  content: '';
  position: absolute; left: 18px; top: 0; bottom: 50%;
  border-left: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
  width: 8px;
  border-bottom-left-radius: 3px;
}
/* The last child closes the rail; the ones above continue it through. */
.cat-table tr.is-child:not(.is-last-child) td:first-child::after {
  content: '';
  position: absolute; left: 18px; top: 50%; bottom: 0;
  border-left: 1px solid var(--border);
}
.cat-name { font-weight: 600; color: var(--ink); }
.cat-sub-name { color: var(--ink); }
.cat-count {
  margin-left: 8px;
  font-family: 'Tektur', sans-serif; font-size: 10.5px; letter-spacing: 0.4px;
  color: var(--gray);
}

/* Destructive, available, and quiet. Forty-eight rows of solid red made the
   loudest thing on a reading screen the one action nobody comes here to take;
   it colours in on the row being pointed at. */
.cat-table .btn-danger {
  background: transparent; color: var(--gray);
  border: 1px solid var(--border);
}
.cat-table tr:hover .btn-danger, .cat-table .btn-danger:focus-visible {
  background: var(--danger); color: #fff; border-color: var(--danger);
}

/* ---------------------------------------------------------------------
   Audit log — a long list read backwards from something that looks wrong.
   --------------------------------------------------------------------- */
.audit-filters {
  display: grid;
  grid-template-columns: minmax(200px, 2fr) repeat(auto-fit, minmax(140px, 1fr));
  gap: 12px;
  margin: 16px 0 4px;
}
.audit-filters .form-row { margin-bottom: 0; }

/* Which rows the page opens on. Two options rather than a checkbox, because
   "Changes only" and "All activity" are both legitimate ways to read an audit
   log and neither is an opt-out from the other. */
.audit-scope { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }
.audit-scope-opt {
  display: flex; align-items: flex-start; gap: 9px; cursor: pointer;
  border: 1px solid var(--border); border-radius: 10px; padding: 10px 14px;
  background: var(--surface); font-size: 13.5px; color: var(--ink);
}
.audit-scope-opt:hover { border-color: var(--blue-mid); }
.audit-scope-opt:has(input:checked) { border-color: var(--blue-deep); background: #F7FAFE; }
.audit-scope-opt input { margin-top: 2px; width: auto; flex-shrink: 0; }
.audit-scope-what { display: block; font-size: 12px; color: var(--gray); margin-top: 2px; }

/* A sentence rather than a diff — what somebody declared when filing, or why a
   payment was sent back. */
.audit-note {
  margin-top: 4px; font-size: 12.5px; color: var(--gray); line-height: 1.5;
  border-left: 2px solid var(--border); padding-left: 9px;
}

.audit-table td { vertical-align: top; font-size: 13px; }
.audit-when { white-space: nowrap; color: var(--gray); font-size: 12.5px; }
.audit-ref { white-space: nowrap; font-variant-numeric: tabular-nums; }
.audit-detail { color: var(--ink); line-height: 1.5; }

/* The action is what the eye runs down the column looking for. The corrective
   and destructive ones carry weight; the routine ones stay quiet, so a page of
   approvals does not shout as loudly as the one cancellation in it. */
.audit-action {
  display: inline-block; white-space: nowrap;
  font-size: 12.5px; font-weight: 500; color: var(--ink);
}
.audit-action.is-heavy { font-weight: 600; color: var(--danger); }
.audit-row.is-heavy > td:first-child { box-shadow: inset 3px 0 0 var(--danger); }

/* Before → after, on its own line per field. The old value is struck through
   because "what did it used to say" is the question, and a reader should not
   have to work out which of the two is which. */
.audit-change {
  display: flex; flex-wrap: wrap; align-items: baseline; gap: 6px;
  margin-top: 4px; font-size: 12.5px;
}
.audit-field {
  font-family: 'Tektur', sans-serif; font-size: 10.5px; letter-spacing: 0.4px;
  text-transform: uppercase; color: var(--gray); min-width: 92px;
}
.audit-was { color: var(--gray); text-decoration: line-through; }
.audit-arrow { color: var(--gray); }
.audit-now { color: var(--ink); font-weight: 500; }

/* ---------------------------------------------------------------------
   The petty cash drawer, on the group dashboard.

   Everything here is deliberately in the CASH ADVANCE module's own palette
   (--ca and friends, defined in cash-advance.css, which index.html already
   loads). That green means "cash advance" across seven screens; borrowing it
   makes the panel legible as a different instrument before a word is read,
   where a new colour would only read as a different product.

   The boldness is spent in ONE place — the aging rail — and everything around
   it is kept quiet on purpose.
   --------------------------------------------------------------------- */
.ca-dash .ca-dash-hero { margin: 18px 0 14px; }

/* Tektur for the caption, as on every cash advance screen: these are keys into
   the data rather than prose, and the face is the module's tell. */
.ca-dash-cap {
  display: block; font-family: 'Tektur', monospace; font-size: 9.5px;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--gray);
  margin-bottom: 4px;
}
.ca-dash-figure {
  display: block; font-family: 'Outfit', sans-serif; font-size: 34px;
  font-weight: 600; line-height: 1.05; color: var(--ca-deep);
  font-variant-numeric: tabular-nums;
}
.ca-dash-sub { display: block; margin-top: 5px; font-size: 12.5px; color: var(--gray); }
/* Zero is still an answer, so it is set in the same place and the same size as
   a real figure — only quieter, so a full drawer never reads as an empty one. */
.ca-dash-figure.is-clear { color: var(--gray); font-weight: 300; }

/* The signature: one rail, the money out of the drawer, split by how long it
   has been out. Segments are flex-grown by AMOUNT, so the widths are the
   figures — nothing here is drawn to a scale of its own. */
.ca-age {
  display: flex; gap: 3px; height: 14px; border-radius: 7px;
  overflow: hidden; background: var(--ca-tint);
}
.ca-age-seg { display: block; min-width: 4px; }

/* The key is an AXIS, not a legend: it is the same flex row as the rail, with
   each item growing by the same amount as the segment above it, so every figure
   sits under the bar it belongs to. That is why there are no colour swatches
   here — position already says which segment each label names, and a swatch
   would be repeating it. */
.ca-age-key {
  list-style: none; margin: 8px 0 0; padding: 0;
  display: flex; gap: 3px;
}
.ca-age-item { min-width: 0; padding-right: 8px; }
.ca-age-amt {
  display: block; font-size: 13px; font-weight: 600; color: var(--ca-deep);
  font-variant-numeric: tabular-nums; line-height: 1.35;
}
.ca-age-label {
  display: block; font-size: 11.5px; color: var(--gray);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
/* An empty bucket keeps its place in both rows. "Nothing is more than 30 days
   out" is worth reporting, and a row that simply vanished would leave the
   reader unsure whether it had been checked at all. */
.ca-age-seg.is-clear { opacity: 0.3; }
.ca-age-item.is-clear .ca-age-amt { color: var(--gray); font-weight: 500; }

.ca-dash-band {
  margin: 22px 0 10px; padding-bottom: 6px;
  border-bottom: 1px solid var(--ca-rule);
  font-family: 'Tektur', monospace; font-size: 9.5px;
  letter-spacing: 0.14em; text-transform: uppercase; color: var(--ca-deep);
}

.ca-wait { list-style: none; margin: 0; padding: 0; }
.ca-wait li {
  display: grid; grid-template-columns: 1fr 40px 68px;
  align-items: center; gap: 0 10px; padding: 7px 0;
  border-bottom: 1px solid var(--border);
}
.ca-wait li:last-child { border-bottom: none; }
.ca-wait-name { font-size: 12.5px; color: var(--ink); }
.ca-wait-n {
  font-size: 12.5px; font-weight: 600; color: var(--ca-deep);
  text-align: right; font-variant-numeric: tabular-nums;
}
.ca-wait-amt {
  font-size: 11.5px; color: var(--gray);
  text-align: right; font-variant-numeric: tabular-nums;
}

/* Borrows the .pipe-plain grid — a name, a bar, a figure — but the bar takes
   its colour from how long that person has held the cash, so the list reads as
   a chase list rather than a leaderboard. */
.ca-holders .pipe-row { grid-template-columns: minmax(120px, 210px) 1fr 92px; }
.ca-holders .pipe-amt.strong { color: var(--ca-deep); }
@media (max-width: 640px) {
  .ca-holders .pipe-row { grid-template-columns: minmax(90px, 1fr) 92px; }
}

/* Below this the three axis columns cannot hold their labels, so the key
   becomes a plain stacked list and the rail keeps its proportions alone. */
@media (max-width: 640px) {
  .ca-dash-figure { font-size: 28px; }
  .ca-age-key { display: block; }
  .ca-age-item { display: flex; justify-content: space-between; align-items: baseline;
    gap: 12px; padding: 4px 0; border-bottom: 1px solid var(--border); }
  .ca-age-item:last-child { border-bottom: none; }
  .ca-age-label { text-align: right; }
}

/* What has come back. A line of context under the hero, not a second figure
   competing with it — the money still OUT is what anybody has to act on. */
.ca-dash-settled { margin: 10px 0 0; font-size: 12.5px; color: var(--gray); }

/* The latest advances, whatever state they are in.
   The drawer card reported only outstanding money, so the moment the last
   advance was liquidated it went blank — indistinguishable from an office that
   had never raised one. A settled advance is still a record somebody opens. */
.ca-recent { list-style: none; margin: 0; padding: 0; }
.ca-recent-row {
  display: grid; grid-template-columns: 132px minmax(0, 1fr) 92px 116px minmax(0, 130px);
  align-items: center; gap: 0 12px; padding: 8px 0;
  border-bottom: 1px solid var(--border);
}
.ca-recent-row:last-child { border-bottom: none; }
.ca-recent-no { font-family: 'Tektur', monospace; font-size: 12px; color: var(--ca-deep, #08503F); }
.ca-recent-who {
  font-size: 12.5px; color: var(--ink);
  white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
}
.ca-recent-amt {
  font-size: 12.5px; font-weight: 600; color: var(--ink);
  text-align: right; font-variant-numeric: tabular-nums;
}
/* The state in words, not a colour alone — the row has to read in a screenshot
   and in print. Colour is the second signal, never the only one. */
.ca-recent-state { font-size: 11.5px; color: var(--gray); }
.ca-recent-state.is-done { color: var(--ca, #0E8A6C); }
.ca-recent-state.is-out { color: #C08A2E; }
.ca-recent-state.is-no { color: var(--danger); }
.ca-recent-link { font-size: 11.5px; text-align: right; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }

@media (max-width: 720px) {
  .ca-recent-row { grid-template-columns: minmax(0, 1fr) 92px; row-gap: 2px; }
  .ca-recent-who, .ca-recent-link { grid-column: 1 / -1; }
}
