/* 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; }

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); }

/* 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; }

/* 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; }

/* Tables */
.data-table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
.data-table th {
  text-align: left; font-size: 11.5px; 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; }
.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; }
/* 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); }
.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="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; }
.hint { font-size: 12px; 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 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; }

/* 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; }
.line-items .vat-col { width: 110px; }
.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: 780px; }
/* 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: var(--surface); border: 1px solid rgba(224, 16, 80, 0.35);
  border-left: 4px solid var(--pink); 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 { border-left: 3px solid var(--yellow, #eac200); }
.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: 11.5px; }
.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); }
.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; }
@media (max-width: 820px) { .detail-grid { grid-template-columns: 1fr; } }
.detail-list { display: grid; grid-template-columns: 1fr 1fr; gap: 6px 20px; }
/* 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: 12px; color: var(--text-muted); min-width: 120px; }
.detail-list dd { font-size: 13.5px; font-weight: 500; }
/* 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 */
.subnav { display: flex; gap: 4px; margin-bottom: 14px; border-bottom: 1px solid var(--grey-line); }
.subnav a {
  color: var(--text-muted); text-decoration: none; padding: 7px 12px;
  font-size: 13px; font-weight: 600; border-bottom: 2px solid transparent; margin-bottom: -1px;
}
.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; }
.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; }

/* 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-left: 3px solid var(--teal);
  border-radius: 6px; padding: 12px 14px; display: block; text-decoration: none;
}
a.stat-link { transition: background 0.1s; }
a.stat-link:hover { background: #f8fafb; }
.stat-tile.accent-approved { border-left-color: var(--green); }
.stat-tile.accent-awaiting { border-left-color: var(--orange); }
.stat-tile.accent-denied { border-left-color: var(--pink); }
.stat-tile.accent-info { border-left-color: var(--yellow); }
.stat-tile.accent-navy { border-left-color: var(--navy); }
.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: 11.5px; 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; }

.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: 12px; 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: 6px 10px; border-left: 3px solid var(--grey-line); margin-bottom: 4px;
  background: var(--grey-bg); border-radius: 0 4px 4px 0;
}
.approval-route .route-role { font-size: 12.5px; font-weight: 600; color: var(--text); }
.approval-route .route-who { font-size: 12px; color: var(--text-muted); }
.route-done { border-left-color: var(--green); }
.route-done .route-role { color: #55701a; }
.route-current { border-left-color: var(--orange); background: rgba(232, 100, 26, 0.08); }
.route-current .route-role { color: #a34310; }
.route-denied { border-left-color: var(--pink); }
.route-denied .route-role { color: #a30c3a; }
.route-info { border-left-color: 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; }

/* Approver action panel. The orange edge means "this is waiting for you". */
.action-panel { border-left: 3px solid var(--orange); }
.action-intro { font-size: 13.5px; margin-bottom: 10px; }

/* The same panel after the decision has been made: green, because it is a
   confirmation that the job is done, not another thing to act on. */
.decided-panel { border-left: 3px solid var(--green); }
.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; }
/* 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;
}
