/* Requestable
   Palette: navy #0a3a68 (shell, headings), teal #3ab1a2 (actions),
   status: approved #93ba32, denied #e01050, info requested #eac200, awaiting #e8641a */

:root {
  --navy: #0a3a68;
  --navy-dark: #072a4d;
  --teal: #3ab1a2;
  --teal-dark: #2d8b7f;
  --green: #93ba32;
  --pink: #e01050;
  --yellow: #eac200;
  --orange: #e8641a;
  --grey-bg: #f4f6f8;
  --grey-line: #dde3e9;
  --text: #1a2733;
  --text-muted: #5c6b7a;
  --surface: #ffffff;
}

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

/* The browser's own rule for [hidden] is beaten by any class that sets display,
   so a hidden button styled with .btn stays on the page. Everything here that
   appears and disappears uses the attribute, so it is made to mean what it says
   rather than each component remembering to hide itself twice. */
[hidden] { display: none !important; }

body {
  font-family: Inter, -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
  font-size: 14px;
  line-height: 1.45;
  color: var(--text);
  background: var(--grey-bg);
  min-height: 100vh;
  display: flex;
  flex-direction: column;
}

/* Header */
.app-header { background: var(--surface); border-bottom: 1px solid var(--grey-line); }
.header-inner {
  max-width: 1280px; margin: 0 auto; padding: 10px 20px;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.brand { display: flex; align-items: center; gap: 10px; text-decoration: none; }
/* Height fixed, width free, but capped: an uploaded logo can be any shape, and a
   very wide one must not push the name out of the header. */
.brand-logo { height: 34px; width: auto; max-width: 220px; object-fit: contain; display: block; }
.brand-text { display: flex; flex-direction: column; }
.brand-name { color: var(--navy); font-weight: 700; font-size: 15px; letter-spacing: -0.01em; }
.brand-sub { color: var(--text-muted); font-size: 11.5px; }
.user-box { display: flex; align-items: baseline; gap: 10px; font-size: 13px; }
.user-name { font-weight: 600; color: var(--navy); }
.user-school { color: var(--text-muted); }
.logout { color: var(--teal-dark); text-decoration: none; }
.logout:hover { text-decoration: underline; }

/* Navigation */
.main-nav { background: var(--navy); }
.nav-inner { max-width: 1280px; margin: 0 auto; padding: 0 20px; display: flex; }
.main-nav a {
  color: rgba(255, 255, 255, 0.82); text-decoration: none; padding: 10px 14px;
  font-size: 13.5px; font-weight: 500; border-bottom: 2px solid transparent;
  /* Centre the label within the (stretched) anchor so single-line and wrapped
     two-line items line up on a shared middle line when the nav wraps. */
  display: flex; align-items: center; justify-content: center; text-align: center;
}
.main-nav a:hover { color: #fff; }
.main-nav a.active { color: #fff; border-bottom-color: var(--teal); }
.nav-items { display: flex; }
.nav-burger { display: none; }

/* Layout */
.content { max-width: 1280px; width: 100%; margin: 0 auto; padding: 20px; flex: 1; }
.page-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 14px; }
h1 { color: var(--navy); font-size: 20px; font-weight: 700; letter-spacing: -0.01em; }
h2 { color: var(--navy); font-size: 14px; font-weight: 700; margin-bottom: 10px; }
h3 { color: var(--navy); font-size: 13px; font-weight: 600; margin: 12px 0 4px; }
.breadcrumb { font-size: 12.5px; color: var(--text-muted); margin-bottom: 2px; }
.breadcrumb a { color: var(--teal-dark); text-decoration: none; }
.breadcrumb a:hover { text-decoration: underline; }

.panel { background: var(--surface); border: 1px solid var(--grey-line); border-radius: 6px; padding: 14px 16px; margin-bottom: 14px; }
.panel.narrow { max-width: 480px; margin: 60px auto; text-align: center; }
.panel.narrow p { margin: 10px 0; }
/* A card that asks for something rather than just saying something: the prose
   stays centred, the form does not, because a centred label above a full-width
   control belongs to nothing in particular. */
.panel.narrow form { text-align: left; margin-top: 20px; }
.panel.narrow .form-actions { justify-content: center; }

/* Chrome-free auth screens (sign-in) */
.auth-body .content { padding-top: 8px; }
.auth-wrap { max-width: 780px; margin: 0 auto; }
/* Sized by both dimensions because a customer's uploaded logo can be any shape:
   a wide wordmark fills the width, a square mark is held to a sensible height
   instead of being blown up to 360px across. */
.auth-logo {
  display: block; width: auto; height: auto;
  max-width: min(360px, 82%); max-height: 110px;
  margin: 40px auto 28px;
}
/* Shown instead of a logo when a customer has typed their name but not uploaded a
   picture yet: their name, set like the wordmark it stands in for. */
.auth-wordmark {
  text-align: center; color: var(--navy); font-size: 26px; font-weight: 700;
  letter-spacing: -0.01em; margin: 44px auto 28px; line-height: 1.25;
}
.auth-wrap .panel { padding: 26px 28px; }
.auth-wrap .panel h1 { margin-bottom: 16px; }
.auth-wrap .dev-banner { margin-bottom: 20px; }
/* These panels carry prose rather than a form's worth of labels: emergency access
   explains itself, and the page after a restore explains why the person reading it
   has just been signed out. Elsewhere in the application the gap between stacked
   blocks comes from the classes on them, and plain paragraphs run together, which
   here would read as one long block nobody finishes. 14px matches .warn-box, so a
   paragraph followed by a notice and a paragraph followed by a paragraph sit the
   same distance apart. */
.auth-wrap .panel p + p,
.auth-wrap .panel p + .inset-panel { margin-top: 14px; }
/* The sign-in card. .panel.narrow already centres the card and its text, so
   these are the two things it does not cover: one action big enough to be the
   only thing on the page worth doing, and the quieter lines around it. */
.auth-signin .auth-lead { color: var(--text-muted); margin-top: -4px; }
/* The customer's own name under the card, so their identity brackets the one
   thing on the page rather than sitting only above it. Comes from the footer
   line they set at Admin > Settings, the same line every other page carries. */
/* The sign-in page carries both identities: the product's wordmark above the
   card, the customer's own logo inside it under the heading. Sized by both
   dimensions for the same reason as .auth-logo, since an uploaded logo can be
   a wide lockup or a square crest. */
/* .panel.narrow carries 60px of its own top margin, which is right for a lone
   card on an empty page and too much under a wordmark that belongs with it. The
   card's margin is dropped here so the gap is this block's alone. */
.auth-signin-brand { margin: 44px auto 22px; text-align: center; }
/* An explicit width rather than a max: the wordmark is 206px across at its
   natural size, so a max-width alone can only ever shrink it. */
.auth-signin-brand .auth-logo { width: 250px; max-width: 82%; margin: 0 auto; }
/* The sign-in card is the whole page, so it is set larger than the cards that
   sit inside the application: a wider measure, more padding, and type a step up
   from the 14px the dense screens use. */
.auth-wrap .panel.narrow { max-width: 560px; margin-top: 0; padding: 34px 36px; }
.auth-signin h1 { font-size: 26px; }
.auth-signin p { font-size: 15.5px; }
.auth-signin-logo {
  display: block; width: auto; height: auto;
  max-width: 86%; max-height: 124px;
  margin: 28px auto 26px;
}
/* The strapline is pulled up under the heading by .auth-lead, which is right
   when it follows the heading directly and wrong when a logo sits between. */
.auth-signin-logo + .auth-lead { margin-top: 0; }
.auth-customer { color: var(--navy); font-size: 17px; font-weight: 600; letter-spacing: -0.01em; }

/* Google's sign-in button, from the markup and CSS published at
   developers.google.com/identity/branding-guidelines, kept close to their
   source so it can be diffed against it. Two deliberate departures: their
   @import of Roboto is dropped, because the page's Content-Security-Policy
   allows no external stylesheet and their own fallback stack (arial, sans-serif)
   is what then applies; and the button is centred in the card by the wrapper
   below rather than by their layout, which assumes a left-aligned form. */
.gsi-form { margin-top: 16px; display: flex; justify-content: center; }
.gsi-material-button {
  -moz-user-select: none; -webkit-user-select: none; -ms-user-select: none; user-select: none;
  -webkit-appearance: none; background-color: WHITE; background-image: none;
  border: 1px solid #747775; -webkit-border-radius: 20px; border-radius: 20px;
  box-sizing: border-box; color: #1f1f1f; cursor: pointer;
  font-family: 'Roboto', arial, sans-serif; font-size: 14px; height: 40px;
  letter-spacing: 0.25px; outline: none; overflow: hidden; padding: 0 12px;
  position: relative; text-align: center;
  -webkit-transition: background-color .218s, border-color .218s, box-shadow .218s;
  transition: background-color .218s, border-color .218s, box-shadow .218s;
  vertical-align: middle; white-space: nowrap; width: auto; max-width: 400px; min-width: min-content;
}
.gsi-material-button .gsi-material-button-icon { height: 20px; margin-right: 12px; min-width: 20px; width: 20px; }
.gsi-material-button .gsi-material-button-content-wrapper {
  align-items: center; display: flex; flex-direction: row; flex-wrap: nowrap;
  height: 100%; justify-content: space-between; position: relative; width: 100%;
}
.gsi-material-button .gsi-material-button-contents {
  flex-grow: 1; font-family: 'Roboto', arial, sans-serif; font-weight: 500;
  overflow: hidden; text-overflow: ellipsis; vertical-align: top;
}
.gsi-material-button .gsi-material-button-state {
  transition: opacity .218s; bottom: 0; left: 0; opacity: 0; position: absolute; right: 0; top: 0;
}
.gsi-material-button:disabled { cursor: default; background-color: #ffffff61; border-color: #1f1f1f1f; }
.gsi-material-button:disabled .gsi-material-button-contents,
.gsi-material-button:disabled .gsi-material-button-icon { opacity: 38%; }
.gsi-material-button:not(:disabled):active .gsi-material-button-state,
.gsi-material-button:not(:disabled):focus .gsi-material-button-state { background-color: #303030; opacity: 12%; }
.gsi-material-button:not(:disabled):hover { box-shadow: 0 1px 2px 0 rgba(60, 64, 67, .30), 0 1px 3px 1px rgba(60, 64, 67, .15); }
.gsi-material-button:not(:disabled):hover .gsi-material-button-state { background-color: #303030; opacity: 8%; }
/* Tables */
.data-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.data-table th {
  text-align: left; font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em;
  color: var(--text-muted); font-weight: 600; padding: 6px 10px; border-bottom: 1px solid var(--grey-line);
}
.data-table td { padding: 7px 10px; border-bottom: 1px solid var(--grey-line); vertical-align: middle; }
/* One alignment rule for every table in the application. The first column is
   what the row is about and reads left; everything after it is centred,
   including status chips and row buttons. Tables that each made their own mind
   up column by column were the reason two screens never looked like the same
   product. */
.data-table th, .data-table td { text-align: center; }
.data-table th:first-child, .data-table td:first-child { text-align: left; }
/* A column of sentences reads left, wherever it sits: a requisition title, an
   email subject, an audit detail. Centring is for the short values a row is
   scanned and compared on, and a wrapped sentence centred over two lines is
   neither scannable nor comparable. */
.data-table th.text-col, .data-table td.text-col { text-align: left; }
/* A form laid out as a table is not a table of data. Its inputs, and the text
   somebody types into them, start at the left like every other field on the
   page; only the short numeric columns are centred, as they always were. */
.data-table.line-items th, .data-table.line-items td { text-align: left; }
.data-table.line-items th.ctr, .data-table.line-items td.ctr { text-align: center; }
/* A total's label still hugs the figure to its right. */
.data-table td.total-label { text-align: right; }
/* A tick or radio column sits ahead of the name without becoming the column the
   row reads from, so the name after it keeps the left alignment. */
.select-col, .select-cell { width: 34px; padding-right: 0; }
.data-table .select-col + th, .data-table .select-cell + td { text-align: left; }
.data-table tbody tr:hover { background: #f8fafb; }
.data-table tfoot td { border-bottom: none; font-weight: 600; padding-top: 10px; }
/* Money columns are centred in the application. Right alignment is used only on
   the purchase order, which is a printed document where the figures stack. */
.num { text-align: center; font-variant-numeric: tabular-nums; }
th.num { text-align: center; }
/* Spend by supplier puts an amount and what it counts in one cell: the money on
   top, because that is what the row is read for, and the count under it in words,
   because a bare number beside an amount is a puzzle. */
.cell-amount { display: block; }
.cell-note { display: block; color: var(--text-muted); font-size: 12px; margin-top: 1px; }
.data-table td.strong { font-weight: 600; }
/* A total's label sits immediately left of its figure, so it hugs it. */
.total-label { text-align: right; color: var(--text-muted); text-transform: uppercase; font-size: 11.5px; letter-spacing: 0.04em; }
.muted { color: var(--text-muted); }
.row-title { color: var(--text); text-decoration: none; font-weight: 500; }
.row-title:hover { color: var(--teal-dark); }
/* Plain links in tables are teal; button-styled anchors keep their own colours
   (e.g. white text on a teal primary button) rather than the link colour. */
.data-table a:not(.btn) { color: var(--teal-dark); }
/* A number is hard to read through an underline: "1" underlined reads as a
   different glyph entirely. Numeric cells drop it and rely on the colour, and
   bring it back on hover so the link is still discoverable. */
.data-table td.num a:not(.btn) { text-decoration: none; }
.data-table td.num a:not(.btn):hover, .data-table td.num a:not(.btn):focus-visible {
  text-decoration: underline;
}
.empty-state { color: var(--text-muted); padding: 24px; text-align: center; }

/* Status chips */
.chip {
  display: inline-block; padding: 2px 9px; border-radius: 10px;
  font-size: 11.5px; font-weight: 600; white-space: nowrap;
}
/* Paid sits alongside approved: both are green, because both are good news, and a
   claimant scanning their list is looking for the difference between them in the
   label, not the colour. */
.chip-approved, .chip-paid { background: rgba(147, 186, 50, 0.16); color: #55701a; }
.chip-denied { background: rgba(224, 16, 80, 0.12); color: #a30c3a; }
.chip-info_requested { background: rgba(234, 194, 0, 0.18); color: #755f00; }
.chip-submitted, .chip-awaiting_approval { background: rgba(232, 100, 26, 0.13); color: #a34310; }
.chip-draft, .chip-cancelled { background: rgba(92, 107, 122, 0.12); color: var(--text-muted); }
.chip-band { background: rgba(10, 58, 104, 0.1); color: var(--navy); }

/* Buttons */
.btn {
  display: inline-block; border: 1px solid transparent; border-radius: 5px;
  padding: 7px 14px; font-size: 13.5px; font-weight: 600; cursor: pointer;
  text-decoration: none; font-family: inherit; line-height: 1.3;
  /* A label split across two lines reads as two buttons. */
  white-space: nowrap;
}
.btn-primary { background: var(--teal); color: #fff; }
.btn-primary:hover { background: var(--teal-dark); }
.btn-secondary { background: #fff; color: var(--teal-dark); border-color: var(--teal); }
.btn-secondary:hover { background: rgba(58, 177, 162, 0.08); }
.btn-ghost { background: transparent; color: var(--text-muted); }
.btn-ghost:hover { color: var(--text); }
/* Removing a line is destructive but minor: a soft red pill, not a solid
   button, so it reads as available without competing with the real actions. */
.btn-remove {
  background: rgba(224, 16, 80, 0.09); color: #a30c3a; border-radius: 999px;
  border-color: rgba(224, 16, 80, 0.22);
}
.btn-remove:hover { background: rgba(224, 16, 80, 0.16); }
.btn-small { padding: 4px 10px; font-size: 12.5px; }
.btn:focus-visible, a:focus-visible, input:focus-visible, select:focus-visible, textarea:focus-visible, button:focus-visible {
  outline: 2px solid var(--teal); outline-offset: 1px;
}

/* Forms */
.field { margin-bottom: 12px; }
.field label { display: block; font-size: 12.5px; font-weight: 600; color: var(--navy); margin-bottom: 4px; }
.field input[type="text"], .field input[type="number"], .field input[type="search"],
.field input[type="email"], .field input[type="date"], .field input[type="password"],
.field select, .field textarea {
  width: 100%; padding: 7px 9px; border: 1px solid var(--grey-line); border-radius: 5px;
  font-size: 13.5px; font-family: inherit; color: var(--text); background: #fff;
  -webkit-appearance: none; appearance: none;
}
/* Dropdowns. appearance:none is needed so a select matches the other inputs,
   but that also removes the arrow, which makes a select look like a plain text
   box. Draw the chevron back on so every dropdown reads as one. */
.field select, .search-row select, .filter-row select, .line-items select, .gov-mark select {
  -webkit-appearance: none; appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='10' height='6'%3E%3Cpath fill='%230a3a68' d='M0 0h10L5 6z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 9px center;
  padding-right: 26px;
  cursor: pointer;
}
.line-items select { background-position: right 6px center; padding-right: 20px; }

.field-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px; }
.req { color: var(--pink); }
.radio-row { display: flex; gap: 18px; padding: 4px 0; }
.radio { display: flex; align-items: center; gap: 6px; font-size: 13.5px; }
/* A choice where each option needs a sentence to explain what it does to the
   data (merging suppliers). The options stack and each is a block, because a row
   of bare words cannot carry the difference between them. */
.radio-stack { display: flex; flex-direction: column; gap: 10px; margin-top: 6px; }
.radio-block {
  display: flex; gap: 10px; align-items: flex-start; cursor: pointer;
  border: 1px solid var(--grey-line); border-radius: 6px; padding: 11px 13px;
  font-size: 13.5px; line-height: 1.5;
}
.radio-block:hover { border-color: var(--teal); }
.radio-block input { margin-top: 3px; flex: none; }
.radio-block strong { color: var(--navy); }
/* A rule the reader must not miss, said once, where it applies. Not a warning,
   because nothing is wrong yet, and not small print. */
.callout {
  background: #f3f9f9; border: 1px solid #cfe4e4; border-radius: 6px;
  padding: 10px 14px; margin-bottom: 14px; font-size: 13.5px; line-height: 1.5;
}
.hint { font-size: 13px; color: var(--text-muted); margin-top: 8px; line-height: 1.5; }
/* Breathing room when an introductory hint sits directly above a table or
   empty-state (e.g. the governance queue and admin tables). .table-scroll is the
   wrapper a wide table sits in, and needs the same treatment as a bare table. */
.hint + .data-table, .hint + .empty-state, .hint + .table-scroll { margin-top: 16px; }
/* The same rule for the other things a hint is followed by. An introductory line
   running flush into the form it introduces reads as one block, and the reader
   cannot tell where the explanation stops and the work starts. Measured across
   every console screen rather than spotted: six places had a nought-pixel gap. */
.hint + form, .hint + .field-row, .hint + .detail-list, .hint + .warn-box { margin-top: 16px; }
.detail-list + .empty-state, .empty-state + form, .detail-list + form { margin-top: 14px; }
/* The same 16px above and below a panel's introductory line, so the heading, the
   line explaining the panel and its first field are evenly spaced. Without the
   second rule the line sits flush against the first field's label and reads as
   part of it rather than as the panel's own explanation. */
.panel > h2 + .hint { margin-top: 16px; }
.panel > .hint + form { margin-top: 16px; }
/* Buttons at the end of a form: clear of the last field above them, and of
   anything that follows. */
/* Buttons need clear water above them: the line before is usually a hint about
   what the button is going to do, and the two should not read as one block. */
.form-actions { display: flex; gap: 10px; align-items: center; margin: 22px 0 24px; }
/* A button block placed inside a .field-row was being laid out as one more grid
   cell, so it sat beside the last input rather than under the form, at whatever
   vertical offset its own margin gave it: the button and the field next to it
   visibly disagreed about where the row was. It takes a row of its own instead.
   The console's forms are built this way throughout, and the app has a few too. */
.field-row > .form-actions { grid-column: 1 / -1; margin: 6px 0 0; }

/* The logo in use, shown beside the upload field at the size it appears in the
   header, so an administrator can see what they are replacing. The box holds a
   tall or a wide picture without either one moving the text beside it. */
.logo-current { display: flex; align-items: center; gap: 12px; margin-bottom: 10px; font-size: 13px; }
.logo-preview { height: 34px; width: auto; max-width: 200px; display: block; }
/* A second form inside a panel, for an action that is separate from the one the
   main form does (removing the logo, as against saving the wording). */
.inline-form { border-top: 1px solid var(--grey-line); margin-top: 4px; padding-top: 14px; }
.inline-form .hint { margin-top: 0; margin-bottom: 12px; }

.line-items input, .line-items select { width: 100%; padding: 5px 7px; border: 1px solid var(--grey-line); border-radius: 4px; font-size: 13px; font-family: inherit; background-color: #fff; color: var(--text); }
/* Every column except the description is fixed, so the description takes all
   the remaining width and the Remove button sits hard against the right edge. */
.line-items .sku-col { width: 150px; }
.line-items .qty-col { width: 70px; }
.line-items .cost-col { width: 120px; }
/* Wide enough for the longest choice, Add 20% VAT, with the arrow beside it. */
.line-items .vat-col { width: 140px; }
.line-items .total-col { width: 100px; }
.line-items .remove-col { width: 96px; }
.line-items td.remove-cell { text-align: right; }
/* Read-only line items (requisition detail): the item number column is sized to
   its heading so it does not wrap and squeeze the description. */
.sku-col-read { width: 130px; }
/* On a narrow screen the fixed columns would squeeze the description to
   nothing, so the table scrolls inside the panel rather than the page. */
.table-scroll { overflow-x: auto; }
.table-scroll .line-items { min-width: 810px; }
/* A list table with more columns than a narrow window can hold. The floor stops
   the columns being squeezed to nothing; past that the table scrolls inside its
   panel, so the page itself never scrolls sideways. */
.table-scroll .wide-table { min-width: 720px; }
.line-items input.num { text-align: center; }
.line-total { white-space: nowrap; }
.nowrap { white-space: nowrap; }

.error-box {
  background: rgba(224, 16, 80, 0.07); border: 1px solid rgba(224, 16, 80, 0.35);
  border-radius: 6px; padding: 10px 14px; margin-bottom: 14px; font-size: 13px;
}
.error-box ul { margin: 6px 0 0 18px; }

/* An error on a page long enough that its top is somewhere else. A server-rendered
   banner arrives with the page scrolled back to the top, which loses the reader's
   place at the moment they need it; this floats over the page instead, so it is in
   view wherever they were, and it can be put away because an error that cannot be
   dismissed is in the way of fixing it. Deliberately not a trapping modal: it is a
   validation message, not a decision. */
.error-modal {
  position: fixed; top: 14px; left: 50%; transform: translateX(-50%);
  z-index: 40; width: min(660px, calc(100% - 28px));
  display: flex; align-items: flex-start; gap: 14px;
  background: #fff7f9; border: 1px solid rgba(224, 16, 80, 0.35);
  border-radius: 6px; padding: 12px 14px;
  box-shadow: 0 8px 28px rgba(10, 39, 51, 0.18);
}
.error-modal-text { font-size: 13px; }
.error-modal-title {
  display: block; font-weight: 700; color: var(--pink); font-size: 12px;
  text-transform: uppercase; letter-spacing: 0.04em; margin-bottom: 3px;
}
/* Dismiss is a link to the page itself, so it still works with no JavaScript;
   app.js upgrades it to close the banner without a round trip. */
.error-modal-dismiss {
  margin-left: auto; flex: none; align-self: center;
  font-size: 12.5px; font-weight: 600; color: var(--text-muted);
  text-decoration: none; padding: 4px 6px; border-radius: 4px;
}
.error-modal-dismiss:hover { color: var(--text); background: var(--grey-bg); }

/* A panel the browser has jumped to by anchor leaves room above itself, so the
   floating error banner that sent the reader there is not sitting on top of the
   first thing they need to read. 96px clears the banner and its shadow. */
.panel[id] { scroll-margin-top: 96px; }

/* A passphrase is a short thing to type, so it stops well short of a wide panel:
   full width would put the caret a foot from its label and make the box read as a
   text area. */
.field input[type="password"] { max-width: 460px; }

/* A passphrase and its confirmation, stacked. Side by side they read as two
   different things to type; one under the other is the shape everybody expects,
   and the pair is held to a sensible line length rather than spanning the panel. */
.pass-stack { max-width: 460px; }

/* Policy notices from the finance policy (Annex A duties, tender, related
   party, capitalisation). Informational, distinct from errors. */
.notice-panel { background: #fffcef; border-color: #ecdca0; }
.notice-list { margin: 0; padding-left: 18px; font-size: 13px; }
.notice-list li { margin: 4px 0; }
.budget-confirm { margin-bottom: 2px; }

.ctr { text-align: center; }
th.ctr { text-align: center; }
.line-items td.ctr input, .line-items td.ctr select { text-align: center; }

.notice-box {
  background: rgba(234, 194, 0, 0.1); border: 1px solid rgba(234, 194, 0, 0.45);
  border-radius: 5px; padding: 8px 12px; font-size: 13px; margin-bottom: 14px;
}

/* An action offered by the notice itself, for putting right what the notice has
   just reported: "deleted ... Undo". Sits on the same line as the sentence. */
.notice-box .notice-action { display: inline-block; margin-left: 10px; vertical-align: middle; }

/* Stronger than a notice: something is not right and the reader may be about to
   act on it (for example approving against a supplier nobody has approved). */
.warn-box {
  background: rgba(232, 100, 26, 0.09); border: 1px solid rgba(232, 100, 26, 0.4);
  border-left-width: 3px; border-radius: 5px; padding: 10px 14px; font-size: 13px;
  margin: 14px 0; line-height: 1.5;
}
.warn-box strong { color: #a34310; }
/* A pill on its own line, with the explanation under it. The margins are not
   equal because the gaps they produce are: the pill is a hard-edged box, while
   the text below it carries half a line of leading above its first line, so it
   needs roughly three pixels less to sit the same distance away. */
.warn-box .warn-pill { margin-top: 3px; margin-bottom: 11px; }
.action-panel .warn-box { margin: 0 0 12px; }

/* Button with a menu (the "New" button). The menu is closed markup-side, so it
   is never in the way for anyone browsing without scripting. */
.menu { position: relative; display: inline-block; }
.menu-toggle { display: inline-flex; align-items: center; gap: 8px; }
.menu-list {
  position: absolute; right: 0; top: calc(100% + 4px); z-index: 30;
  min-width: 260px; padding: 4px; background: #fff;
  border: 1px solid var(--grey-line); border-radius: 6px;
}
.menu-list a {
  display: block; padding: 8px 10px; border-radius: 4px;
  font-size: 13.5px; font-weight: 600; color: var(--text); text-decoration: none;
}
.menu-list a:hover, .menu-list a:focus-visible { background: rgba(58, 177, 162, 0.12); }
.menu-sub { display: block; margin-top: 1px; font-size: 12px; font-weight: 400; color: var(--text-muted); }

/* A nav item that opens a submenu. Styled to sit in the nav bar exactly as the
   plain links do, including the active underline. */
.nav-menu { display: flex; }
.nav-menu-toggle {
  background: none; border: none; border-bottom: 2px solid transparent; border-radius: 0;
  color: rgba(255, 255, 255, 0.82); padding: 10px 14px; font-size: 13.5px; font-weight: 500;
  font-family: inherit; cursor: pointer;
}
.nav-menu-toggle:hover { color: #fff; }
.nav-menu-toggle.active { color: #fff; border-bottom-color: var(--teal); }
.nav-menu .menu-list { left: 0; right: auto; }
/* The nav centres and colours its own links; a submenu's items are ordinary
   left-aligned menu items, so undo that inside the menu. */
.nav-menu .menu-list a {
  display: block; text-align: left; justify-content: flex-start;
  color: var(--text); border-bottom: none; padding: 8px 10px;
}

/* Search-and-choose field (the supplier picker). A plain text input with our own
   filtered list underneath, so choosing a value never hides the rest of the list
   the way a native datalist does. */
.combo { position: relative; }
.combo-list {
  position: absolute; z-index: 20; left: 0; right: 0; top: calc(100% + 3px);
  list-style: none; margin: 0; padding: 4px;
  background: #fff; border: 1px solid var(--grey-line); border-radius: 5px;
  max-height: 260px; overflow-y: auto;
}
.combo-option {
  display: flex; justify-content: space-between; gap: 12px; align-items: baseline;
  padding: 6px 8px; border-radius: 4px; font-size: 13.5px; cursor: pointer;
}
.combo-option .muted { font-size: 12px; }
.combo-option:hover, .combo-option.active { background: rgba(58, 177, 162, 0.12); }
.combo-group {
  padding: 8px 8px 2px; font-size: 11px; text-transform: uppercase;
  letter-spacing: 0.04em; color: var(--text-muted); font-weight: 600;
}
.combo-none { padding: 8px; font-size: 13px; color: var(--text-muted); }
/* The register is searched in the database and only the first matches come
   back, so the list says how many it is showing rather than letting a name
   further down look like a supplier who was never registered. */
.combo-more {
  padding: 8px; font-size: 12px; color: var(--text-muted);
  border-top: 1px solid var(--grey-line); margin-top: 4px;
}
.combo-add {
  margin-top: 4px; border-top: 1px solid var(--grey-line); padding-top: 8px;
  color: var(--teal-dark); font-weight: 600;
}

/* A form section that opens inside another (the new supplier details on the
   requisition form): inset so it reads as part of the parent panel. */
.inset-panel {
  background: var(--grey-bg); border: 1px solid var(--grey-line); border-radius: 6px;
  padding: 14px 16px; margin: 4px 0 14px;
}
.inset-panel h3 { font-size: 13.5px; color: var(--navy); margin: 0 0 4px; }
.inset-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 12px; }
/* "Choose an existing supplier instead" is a way back rather than an action, so
   it reads as a quiet pill rather than a button or a link. */
.inset-head .btn {
  padding: 3px 11px; font-size: 12px; border-radius: 999px; background: #fff;
  border-color: var(--grey-line); color: var(--text-muted); text-decoration: none;
}
.inset-head .btn:hover { color: var(--text); border-color: var(--text-muted); }
.inset-panel > .hint:first-of-type { margin: 0 0 12px; }
.inset-panel .field input, .inset-panel .field textarea { background: #fff; }

.draft-banner {
  background: rgba(58, 177, 162, 0.1); border: 1px solid rgba(58, 177, 162, 0.4);
  border-radius: 5px; padding: 8px 12px; font-size: 13px; margin-bottom: 14px;
}

.checkbox { display: flex; align-items: center; gap: 7px; font-size: 13px; font-weight: 400 !important; color: var(--text) !important; margin-top: 7px; }
/* A field following a confirmation tick needs its own breathing room, or its
   label reads as part of the checkbox above it. */
.checkbox + .field { margin-top: 14px; }
.detail-field { margin: 8px 0 0 0; padding-left: 2px; }
.links-list { margin: 4px 0 0 18px; font-size: 13px; }
.links-list a { color: var(--teal-dark); word-break: break-all; }
.head-actions { display: flex; align-items: center; gap: 12px; }

.dev-banner {
  background: rgba(234, 194, 0, 0.13); border: 1px solid rgba(234, 194, 0, 0.5);
  border-radius: 5px; padding: 8px 12px; font-size: 13px; margin-bottom: 12px;
}

/* Detail page */
.detail-grid { display: grid; grid-template-columns: minmax(0, 2fr) minmax(260px, 1fr); gap: 14px; align-items: start; }
/* minmax(0, ...) rather than 1fr: a bare 1fr column is never narrower than its
   widest content, so one wide table made the whole page wider than a phone. */
@media (max-width: 820px) { .detail-grid { grid-template-columns: minmax(0, 1fr); } }
.detail-main, .detail-side { min-width: 0; }
.detail-list { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 6px 20px; }
@media (max-width: 640px) { .detail-list { grid-template-columns: minmax(0, 1fr); } }
/* One column where the values are long free text (a supplier's address and
   notes), which two narrow columns break up badly. */
.detail-list.stacked { grid-template-columns: 1fr; }
.detail-list div { display: flex; gap: 8px; align-items: baseline; }
.detail-list dt { font-size: 12.5px; color: var(--text-muted); min-width: 120px; }
.detail-list dd { font-size: 13.5px; font-weight: 500; min-width: 0; overflow-wrap: anywhere; }
/* A status pill sits under its value rather than trailing it, so a long value
   and its pill never run together on one line. */
.dd-pill { display: block; margin-top: 4px; font-weight: 400; }
.justification { font-size: 13.5px; }
/* Question answers: fixed-width question column, answer starts immediately
   beside it, left-aligned. Long answers wrap within their own column. */
.qa-row {
  display: grid; grid-template-columns: minmax(220px, 320px) 1fr; gap: 2px 28px;
  padding: 7px 0; border-bottom: 1px solid var(--grey-line); align-items: start;
}
.qa-row:last-child { border-bottom: none; }
.qa-q { font-size: 13px; color: var(--text-muted); }
.qa-a { font-size: 13.5px; font-weight: 500; }
@media (max-width: 640px) { .qa-row { grid-template-columns: 1fr; } }

/* Search rows */
.search-row { display: flex; gap: 8px; align-items: center; }
.search-row input[type="search"], .search-row input[type="date"], .search-row select {
  padding: 6px 9px; border: 1px solid var(--grey-line); border-radius: 5px;
  font-size: 13px; font-family: inherit; min-width: 180px; background-color: #fff; color: var(--text);
  -webkit-appearance: none; appearance: none;
}
/* Hide WebKit's native search decorations, which otherwise clip the placeholder. */
.search-row input[type="search"]::-webkit-search-decoration,
.search-row input[type="search"]::-webkit-search-cancel-button { -webkit-appearance: none; }
.search-row select { min-width: 130px; }
.search-row input[type="date"] { min-width: 130px; }

/* Icon action buttons (admin tables) */
.icon-btn {
  display: inline-flex; align-items: center; justify-content: center;
  width: 26px; height: 26px; border: none; border-radius: 5px;
  background: transparent; cursor: pointer; padding: 0;
}
.icon-btn:hover { background: rgba(10, 58, 104, 0.08); }
.icon-btn:disabled { opacity: 0.25; cursor: default; }
.icon-btn:disabled:hover { background: transparent; }
.icon-up, .icon-down { color: var(--navy); }
.icon-edit { color: var(--teal-dark); }
.icon-deactivate, .icon-remove { color: var(--pink); }
.icon-activate { color: #55701a; }

/* Notifications */
.nav-right { margin-left: auto; }
.nav-bell { display: inline-flex; align-items: center; gap: 2px; }
.nav-badge {
  display: inline-block; margin-left: 6px; padding: 1px 7px; border-radius: 9px;
  background: var(--orange); color: #fff; font-size: 11px; font-weight: 700;
}
.notif-list { list-style: none; }
.notif-item {
  display: flex; gap: 10px; align-items: flex-start; padding: 9px 0;
  border-bottom: 1px solid var(--grey-line);
}
.notif-item:last-child { border-bottom: none; }
.notif-item .timeline-dot { margin-top: 4px; flex-shrink: 0; }
.notif-body { flex: 1; font-size: 13.5px; }
.notif-body a { color: var(--text); text-decoration: none; font-weight: 500; }
.notif-body a:hover { color: var(--teal-dark); }
.notif-meta { font-size: 12px; color: var(--text-muted); margin-top: 1px; }

/* Clickable table rows */
tr.row-link { cursor: pointer; }

/* History legend */
.legend {
  display: flex; flex-wrap: wrap; gap: 6px 14px; margin-top: 10px; padding-top: 8px;
  border-top: 1px solid var(--grey-line); font-size: 11.5px; color: var(--text-muted);
}
.legend > span { display: inline-flex; align-items: center; gap: 5px; }
.legend .timeline-dot { width: 9px; height: 9px; margin: 0; }

/* Admin sub-navigation */
/* Admin's section tabs. One row of whole labels at every width: where they do
   not fit, the row scrolls sideways (app.js brings the current tab into view)
   rather than breaking labels onto two lines. The rule under the row is drawn
   inside it, so the current tab's underline sits on it without spilling out of
   a box that scrolls. */
.subnav {
  display: flex; gap: 4px; margin-bottom: 14px;
  box-shadow: inset 0 -1px 0 var(--grey-line);
  overflow-x: auto; scrollbar-width: thin;
}
.subnav a {
  color: var(--text-muted); text-decoration: none; padding: 7px 12px;
  font-size: 13px; font-weight: 600; border-bottom: 2px solid transparent;
  white-space: nowrap; flex: none;
}
.subnav a:hover { color: var(--navy); }
.subnav a.active { color: var(--navy); border-bottom-color: var(--teal); }
.subnav-count {
  display: inline-block; min-width: 16px; padding: 0 5px; margin-left: 4px;
  font-size: 11px; line-height: 16px; text-align: center; border-radius: 8px;
  background: var(--orange); color: #fff; font-weight: 700;
}
.gov-mark { display: flex; gap: 6px; align-items: center; }
.gov-mark input[type="text"], .gov-mark input[type="date"] {
  padding: 5px 8px; border: 1px solid var(--grey-line); border-radius: 4px;
  font-size: 12.5px; font-family: inherit; background-color: #fff; color: var(--text);
}
.gov-mark input[type="text"] { min-width: 210px; }
.gov-mark input[type="date"] { min-width: 170px; }

/* Email log: the message body, shown as sent (plain text, preserved wrapping) */
.email-detail-row td { background: var(--grey-bg); }
.email-body {
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: 12.5px; line-height: 1.55; color: var(--text);
  white-space: pre-wrap; word-break: break-word; margin: 0;
  background: #fff; border: 1px solid var(--grey-line); border-radius: 5px; padding: 12px 14px;
}

/* Filters and list helpers */
.filter-row { display: flex; gap: 12px; flex-wrap: wrap; align-items: flex-end; }
/* A filter sitting directly on top of the table it filters reads as part of the
   table's header. Every screen with one gets the same gap. */
.filter-row + .table-scroll,
.filter-row + .data-table,
.filter-row + .empty-state,
form:has(> .filter-row) + .table-scroll,
form:has(> .filter-row) + .data-table,
form:has(> .filter-row) + .empty-state { margin-top: 18px; }
form.filter-row + .table-scroll,
form.filter-row + .data-table,
form.filter-row + .empty-state { margin-top: 18px; }
.filter-row .field { margin-bottom: 0; min-width: 160px; }
/* A filter row that sits directly beneath a form takes the form's own columns, so
   the two rows line up rather than nearly lining up: same grid as .field-row, so a
   filter field is exactly as wide as the field above it. Anything that is not a
   field (a tick, the buttons) shares the last column. */
.filter-row.filter-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); align-items: end;
}
.filter-row.filter-grid .field { min-width: 0; }
.filter-actions { display: flex; gap: 8px; align-items: flex-end; }
/* A ghost button has no border or fill, so its 14px of padding is invisible and
   the eye measures the gap from the previous button's edge to this one's text: 23px
   against the 8px between two solid buttons. Dropping the padding makes the three
   look evenly spaced, which is what they already were on paper. Safe here because
   .btn-ghost has no hover fill to hug the text. */
.filter-actions .btn-ghost { padding-left: 0; padding-right: 0; }
/* A heading with a button beside it. The button is taller than the heading text,
   so without a margin here whatever follows sits against the bottom of it. */
.panel-head-row { display: flex; justify-content: space-between; align-items: baseline; gap: 12px; margin-bottom: 12px; }
.widget-link { font-size: 12.5px; font-weight: 600; color: var(--teal-dark); text-decoration: none; white-space: nowrap; }
.widget-link:hover { text-decoration: underline; }
.row-actions { display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
/* Actions that read as a pair (Excel / CSV) stay on one line even in a narrow
   column; the table scrolls inside its panel if there is genuinely no room. */
.row-actions.nowrap { flex-wrap: nowrap; justify-content: flex-end; }
/* On a table cell, flex takes the cell out of the row's layout: it stops
   stretching to the row's height, so as soon as a neighbouring cell wraps onto a
   second line this one's bottom border is drawn above everyone else's. Cells stay
   cells; the buttons are spaced with margins instead. */
td.row-actions { display: table-cell; vertical-align: middle; }
td.row-actions > * { vertical-align: middle; }
td.row-actions > * + * { margin-left: 6px; }
td.row-actions.nowrap { text-align: right; white-space: nowrap; }
td.row-actions form { display: inline-block; margin: 0; }
/* Where a row can have two actions but not always, the pair sits in fixed slots
   so the pencils line up down the column instead of drifting to the middle of
   the cell on the rows that have nothing beside them. */
.action-slots { display: inline-grid; grid-template-columns: 26px 26px; gap: 6px; align-items: center; justify-items: center; }
.action-slots > * { margin: 0; }
.action-slot-empty { display: inline-block; width: 26px; height: 26px; }

/* Dashboard */
.dash-sub { font-size: 13px; color: var(--text-muted); margin-top: 2px; }
.stat-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(160px, 1fr)); gap: 12px; margin-bottom: 14px; }
.stat-tile {
  background: var(--surface); border: 1px solid var(--grey-line);
  border-radius: 6px; padding: 12px 14px; display: block; text-decoration: none;
}
a.stat-link { transition: background 0.1s; }
a.stat-link:not([class*="accent-"]):hover { background: #f8fafb; }
/* An accented tile is the whole box, not a stripe down its edge: at a glance the
   row reads as five coloured cards rather than five white ones with a detail. The
   tints are deliberately weak, because these sit above the page's real content
   and must not out-shout it, and the number stays navy on every one so the tint
   never has to carry the contrast. Tiles with no accent (the console's) keep the
   left stripe and are untouched. */
.stat-tile[class*="accent-"] { border-left-width: 1px; border-style: solid; }
.stat-tile.accent-approved { background: #f5f9ea; border-color: #dde9bc; }
.stat-tile.accent-awaiting { background: #fdf2ea; border-color: #f6d5bf; }
.stat-tile.accent-denied { background: #fdecf1; border-color: #f5c4d4; }
.stat-tile.accent-info { background: #fdf9e4; border-color: #eee1a6; }
.stat-tile.accent-navy { background: #eef3f8; border-color: #c8d7e4; }
/* One hover rule for every tint: a thin wash of the page's own navy over
   whatever colour the tile already is, rather than six near-identical hex
   values that would drift apart the first time one of them is adjusted. */
a.stat-link[class*="accent-"]:hover { background-image: linear-gradient(rgba(10, 58, 104, 0.05), rgba(10, 58, 104, 0.05)); }
.stat-num { font-size: 23px; font-weight: 700; color: var(--navy); line-height: 1.1; }
.stat-label { font-size: 12.5px; color: var(--text); margin-top: 3px; }
.stat-sub { font-size: 12px; color: var(--text-muted); margin-top: 3px; }

.dash-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(340px, 1fr)); gap: 14px; align-items: stretch; }
.dash-grid .panel { margin-bottom: 0; display: flex; flex-direction: column; }
/* Cards in a row share the tallest height (equal size); their content stays at
   the top of the card rather than floating in the vertical centre. */
.chart-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); gap: 14px; margin-bottom: 14px; align-items: stretch; }
.chart-card { margin-bottom: 0; }

/* Folding a dashboard box away. The button and the arrow are added by app.js, so
   everything here only ever applies once the script has run; with scripting off
   there is no button to style and every box stays open. The heading itself is the
   control, because a 14px arrow is a small target and the words next to it are
   what people aim at. */
/* The slot the Collapse All button is put into. Empty until the script fills it,
   and a flex gap either side of an empty box is still a gap. */
[data-collapse-all]:empty { display: none; }
.panel-toggle {
  display: flex; align-items: baseline; gap: 7px; min-width: 0;
  background: none; border: 0; padding: 0; margin: 0;
  font: inherit; color: inherit; text-align: left; cursor: pointer;
}
.panel-toggle h2 { margin-bottom: 0; }
.panel-toggle:hover h2 { color: var(--teal-dark); }
.panel-toggle:focus-visible { outline: 2px solid var(--teal); outline-offset: 3px; border-radius: 3px; }
.panel-chevron {
  width: 12px; height: 12px; flex: none; color: var(--text-muted);
  transition: transform 0.12s ease; transform-origin: 50% 50%;
}
.panel.collapsed .panel-chevron { transform: rotate(-90deg); }
.panel.collapsed .panel-head-row { margin-bottom: 0; }
/* The link beside the heading opens the full list, which is not an offer worth
   making about a box somebody has just shut. */
.panel.collapsed .widget-link { display: none; }
/* Shutting a box must not hide work waiting in it. While the box is closed, a
   dot sits beside the heading; open, the count in the title says the same thing
   at more length, so the dot would only repeat it. */
.panel.collapsed[data-pending] .panel-toggle h2::after {
  content: ""; display: inline-block; width: 7px; height: 7px; margin-left: 8px;
  border-radius: 50%; background: var(--pink); vertical-align: middle;
}
/* And on the one control that shut them, so a page scrolled past the headings
   still says there is something to come back to. */
.collapse-all.has-pending::after {
  content: ""; display: inline-block; width: 7px; height: 7px; margin-left: 7px;
  border-radius: 50%; background: var(--pink); vertical-align: middle;
}
/* Cards in a grid row share the tallest height. A collapsed card must not be
   stretched to match the open one beside it, or closing it would leave a tall
   empty box where the content used to be. */
.dash-grid .panel.collapsed, .chart-grid .panel.collapsed { align-self: start; }

/* "Showing approved only" and the way back out of it. Quieter than a notice box:
   it reports the state of the page rather than the result of an action. */
.filter-note {
  background: #f3f9f9; border: 1px solid #cfe4e4;
  border-radius: 5px; padding: 8px 12px; font-size: 13px; margin-bottom: 14px;
}
.filter-note a { color: var(--teal-dark); }

.mini-list { list-style: none; }
.mini-list li { display: flex; justify-content: space-between; gap: 10px; align-items: center; padding: 8px 0; border-bottom: 1px solid var(--grey-line); }
.mini-list li:last-child { border-bottom: none; }
.mini-list li:first-child { padding-top: 2px; }
/* Stacked items (recent-activity lists): title on its own line, status pill below. */
.mini-list li.mini-stack { flex-direction: column; align-items: flex-start; gap: 6px; }
.mini-title { color: var(--text); text-decoration: none; font-weight: 500; font-size: 13.5px; }
.mini-title:hover { color: var(--teal-dark); }
.mini-right { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
/* Rows that carry a description as well as a button: let them wrap inside the
   card instead of pushing the button off the right-hand edge. */
.mini-right-wrap { width: 100%; flex-wrap: wrap; white-space: normal; row-gap: 8px; }
.mini-meta { font-size: 12.5px; color: var(--text-muted); }
.mini-list-flat li > span:first-child { font-size: 13px; }
/* Metric lists (System overview, Governance to action): centre the figures in a
   fixed column and give buttons a uniform width so both line up down the card. */
.mini-list-flat .mini-right strong { min-width: 56px; text-align: center; }
.mini-list-flat .mini-right .btn { min-width: 88px; text-align: center; }

/* Charts */
.stack-bar { display: flex; height: 16px; border-radius: 8px; overflow: hidden; background: var(--grey-bg); }
.stack-seg { height: 100%; }
.chart-legend { display: flex; flex-wrap: wrap; gap: 12px; margin-top: 10px; font-size: 12.5px; color: var(--text); }
.chart-legend-item { display: flex; align-items: center; gap: 6px; }
.chart-swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; flex-shrink: 0; }
.hbars { display: flex; flex-direction: column; gap: 9px; margin-top: 4px; }
.hbar-row { display: grid; grid-template-columns: 116px 1fr auto; gap: 10px; align-items: center; }
.hbar-label { font-size: 12.5px; color: var(--text); white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.hbar-track { background: var(--grey-bg); border-radius: 6px; height: 12px; overflow: hidden; }
.hbar-fill { display: block; height: 100%; background: var(--teal); border-radius: 6px; }
.hbar-value { font-size: 12.5px; font-variant-numeric: tabular-nums; color: var(--text); }
.c-approved { background: var(--green); }
.c-awaiting { background: var(--orange); }
.c-info { background: var(--yellow); }
.c-denied { background: var(--pink); }
.c-draft { background: var(--text-muted); }
.c-cancelled { background: var(--grey-line); }
.row-actions form { display: inline; }
.row-inactive td { opacity: 0.55; }
.attachment-list { list-style: none; }
.attachment-list li { padding: 5px 0; border-bottom: 1px solid var(--grey-line); font-size: 13.5px; display: flex; gap: 10px; align-items: baseline; }
.attachment-list li:last-child { border-bottom: none; }
.attachment-list a { color: var(--teal-dark); font-weight: 500; }
.attachment-list .muted { font-size: 12px; }
input[type="file"] { font-size: 13px; }
input[type="date"] { padding: 6px 9px; border: 1px solid var(--grey-line); border-radius: 5px; font-size: 13.5px; font-family: inherit; }

/* Approval progress */
.approval-route { list-style: none; margin: 8px 0 4px; }
.approval-route .route-step {
  display: flex; justify-content: space-between; align-items: baseline; gap: 10px;
  padding: 7px 10px; margin-bottom: 4px;
  background: var(--grey-bg); border-radius: 4px;
}
.approval-route .route-role { font-size: 12.5px; font-weight: 600; color: var(--text); }
.approval-route .route-role::before {
  content: ""; display: inline-block; width: 8px; height: 8px; margin-right: 8px;
  border-radius: 50%; background: #b9c3cc; vertical-align: 1px;
}
.approval-route .route-who { font-size: 12px; color: var(--text-muted); }
.route-done .route-role::before { background: var(--green); }
.route-done .route-role { color: #55701a; }
.route-current { background: rgba(232, 100, 26, 0.08); }
.route-current .route-role::before { background: var(--orange); }
.route-current .route-role { color: #a34310; }
.route-denied .route-role::before { background: var(--pink); }
.route-denied .route-role { color: #a30c3a; }
.route-info .route-role::before { background: var(--yellow); }
.route-todo { opacity: 0.72; }
.subhead { font-size: 12px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text-muted); margin: 14px 0 8px; }
.approval-steps { list-style: none; }
.step { display: flex; gap: 10px; padding: 0 0 14px; position: relative; }
.step:not(:last-child)::before {
  content: ""; position: absolute; left: 5px; top: 14px; bottom: 0; width: 1px; background: var(--grey-line);
}
.step .timeline-dot { margin-top: 3px; }
.step-current .step-name { color: var(--navy); }
.step-name { font-size: 13.5px; font-weight: 600; }
.step-role {
  font-weight: 500; font-size: 11px; color: var(--text-muted);
  text-transform: uppercase; letter-spacing: 0.04em; margin-left: 4px;
}
.step-meta { font-size: 12px; color: var(--text-muted); margin-top: 1px; }
.step-comment {
  font-size: 12.5px; margin-top: 4px; padding: 5px 8px;
  background: var(--grey-bg); border-radius: 4px; color: var(--text);
}
.dot-neutral { background: var(--grey-line); }

/* Naming the person a form is about to send something to. Not small print: it is
   the thing to check before pressing the button, so it reads as normal text. */
.confirmer-line { color: var(--text); margin-top: 8px; }

/* Where an approver decides, at the foot of the evidence, and the banner at the
   top of the page that says a decision is waiting and goes to it. Both use the
   warm surface of a queue panel rather than a coloured edge. */
.action-panel { background: #fffdf6; border-color: #e7d9b4; scroll-margin-top: 16px; }
.decision-banner {
  background: #fffdf6; border-color: #e7d9b4;
  display: flex; align-items: center; justify-content: space-between; gap: 14px 20px; flex-wrap: wrap;
}
.decision-banner h2 { margin-bottom: 4px; }
.decision-banner p { font-size: 13.5px; color: #5c5240; max-width: 62ch; }
.decision-banner .btn { flex: none; }

/* A panel that is there because somebody is waiting on you.
   The signal is the count in the heading and a faintly warmer surface, not a
   coloured rail down the left: a rail says "this block has been decorated", a
   number says "three people cannot sign in". It only renders when the queue has
   something in it, so its presence is itself the alert. */
.queue-panel { background: #fffdf6; border-color: #e7d9b4; }
.queue-panel .data-table th { border-bottom-color: #e7d9b4; }
.queue-panel .data-table td { border-bottom-color: #f0e7cf; }
.queue-panel .hint { color: #6b5f45; }
.count-chip {
  display: inline-block; margin-left: 8px; padding: 1px 10px;
  border-radius: 10px; background: var(--orange); color: #fff;
  font-size: 12px; font-weight: 700; letter-spacing: 0;
  position: relative; top: -1px;
}
.action-intro { font-size: 13.5px; margin-bottom: 10px; }
.waited-chip { display: block; width: fit-content; margin: 4px auto 0; }

/* The same panel after the decision has been made: a faint green surface,
   because it confirms the job is done rather than asking for anything. */
.decided-panel { background: #f6fbf3; border-color: #cfe2c4; }
.decided-tick { color: var(--green); font-weight: 700; margin-right: 4px; }

/* A closed disclosure. Used where an action exists but should never be taken by
   accident: changing a decision already made. Opening it is a deliberate act,
   and it works without JavaScript. */
.disclosure { margin-top: 4px; }
/* Following a row of buttons it needs the same breathing room anything else
   below a button gets, rather than sitting on the button's heels. */
.action-buttons + .disclosure { margin-top: 16px; }
.disclosure > summary {
  display: inline-flex; align-items: center; gap: 7px; cursor: pointer;
  font-size: 13px; font-weight: 600; color: var(--teal-dark);
  padding: 6px 12px; border: 1px solid var(--grey-line); border-radius: 999px;
  background: var(--surface); list-style: none; user-select: none;
}
.disclosure > summary::-webkit-details-marker { display: none; }
.disclosure > summary::after {
  content: ""; width: 0; height: 0;
  border-left: 4px solid transparent; border-right: 4px solid transparent;
  border-top: 5px solid currentColor; transition: transform 0.15s ease;
}
.disclosure[open] > summary::after { transform: rotate(180deg); }
.disclosure > summary:hover { border-color: var(--teal-dark); }
.disclosure > summary:focus-visible { outline: 2px solid var(--teal-dark); outline-offset: 2px; }
.disclosure-body { margin-top: 14px; padding-top: 14px; border-top: 1px solid var(--grey-line); }
/* Space between the last confirmation hint and the decision buttons, so the
   buttons are not pressed up against the small print. */
.action-buttons { display: flex; gap: 10px; flex-wrap: wrap; margin-top: 16px; }
/* Shown while a required confirmation is unticked, in place of nothing at all,
   so it is obvious why Approve is not yet available. */
.approve-blocked { font-size: 12.5px; color: #a34310; margin-top: 12px; }
.btn:disabled { opacity: 0.5; cursor: not-allowed; }
.btn-approve { background: var(--green); color: #fff; }
.btn-approve:hover { background: #7d9e2b; }
.btn-info { background: var(--yellow); color: #4a3c00; }
.btn-info:hover { background: #cfab00; }
.btn-deny { background: var(--pink); color: #fff; }
.btn-deny:hover { background: #c00d45; }

/* Timeline */
.timeline { list-style: none; }
.timeline-item { display: flex; gap: 10px; padding: 0 0 14px; position: relative; }
.timeline-item:not(:last-child)::before {
  content: ""; position: absolute; left: 5px; top: 14px; bottom: 0; width: 1px; background: var(--grey-line);
}
.timeline-dot { width: 11px; height: 11px; border-radius: 50%; margin-top: 3px; flex-shrink: 0; background: var(--text-muted); }
.dot-submitted, .dot-awaiting_approval { background: var(--orange); }
.dot-approved { background: var(--green); }
.dot-denied { background: var(--pink); }
.dot-info_requested { background: var(--yellow); }
.timeline-detail { font-size: 13px; }
.timeline-meta { font-size: 12px; color: var(--text-muted); margin-top: 1px; }

/* Footer */
.app-footer {
  max-width: 1280px; width: 100%; margin: 0 auto; padding: 14px 20px;
  font-size: 12px; color: var(--text-muted);
}

/* ---------------------------------------------------------------------------
   The platform console (workers/control.ts)
   Requestable's own admin, not a customer's. It shares this stylesheet so tables,
   panels and chips match the product, but the masthead is deliberately different:
   dark, narrow, and naming Requestable rather than a trust. Somebody holding this
   much authority should never have to wonder which platform they are looking at.
   --------------------------------------------------------------------------- */
.console-body { background: var(--grey-bg); }
.console-header { background: var(--navy-dark); }
.console-inner {
  max-width: 1280px; margin: 0 auto; padding: 9px 20px;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
}
.console-brand { display: flex; align-items: center; gap: 10px; text-decoration: none; }
.console-brand img { display: block; }
.console-brand-text { display: flex; flex-direction: column; color: #fff; font-weight: 700; font-size: 15px; letter-spacing: -0.01em; }
.console-brand-sub { color: rgba(255, 255, 255, 0.62); font-weight: 500; font-size: 11.5px; letter-spacing: 0.02em; text-transform: uppercase; }
.console-user { display: flex; align-items: baseline; gap: 10px; font-size: 13px; }
.console-user-name { color: #fff; font-weight: 600; }
/* The role is stated next to the name because what this account may do is not
   guessable from the screen: an operator and an owner see the same pages. */
.console-user-role {
  color: rgba(255, 255, 255, 0.72); font-size: 11.5px; border: 1px solid rgba(255, 255, 255, 0.3);
  border-radius: 999px; padding: 1px 8px;
}
.console-user .logout { color: rgba(255, 255, 255, 0.85); }
.console-nav { background: rgba(0, 0, 0, 0.22); display: flex; padding: 0 20px; max-width: 1280px; margin: 0 auto; }
.console-nav a {
  color: rgba(255, 255, 255, 0.8); text-decoration: none; padding: 9px 14px;
  font-size: 13.5px; font-weight: 500; border-bottom: 2px solid transparent;
}
.console-nav a:hover { color: #fff; }
.console-nav a.active { color: #fff; border-bottom-color: var(--teal); }
.console-auth-label {
  text-align: center; color: var(--text-muted); font-size: 12px;
  text-transform: uppercase; letter-spacing: 0.08em; margin-bottom: 18px;
}
/* Money and counts in a tile can be wide (£360,000), so the tile number wraps
   down a size rather than overflowing its box. */
.console-body .stat-num { font-size: 21px; }
.console-body .mini-meta { display: block; }

/* The console's backup matrix: one column per night, one row per customer. Small
   squares rather than words, because the question is "which nights are not green"
   and thirty rows of text does not answer it at a glance. Colour is not the only
   signal: every square carries its state in the title and for screen readers. */
.matrix-table th.matrix-head { font-size: 11px; text-align: center; padding: 6px 3px; color: var(--text-muted); }
.matrix-cell { padding: 4px 3px !important; text-align: center; }
.matrix-cell::after {
  content: ""; display: inline-block; width: 13px; height: 13px; border-radius: 3px;
  background: var(--grey-line);
}
.matrix-ok::after { background: var(--green); }
.matrix-failed::after { background: var(--pink); }
.matrix-missing::after { background: #cfd6dd; border: 1px dashed #a9b4bf; }
.matrix-text-missing { color: var(--pink); font-weight: 600; }
/* The bar that rises when rows are ticked. Fixed to the bottom of the window
   rather than the end of the table, because the tick that completes a selection
   is as likely to be forty rows down as at the top, and an action that has
   scrolled out of sight is an action nobody uses. */
.select-bar {
  position: fixed; left: 50%; transform: translateX(-50%); bottom: 20px; z-index: 60;
  display: flex; align-items: center; gap: 14px; flex-wrap: wrap;
  background: var(--navy); color: #fff;
  padding: 10px 8px 10px 18px; border-radius: 8px;
  box-shadow: 0 8px 24px rgba(7, 42, 77, 0.32);
  font-size: 13.5px; font-weight: 600;
  max-width: calc(100% - 32px);
}
.select-bar-hint { font-weight: 400; color: rgba(255, 255, 255, 0.72); }
.select-bar-actions { display: flex; gap: 8px; align-items: center; }
/* The quiet button on a navy ground cannot be the page's grey ghost. */
.select-bar .btn-ghost { color: #fff; border-color: rgba(255, 255, 255, 0.45); background: transparent; }
.select-bar .btn-ghost:hover { background: rgba(255, 255, 255, 0.12); border-color: #fff; }
@media (max-width: 620px) {
  .select-bar { left: 16px; right: 16px; transform: none; max-width: none; justify-content: space-between; }
}

/* Available to a screen reader, invisible on the page. */
.sr-only {
  position: absolute; width: 1px; height: 1px; padding: 0; margin: -1px;
  overflow: hidden; clip: rect(0, 0, 0, 0); white-space: nowrap; border: 0;
}
/* A secret shown exactly once, so it has to be easy to select and hard to misread. */
.key-reveal {
  display: block; font-size: 14px; letter-spacing: 0.02em; word-break: break-all;
  user-select: all; background: #fff; border: 1px solid var(--grey-line);
  border-radius: 5px; padding: 10px 12px;
}

/* Restore progress. The bar is the only thing on the page while a restore runs,
   so it is large enough to read across a room and honest about the phases having
   very different costs. */
.restore-progress { margin: 18px 0 22px; }
.progress-track {
  height: 14px; border-radius: 999px; background: var(--grey-line);
  overflow: hidden; margin: 10px 0 8px;
}
.progress-fill {
  height: 100%; background: var(--teal); border-radius: 999px;
  transition: width 0.4s ease;
}

/* Console: the customer page, laid out so the eye can find the seams.

   A panel heading followed by a line of context should read as one unit, so the
   subhead sits tight under the heading and the detail list below it gets the air
   instead. Before this the "Added ..." line floated between the two, closer to the
   data than to the heading it belonged with. */
.panel-head-tight { margin-bottom: 2px; }
.panel-subhead { font-size: 12px; color: var(--text-muted); margin: 0 0 4px; line-height: 1.5; }
.detail-list-spaced { margin-top: 16px; margin-bottom: 18px; }
/* A label and the thing it labels belong together: the status pill was drifting a
   long way from the word Status. The distance is the 120px min-width every
   .detail-list label carries, which is right when labels have to line up in a
   column of them and wrong for a lone row, so this one row opts out.

   Worth recording: the first attempt set grid-template-columns here, which did
   nothing whatsoever, because .detail-list div is display:flex. */
.status-line dt { min-width: 0; }
/* A block that takes the whole width of a field grid: a warning that applies to the
   form rather than to one field, or a textarea that wants the room. */
.field-row > .form-full { grid-column: 1 / -1; }
.field-row > .form-full .field { margin: 0; }
/* Guidance that belongs beside the button rather than under a field, because it
   describes when pressing the button is allowed rather than what to type. */
.hint-inline { margin: 0; max-width: 52ch; }
/* The term form is three deliberate lines rather than one reflowing grid, because
   the grouping is the meaning: what it costs, when it runs, how it was billed. */
.term-form .term-line + .term-line { margin-top: 14px; }
/* Two controls in a page head: the collapse toggle app.js fills in, and whatever
   the page's own action is. */
.head-actions { display: flex; align-items: center; gap: 8px; }

/* Field guidance, floated above the form rather than folded into it.

   Two problems, one after the other. Printed under every input, a form of ten
   fields became mostly explanation and the guidance got skipped precisely because
   there was so much of it. Hidden and then revealed in place, showing one hint
   pushed every field below it down the page, so reading about a field rearranged
   the form you were filling in.

   So it is a small card, positioned against the marker and taken out of the flow
   entirely: nothing moves when it appears. Hover or keyboard focus shows it for as
   long as you are there; a click pins it, which is the half a touch screen has.

   It stays a real element with a real id throughout, so aria-controls resolves and
   a screen reader loses nothing to either round of tidying. */
.field-label-row { display: flex; align-items: center; gap: 6px; position: relative; }
.hint-toggle {
  display: inline-flex; align-items: center; justify-content: center;
  width: 15px; height: 15px; padding: 0; border-radius: 50%;
  border: 1px solid var(--grey-line); background: var(--surface);
  color: var(--text-muted); font-size: 10px; font-weight: 700; font-style: italic;
  font-family: Georgia, "Times New Roman", serif; line-height: 1; cursor: pointer;
}
.hint-toggle:hover, .hint-toggle:focus-visible { border-color: var(--teal); color: var(--teal-dark); }
.field-label-row .hint {
  display: none;
  position: absolute; z-index: 40; top: calc(100% + 6px); left: 0;
  width: max-content; max-width: 300px;
  margin: 0; padding: 9px 11px;
  background: var(--surface); color: var(--text);
  border: 1px solid var(--grey-line); border-radius: 6px;
  box-shadow: 0 10px 28px rgba(7, 42, 77, 0.16), 0 2px 6px rgba(7, 42, 77, 0.08);
  font-size: 12px; line-height: 1.5; text-align: left; font-weight: 400;
}
/* The last field in a row would push its card off the right edge, so that one
   hangs from the right instead. */
.field-row > .field:last-child .field-label-row .hint { left: auto; right: 0; }
.field-label-row:has(.hint-toggle:hover) .hint,
.field-label-row:has(.hint-toggle:focus-visible) .hint,
.field.hint-open .field-label-row .hint { display: block; }
/* A set of tick boxes reads as one field, not as a tall stack that leaves the rest
   of the grid row empty beside it. Across the width, wrapping as needed. */
.checkbox-row { display: flex; flex-wrap: wrap; gap: 8px 24px; margin-top: 4px; }
.checkbox-row .checkbox { margin: 0; }
/* A list of findings under a detail list needs to be clear of it, and its bullets
   need room to breathe: these are sentences, not a shopping list. */
.detail-list + .notice-list { margin-top: 14px; }
.panel-body > .notice-list { margin-top: 12px; }
.notice-list > li { margin: 7px 0; line-height: 1.55; padding-left: 2px; }

/* Phones. The header keeps the name and Sign Out and drops what only fills
   space, and the page heading stacks. The nav becomes a Menu button with the
   bell beside it; the list opens underneath as full-width rows, and Finance
   opens in place inside it rather than as a floating box. The list is shut by
   this stylesheet, not by app.js, so a phone never shows the whole nav for a
   moment and then snaps it closed. */
@media (max-width: 620px) {
  .header-inner { padding: 10px 16px; flex-wrap: wrap; row-gap: 6px; }
  .brand-sub, .user-school { display: none; }
  .user-box { flex-wrap: wrap; row-gap: 2px; }
  .nav-inner { padding: 0 8px; flex-wrap: wrap; align-items: center; }
  .nav-burger {
    display: inline-flex; align-items: center; gap: 8px;
    background: none; border: 0; border-radius: 5px; cursor: pointer;
    color: #fff; font-family: inherit; font-size: 14px; font-weight: 600;
    padding: 11px 10px;
  }
  .nav-burger:hover { background: rgba(255, 255, 255, 0.08); }
  .nav-burger-close { display: none; }
  .nav-burger[aria-expanded="true"] .nav-burger-open { display: none; }
  .nav-burger[aria-expanded="true"] .nav-burger-close { display: block; }
  .main-nav .nav-items {
    display: none; order: 3; flex-basis: 100%; flex-direction: column;
    padding: 6px 0 12px; border-top: 1px solid rgba(255, 255, 255, 0.14);
  }
  .main-nav.nav-open .nav-items { display: flex; }
  .main-nav .nav-items > a, .main-nav .nav-menu-toggle {
    justify-content: flex-start; text-align: left; width: 100%;
    padding: 12px 10px; border-bottom: 0; border-radius: 5px;
    font-size: 14.5px; white-space: nowrap;
  }
  .main-nav .nav-items > a.active, .main-nav .nav-menu-toggle.active {
    background: rgba(255, 255, 255, 0.1); color: #fff; font-weight: 600;
  }
  .main-nav .nav-menu { display: block; }
  .main-nav .nav-menu-toggle svg { transition: transform 0.12s ease; }
  .main-nav .nav-menu-toggle[aria-expanded="true"] svg { transform: rotate(180deg); }
  .main-nav .nav-menu .menu-list {
    position: static; min-width: 0; padding: 0 0 6px 14px;
    background: transparent; border: 0; box-shadow: none;
  }
  .main-nav .nav-menu .menu-list a { color: rgba(255, 255, 255, 0.9); white-space: normal; padding: 9px 10px; }
  .main-nav .nav-menu .menu-list .menu-sub { color: rgba(255, 255, 255, 0.62); }
  .main-nav .nav-menu .menu-list a:hover, .main-nav .nav-menu .menu-list a:focus-visible {
    background: rgba(255, 255, 255, 0.08);
  }
  .content { padding: 16px; }
  .page-head { flex-direction: column; gap: 10px; }
  .page-head .head-actions { flex-wrap: wrap; }
  .decision-banner .btn { width: 100%; text-align: center; }
  /* A page's search and export buttons, and a form's row of buttons, wrap onto
     another line rather than pushing the page wider than the screen. */
  .search-row, .form-actions { flex-wrap: wrap; row-gap: 8px; }
  .search-row input[type="search"] { flex: 1 1 180px; min-width: 0; }
}
