:root {
  --primary-color: #0A2540;
  --secondary-color: #635BFF;
  --bg-color: #F6F9FC;
  --card-bg: #FFFFFF;
  --text-dark: #32325D;
  --text-light: #6B7C93;
  --border-color: #E6EBF1;
  --error-color: #E25950;
  --success-color: #32D583;
  /* Form-control tokens (2026-08-26). Inputs previously inherited the page
     background, which left them near-invisible against a white card. */
  --input-bg: #FFFFFF;
  --input-bg-focus: #FFFFFF;
  --input-bg-disabled: #F1F4F8;
  --input-border-hover: #C7D0DC;
  --focus-ring: rgba(99, 91, 255, 0.15);
  --table-header-bg: #F9FBFD;
}

* {
  box-sizing: border-box;
  margin: 0;
  padding: 0;
  font-family: 'Inter', sans-serif;
}

body {
  background-color: var(--bg-color);
  color: var(--text-dark);
  display: flex;
  min-height: 100vh;
  flex-direction: column;
}

/* Authentication Screen */
.auth-container {
  display: flex;
  align-items: center;
  justify-content: center;
  height: 100vh;
  width: 100%;
}

.auth-card {
  background: var(--card-bg);
  padding: 3rem 2rem;
  border-radius: 12px;
  box-shadow: 0 15px 35px rgba(50, 50, 93, 0.1), 0 5px 15px rgba(0, 0, 0, 0.07);
  text-align: center;
  width: 100%;
  max-width: 400px;
}

.auth-card h1 {
  font-size: 2rem;
  color: var(--primary-color);
  margin-bottom: 0.5rem;
}

.auth-card p {
  color: var(--text-light);
  margin-bottom: 2rem;
}

.btn {
  padding: 12px 24px;
  border: none;
  border-radius: 6px;
  font-size: 1rem;
  font-weight: 600;
  cursor: pointer;
  transition: all 0.3s ease;
}

.primary-btn {
  background-color: var(--secondary-color);
  color: white;
  width: 100%;
}

.primary-btn:hover {
  background-color: #534be0;
  transform: translateY(-2px);
  box-shadow: 0 4px 10px rgba(99, 91, 255, 0.3);
}

.secondary-btn {
  background-color: transparent;
  color: var(--secondary-color);
  border: 1px solid var(--secondary-color);
}

.secondary-btn:hover {
  background-color: rgba(99, 91, 255, 0.05);
}


.error-message {
  color: var(--error-color);
  margin-top: 1rem;
  font-size: 0.9rem;
}

/* Dashboard Layout */
.app-container {
  display: flex;
  width: 100%;
  height: 100vh;
}

.sidebar {
  width: 250px;
  background-color: var(--primary-color);
  color: white;
  padding: 2rem 1rem;
  display: flex;
  flex-direction: column;
  /* Fix (2026-08-06, Claude, Craig's report) - the sidebar is a flex child
     of .app-container (height:100vh) with no height/overflow of its own, so
     once enough nav links were added (Tender Desk pushed it over the edge
     on a laptop-height screen) the bottom items - Sign Out, "Logged in
     as:" - were silently clipped off the bottom of the viewport with no way
     to reach them, rather than the sidebar scrolling independently of the
     main content area. flex-shrink:0 stops the flexbox from squashing it
     unpredictably; overflow-y:auto lets IT scroll on its own when its
     content is taller than the viewport, without affecting main-content's
     own scrolling. Combined with tighter nav-link/header spacing below so
     everything fits without scrolling on most screens, and only scrolls as
     a fallback on genuinely short ones. */
  flex-shrink: 0;
  overflow-y: auto;
  scrollbar-width: thin;
  scrollbar-color: rgba(255, 255, 255, 0.25) transparent;
}
.sidebar::-webkit-scrollbar {
  width: 6px;
}
.sidebar::-webkit-scrollbar-thumb {
  background: rgba(255, 255, 255, 0.25);
  border-radius: 3px;
}

.sidebar-header {
  font-size: 1.3rem;
  font-weight: 700;
  margin-bottom: 1.25rem;
  text-align: center;
}

/* Internal-only Express Holdings logo (Phase K - Branding, 2026-08-02) -
   the sidebar background is dark navy and the logo's own wordmark is a
   similar dark navy, so it needs a white chip behind it to actually be
   visible - see dashboard.js's sidebar template. */
.sidebar-logo-chip {
  background: #fff;
  border-radius: 8px;
  padding: 8px 12px;
  display: inline-block;
  margin-bottom: 0.5rem;
}
.sidebar-logo-chip img {
  max-width: 100%;
  max-height: 40px;
  display: block;
}

.nav-link {
  color: rgba(255, 255, 255, 0.7);
  text-decoration: none;
  padding: 8px 15px;
  border-radius: 6px;
  margin-bottom: 0.3rem;
  transition: background 0.2s;
  cursor: pointer;
}

.nav-link:hover, .nav-link.active {
  background-color: rgba(255, 255, 255, 0.1);
  color: white;
}

.main-content {
  flex: 1;
  padding: 2rem 3rem;
  overflow-y: auto;
}

.header {
  display: flex;
  justify-content: space-between;
  align-items: center;
  margin-bottom: 2rem;
}

.header h2 {
  font-size: 1.8rem;
  color: var(--primary-color);
}

.card {
  background: var(--card-bg);
  border-radius: 8px;
  padding: 1.5rem;
  box-shadow: 0 4px 6px rgba(50, 50, 93, 0.05);
  margin-bottom: 1.5rem;
}

/* Dashboard / Home landing page (Phase AW, 2026-08-08, Claude) - the new
   default view for Owner/Manager/Office (see home.js). A simple responsive
   card grid, reusing .card's existing look rather than a new surface style,
   with a clickable affordance (cursor + hover) since every card here
   click-throughs to the real module. */
.dashboard-cards {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
  gap: 1rem;
}

.dash-card {
  cursor: pointer;
  transition: box-shadow 0.15s ease, transform 0.15s ease;
}

.dash-card:hover {
  box-shadow: 0 6px 14px rgba(50, 50, 93, 0.12);
  transform: translateY(-1px);
}

.dash-card-label {
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text-light);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.dash-card-number {
  font-size: 2.2rem;
  font-weight: 700;
  color: var(--primary-color);
  margin: 0.25rem 0;
}

.dash-card-detail {
  font-size: 0.85rem;
  color: var(--text-light);
}

.dash-card-subrow {
  font-size: 0.8rem;
  color: var(--text-light);
  margin-top: 0.5rem;
}

.dash-card-list {
  list-style: none;
  margin-top: 0.75rem;
  padding-top: 0.5rem;
  border-top: 1px solid var(--border-color);
}

.dash-card-list li {
  font-size: 0.8rem;
  padding: 3px 0;
  display: flex;
  justify-content: space-between;
  gap: 0.5rem;
}

/* 2026-08-20: two-row layout for the Telesales card's per-rep breakdown
   (name on its own line, MTD/all-time stat below it). Scoped to this
   modifier class rather than changing the base .dash-card-list li rule,
   because the Open Jobs card also uses .dash-card-list and needs to keep
   its original single-row, space-between layout (address left, date
   right). See home.js buildTelesalesCard(). */
.dash-card-list--rows li {
  display: block;
  padding: 4px 0;
}

.dash-card-list--rows li .rep-name {
  display: block;
  font-weight: 600;
}

.dash-card-list--rows li .rep-stat {
  display: block;
  margin-top: 2px;
  color: var(--text-light);
}

/* Spacing inside cards (2026-08-26, Craig: the Telesales roster table "does
   not have padding on top between the paragraph above it and the div which
   makes it look bad having an opaque bg directly under the text").

   Root cause is the global `* { margin: 0 }` reset at the top of this file:
   NO paragraph anywhere has bottom spacing unless someone inline-styled it,
   so an explanatory <p> sits flush against whatever follows - which looks
   worst when the next thing is a table header with an opaque background.
   Dozens of places worked around it with inline margins; the ones that
   forgot are the broken ones.

   Scoped to .card rather than applied to every <p> in the app, so layout
   outside cards is untouched. Anything that already sets its own margin
   inline still wins, so this only affects the elements that had none. */
.card p { margin-bottom: 0.75rem; }
.card p:last-child { margin-bottom: 0; }
.card h3 + p, .card h4 + p { margin-top: 0.35rem; }

.table-container {
  width: 100%;
  border-collapse: collapse;
}

/* Same reasoning: a table following body text needs a gap, and an
   overflow-x wrapper around one is the common shape in this app. */
.card > .table-container,
.card > div[style*="overflow-x"] { margin-top: 0.35rem; }

.table-container th, .table-container td {
  padding: 12px 15px;
  text-align: left;
  border-bottom: 1px solid var(--border-color);
}

.table-container th {
  font-weight: 600;
  color: var(--text-light);
  /* Was a hardcoded #f9fbfd. That is a CSS rule, not an inline style, so the
     generated dark-override block cannot reach it - it needs a token. */
  background-color: var(--table-header-bg);
}

/* Staff Management table (2026-08-07, Claude) - Craig asked to scale down
   the columns carrying less important info (Company, Phone, Employee #)
   so Name / Role / Actions stand out more on a list that runs 80+ rows.
   Scoped to .staff-table only so it doesn't affect any other table in the
   app (Quotes, Jobs, etc. all reuse the same .table-container class).
   FIXED (Phase CX, 2026-08-12, Claude, Craig's report - "spacing has
   changed... click boxes on top of each other and the font has
   increased") - these nth-child positions were written against a 6-column
   table (Name/Company/Role/Phone/Employee #/Actions) and never updated
   the very next day when the new Team column (2026-08-08, Claude) was
   inserted at position 4, shifting Phone/Employee #/Actions each one
   column to the right. Every selector below had been silently targeting
   the WRONG column ever since: nth-child(4) was hitting Team instead of
   Phone, nth-child(5) was hitting Phone instead of Employee #, and
   nth-child(6) - the one carrying width:26%/white-space:nowrap for the
   Actions buttons - was hitting Employee # instead of Actions, so the
   real Actions column got no special treatment at all and its three
   buttons wrapped onto separate lines. This had been silently wrong for
   days; a taller row height from an unrelated same-day change (a profile-
   picture thumbnail added to the Name cell) just made the existing
   misalignment obvious enough for Craig to notice and report. Renumbered
   every selector by one position; Team (the new column) deliberately gets
   no special sizing since it was never part of Craig's original "shrink
   Company/Phone/Employee #" request. */
.staff-table th:nth-child(2),
.staff-table th:nth-child(5) {
  font-size: 0.7rem;
  width: 12%;
}

/* Employee # is always a short number - narrower still than Company/Phone
   (2026-08-07, Claude, follow-up to Craig's request) */
.staff-table th:nth-child(6) {
  font-size: 0.7rem;
  width: 6%;
}

.staff-table td:nth-child(2),
.staff-table td:nth-child(5),
.staff-table td:nth-child(6) {
  font-size: 0.8rem;
  color: var(--text-light);
  padding-top: 10px;
  padding-bottom: 10px;
}

/* Actions column - widened so Edit / HR-Payroll / Delete sit on one line
   instead of wrapping (2026-08-07, Claude, follow-up to Craig's request).
   The extra room freed up by narrowing Company/Phone/Employee # above
   goes here. */
.staff-table th:nth-child(7) {
  width: 26%;
}

.staff-table td:nth-child(7) {
  white-space: nowrap;
}

.staff-table td:nth-child(7) .btn {
  margin-right: 6px;
}

/* Compliance Reference browse table (2026-08-15, Claude, Craig's report -
   "loads of wasted space and one column that should be widened"). Root
   cause: the table had no explicit column widths, so the browser's default
   auto-layout gave Clause Ref/Topic/Added far more room than their short
   content needs, while Summary - which carries the actual paraphrase, risk
   note, practical note, collapsible technical wording and any data-quality
   flag - was capped at a fixed max-width:420px inline style regardless of
   how wide the table's actual container was. table-layout:fixed makes the
   percentage widths below authoritative instead of advisory, and
   word-wrap ensures a long unbroken run of text in Summary still wraps
   within its column rather than forcing the table wider. Scoped to
   .compliance-table only, same convention as .staff-table above, so no
   other table in the app (Quotes, Jobs, etc.) is affected.

   UPDATED 2026-08-30 (Claude, Craig's report - "spacing overlap, columns
   not the same height") - the new Pest Control entries cite real
   legislation in full (e.g. "Fertilizers, Farm Feeds, Agricultural
   Remedies and Stock Remedies Act 36 of 1947...") in Clause Ref, not just
   short refs like "SANS 10142-1:2026 5.3.8(e)". Combined with the
   inline `white-space:nowrap` compliance.js put on that cell (removed
   there in this same change), a long citation overflowed its narrow 13%
   column uncontained and visually bled into the row below. Clause Ref
   widened 13% -> 18% and Summary trimmed 56% -> 51% to keep the total at
   100%, so a full Act name has room to wrap across 2-3 lines instead of
   one long unbroken overflowing line. */
.compliance-table {
  table-layout: fixed;
}

.compliance-table th:nth-child(1),
.compliance-table td:nth-child(1) {
  width: 18%;
}

.compliance-table th:nth-child(2),
.compliance-table td:nth-child(2) {
  width: 15%;
}

.compliance-table th:nth-child(3),
.compliance-table td:nth-child(3) {
  width: 51%;
  word-wrap: break-word;
}

.compliance-table th:nth-child(4),
.compliance-table td:nth-child(4) {
  width: 11%;
}

.compliance-table th:nth-child(5),
.compliance-table td:nth-child(5) {
  width: 5%;
}

.staff-table td:nth-child(7) .btn:last-child {
  margin-right: 0;
}

/* Jobs Dashboard table (2026-08-07, Claude) - Craig asked for the same
   column-scaling treatment as Staff Management, starting here since it's
   the page he flagged as most important. This table's columns are
   user-toggleable per browser (the "Columns" picker), so unlike
   .staff-table this is targeted by data-col (the column's key) rather
   than nth-child position - position would shift depending on which
   columns a given person has hidden. Contact-number columns are the
   closest match to Staff Management's "Phone" column (reference info,
   not something you scan the board for), so those are scaled down;
   Job #, Address, Client Name, Status, and the History action stay full
   size since those are what the board is actually used to track. */
.jobs-table th[data-col="clientContactNumber"],
.jobs-table th[data-col="estateAgentContactNumber"] {
  font-size: 0.7rem;
}

.jobs-table td[data-col="clientContactNumber"],
.jobs-table td[data-col="estateAgentContactNumber"] {
  font-size: 0.8rem;
  color: var(--text-light);
}

/* Clients table (formerly "Client Holdings", renamed 2026-08-07, Claude)
   - same treatment: Contact Info is reference info (phone/email), scaled
   down like Staff Management's Phone column; Actions widened so
   "Schedule Inspection" and "Details" sit on one line instead of
   wrapping. Addresses and Name stay full size - that's what this page is
   actually used to search/scan.
   Fix (2026-08-18, Claude, Craig's report - "Schedule Inspection and
   Details are still on top of each other") - this Actions treatment was
   still targeting nth-child(4), which was correct back when the table
   only had 4 columns (Addresses/Name/Contact Info/Actions). A Segment
   column was added between Contact Info and Actions at some point,
   pushing Actions to position 5 - the width/nowrap/margin-right rules
   below kept applying to Segment instead, so Actions (now un-widened,
   default width) let its two buttons wrap onto separate lines. Retargeted
   to nth-child(5) to match the table's actual current column order:
   Addresses(1)/Name(2)/Contact Info(3)/Segment(4)/Actions(5). */
.clients-table th:nth-child(3) {
  font-size: 0.7rem;
  width: 14%;
}

.clients-table td:nth-child(3) {
  font-size: 0.8rem;
  color: var(--text-light);
}

.clients-table th:nth-child(5) {
  width: 22%;
}

.clients-table td:nth-child(5) {
  white-space: nowrap;
}

.clients-table td:nth-child(5) .btn {
  margin-right: 6px;
}

.clients-table td:nth-child(5) .btn:last-child {
  margin-right: 0;
}

/* Enquiries table (2026-08-07, Claude) - Source and Assigned are
   reference/categorisation info, scaled down the same way as Company on
   Staff Management. Received (date) is left full size - unlike Jobs
   Dashboard, staleness/triage is exactly what this table is for, so the
   date needs to stay easy to scan. Client, Property, and Status are the
   at-a-glance fields and are untouched. */
.enquiries-table th:nth-child(5),
.enquiries-table th:nth-child(6) {
  font-size: 0.7rem;
}

.enquiries-table td:nth-child(5),
.enquiries-table td:nth-child(6) {
  font-size: 0.8rem;
  color: var(--text-light);
}

/* Vehicle Management table (formerly "Van Management", renamed
   2026-08-07, Claude) - Added Date is just record-keeping, scaled down
   like Staff Management's Company column. The three expiry/due-date
   columns are deliberately left full size (unlike other secondary
   fields elsewhere) - tracking those deadlines is the entire point of
   this page. Actions widened + nowrap so Details / Edit / Delete sit on
   one line.
   RENUMBERED (Phase CW/CX, 2026-08-12, Claude) - a new Current Driver
   column was inserted at position 2 in this same session (Vehicle
   Management expansion), pushing Added Date to position 3 and Actions to
   position 7. Fixed the nth-child positions here in the SAME change that
   added the column, rather than after the fact - see the matching fix
   just above on .staff-table for what happens when this step gets missed
   (Craig had to report it as a bug there first). Current Driver
   deliberately stays full-size/unscaled - it's exactly the at-a-glance
   info this column exists to surface, not secondary reference info. */
/* UPDATED 2026-08-27, Claude: a new "Vehicle" (Year Make Model) column was
   inserted at position 2, shifting every column after it right by one -
   Added Date moved from nth-child(3) to nth-child(4), Actions from
   nth-child(7) to nth-child(8). Vehicle itself deliberately stays
   full-size/unscaled, same reasoning as Current Driver above - it's
   at-a-glance identity info, not secondary reference info. */
.vehicles-table th:nth-child(4) {
  font-size: 0.7rem;
  width: 10%;
}

.vehicles-table td:nth-child(4) {
  font-size: 0.8rem;
  color: var(--text-light);
}

.vehicles-table th:nth-child(8) {
  width: 28%;
}

.vehicles-table td:nth-child(8) {
  white-space: nowrap;
}

.vehicles-table td:nth-child(8) .btn {
  margin-right: 6px;
}

.vehicles-table td:nth-child(8) .btn:last-child {
  margin-right: 0;
}

/* Quotes (2026-08-07, Claude) - reviewed all 3 tables on this page. The
   main "All Quotes" list and "Needs Quoting" table were already built
   compact (small font-size and muted colour baked into each cell's own
   markup, single-button actions that can't wrap) - left untouched
   rather than force changes for their own sake, same call as Attendance
   in the AQ phase. The Price List admin table has 6 columns (Code, Name,
   Description, Price, Default Supplier, Actions) - Default Supplier is
   reference info, scaled down the same as elsewhere; Actions gets the
   nowrap + spacing fix since Edit/Deactivate can wrap on a narrow
   window. */
.pricelist-table th:nth-child(5) {
  font-size: 0.7rem;
}

.pricelist-table td:nth-child(5) {
  font-size: 0.8rem;
  color: var(--text-light);
}

.pricelist-table td:nth-child(6) {
  white-space: nowrap;
}

.pricelist-table td:nth-child(6) .btn {
  margin-right: 4px;
}

.pricelist-table td:nth-child(6) .btn:last-child {
  margin-right: 0;
}

/* Certificates of Compliance table (2026-08-07, Claude) - Inspector name
   is reference info here (Property Address, Status, and Missing are what
   this page is actually used to track - it's literally a checklist of
   what's blocking a certificate). Actions is a single button so it was
   never at risk of wrapping, but nowrap is added anyway for consistency
   and to stop "Generate Certificate" ever breaking mid-word on a narrow
   window. */
.coc-table th:nth-child(2) {
  font-size: 0.7rem;
  width: 12%;
}

.coc-table td:nth-child(2) {
  font-size: 0.8rem;
  color: var(--text-light);
}

.coc-table td:nth-child(5) {
  white-space: nowrap;
}

/* Stock & Suppliers - Suppliers table (2026-08-07, Claude) - Email/Phone
   are contact reference info, same treatment as Contact Info on Clients.
   Name and Address stay full size (Address in particular already has
   its own max-width/font-size from the original build). Actions gets
   the same nowrap + spacing fix as everywhere else - Edit/Deactivate
   were already close together but could still wrap on a narrow window. */
.suppliers-table th:nth-child(2),
.suppliers-table th:nth-child(3) {
  font-size: 0.7rem;
  width: 14%;
}

.suppliers-table td:nth-child(2),
.suppliers-table td:nth-child(3) {
  font-size: 0.8rem;
  color: var(--text-light);
}

.suppliers-table td:nth-child(5) {
  white-space: nowrap;
}

/* Purchase Orders (2026-08-07, Claude) - Supplier is reference info vs
   PO Number/Status/Total which are what this list is actually scanned
   for. Actions is a single "Open" button, nowrap added for consistency. */
.po-table th:nth-child(2) {
  font-size: 0.7rem;
}

.po-table td:nth-child(2) {
  font-size: 0.8rem;
  color: var(--text-light);
}

.po-table td:nth-child(6) {
  white-space: nowrap;
}

/* Referral Partners - main partner list (2026-08-07, Claude) - Assigned
   Rep scaled down, same idea as "Assigned" on Jobs Dashboard. Agency,
   Contact, Area, Grade, and Visit Status left alone - all of those
   matter for actually managing the relationship, unlike Assigned Rep
   which is more of a routing/reference field. */
.referral-table th:nth-child(6) {
  font-size: 0.7rem;
}

.referral-table td:nth-child(6) {
  font-size: 0.8rem;
  color: var(--text-light);
}

.referral-table td:nth-child(7) {
  white-space: nowrap;
}

/* Referral Partners - Sales Team / Reps admin table (2026-08-07, Claude)
   - Cell/Email are contact reference info, same treatment as Contact
   Info on Clients. */
.reps-table th:nth-child(3),
.reps-table th:nth-child(4) {
  font-size: 0.7rem;
}

.reps-table td:nth-child(3),
.reps-table td:nth-child(4) {
  font-size: 0.8rem;
  color: var(--text-light);
}

.reps-table td:nth-child(7) {
  white-space: nowrap;
}

/* Reports - the three "action needed" flag lists (no contact logged,
   overdue follow-up, aging leads) share this class (2026-08-07, Claude)
   - Phone scaled down like everywhere else it appears as reference info.
   No Actions column on these - they're read-only report rows. */
.reporting-flag-table th:nth-child(3) {
  font-size: 0.7rem;
}

.reporting-flag-table td:nth-child(3) {
  font-size: 0.8rem;
  color: var(--text-light);
}

/* Renewals (2026-08-07, Claude) - Trade scaled down as a category field;
   Last Inspected/Suggested Renewal/Last Outreach deliberately left full
   size since tracking those dates IS the point of this page. Actions is
   NOT touched with nowrap here, unlike other tables - the two buttons
   (Log Outreach / Find in Clients) already use a deliberate margin-top
   stacked layout in the original build, and forcing nowrap would fight
   that existing design instead of matching it. */
.renewals-table th:nth-child(2) {
  font-size: 0.7rem;
}

.renewals-table td:nth-child(2) {
  font-size: 0.8rem;
  color: var(--text-light);
}

/* Roles & Access (2026-08-07, Claude) - only 4 columns and all of them
   matter (this is the page that controls who can get into the app at
   all), so nothing is scaled down. Actions gets the nowrap + spacing
   fix since "Remove (revoke access)" is long enough to wrap next to
   Edit on a narrower window. */
.roles-table td:nth-child(4) {
  white-space: nowrap;
}

.roles-table td:nth-child(4) .btn {
  margin-right: 4px;
}

.roles-table td:nth-child(4) .btn:last-child {
  margin-right: 0;
}

/* Tender Desk - Tenders table (2026-08-07, Claude) - Tender # scaled
   down as a reference code (same idea as Employee # elsewhere).
   Deadline and Pitfalls are deliberately untouched - risk-tracking is
   the entire point of this tracker. Actions is a single "Open" button,
   nowrap added for consistency. */
.tenders-table th:nth-child(3) {
  font-size: 0.7rem;
}

.tenders-table td:nth-child(3) {
  font-size: 0.8rem;
  color: var(--text-light);
}

.tenders-table td:nth-child(7) {
  white-space: nowrap;
}

/* Tender Desk - Compliance Vault table (2026-08-07, Claude) - Issuer
   scaled down as reference info. Issue Date/Expiry Date/Status left
   full size - the whole point of this table is catching expired
   certificates before they disqualify a tender. Actions is a single
   "Delete" button, nowrap added for consistency. */
.vault-table th:nth-child(2) {
  font-size: 0.7rem;
}

.vault-table td:nth-child(2) {
  font-size: 0.8rem;
  color: var(--text-light);
}

.vault-table td:nth-child(7) {
  white-space: nowrap;
}

.form-group {
  margin-bottom: 1rem;
}

.form-group label {
  display: block;
  margin-bottom: 0.5rem;
  font-weight: 600;
  font-size: 0.9rem;
}

.form-control {
  width: 100%;
  padding: 10px;
  border: 1px solid var(--border-color);
  border-radius: 6px;
  font-size: 1rem;
}

.form-control:focus {
  outline: none;
  border-color: var(--secondary-color);
  box-shadow: 0 0 0 3px rgba(99, 91, 255, 0.1);
}

select.form-control {
  appearance: none;
  /* Was a hardcoded #fff (2026-08-27, Claude) - same reasoning as the table
     header fix above: a CSS rule, not an inline style, so the generated
     dark-override block can't reach it - needs the token instead. */
  background-color: var(--input-bg);
  cursor: pointer;
}

.user-info {
  margin-top: auto;
  font-size: 0.8rem;
  color: rgba(255, 255, 255, 0.5);
  text-align: center;
}

/* Mobile layout (2026-08-04, Claude) - Craig reported the app "does not
   display properly on the phone". This file had zero @media breakpoints
   before this - the fixed 250px sidebar was always in the flow, which left
   almost no usable width for content on a phone-sized screen. Below
   ~900px the sidebar becomes a hidden off-canvas drawer (toggled by the
   hamburger button dashboard.js now renders), and tables get horizontal
   scroll instead of overflowing/squashing. Nothing here changes anything
   above 900px wide - existing desktop layout is untouched. */
.mobile-nav-toggle {
  display: none;
  position: fixed;
  top: 1rem;
  left: 1rem;
  width: 44px;
  height: 44px;
  border: none;
  border-radius: 8px;
  background-color: var(--primary-color);
  color: #fff;
  font-size: 1.4rem;
  line-height: 1;
  cursor: pointer;
  z-index: 1001;
  align-items: center;
  justify-content: center;
}

.mobile-nav-backdrop {
  display: none;
}

@media (max-width: 900px) {
  .mobile-nav-toggle {
    display: flex;
  }

  .app-container {
    position: relative;
  }

  .sidebar {
    position: fixed;
    top: 0;
    left: 0;
    height: 100vh;
    width: 250px;
    max-width: 80vw;
    transform: translateX(-100%);
    transition: transform 0.25s ease;
    overflow-y: auto;
    z-index: 1000;
  }

  .sidebar.open {
    transform: translateX(0);
  }

  .mobile-nav-backdrop.open {
    display: block;
    position: fixed;
    top: 0;
    left: 0;
    right: 0;
    bottom: 0;
    background: rgba(10, 37, 64, 0.45);
    z-index: 999;
  }

  .main-content {
    padding: 4.5rem 1rem 1.5rem;
  }

  .header {
    flex-wrap: wrap;
    gap: 0.5rem;
  }

  .header h2 {
    font-size: 1.4rem;
  }

  /* Tables are wrapped in a .card almost everywhere in this app - letting
     the card scroll horizontally keeps every existing table exactly as-is
     (no changes needed in enquiries.js/referral.js/quotes.js/etc.) while
     fixing the "content squashed off the edge of the screen" symptom. */
  .card {
    overflow-x: auto;
  }
}

@media (max-width: 480px) {
  .auth-card {
    padding: 2rem 1.25rem;
    max-width: 90vw;
  }
}

/* =========================================================================
   DARK MODE (2026-08-26, Claude, Craig's request)
   =========================================================================
   Activated by data-theme="dark" on <html>, set by public/theme.js. An
   inline script in index.html/inspect.html applies it BEFORE this stylesheet
   paints, so there is no white flash on load.

   TWO LAYERS, and it is worth understanding why:

   LAYER 1 - the variables below. Everything in this app that uses
   var(--card-bg), var(--text-dark) and friends gets dark mode for free. That
   is the whole chrome: body, sidebar, cards, tables, forms, buttons,
   borders. This layer is the real dark mode and it is the one to extend.

   LAYER 2 - the [style*="..."] rules further down. The modules carry roughly
   269 hardcoded colours written directly into inline style attributes
   (background:#fff, color:#666, background:#fff8e6 ...), from before there
   was any theming to speak of. Those cannot see a CSS variable, so on a dark
   page they would render as white cards with grey text - unreadable islands
   in an otherwise dark app.

   Rather than hand-edit 269 sites across 30 files in one go (a large, risky,
   entirely mechanical diff), this layer matches the exact inline strings and
   overrides them. It is blunt, and it uses !important, which is normally a
   smell - here it is unavoidable, because an inline style attribute wins
   over every ordinary stylesheet rule by specificity.

   This layer is a BRIDGE, not the destination. The proper fix is to replace
   those inline colours with semantic variables (--surface-warning,
   --text-muted, --text-success ...) module by module, deleting the matching
   rule here as each one goes. Doing that incrementally is safe; doing it all
   at once, untested, across every screen in the app, is not.

   The selectors match the exact no-space spelling the codebase actually uses
   (`background:#fff`, not `background: #fff`) - verified by grep across
   public/*.js before writing them. If you add an inline colour, either use a
   variable (preferred) or match the existing spelling so this net catches it.
   ========================================================================= */

:root[data-theme="dark"] {
  --primary-color: #7aa2d2;      /* headings/links - lightened; the original
                                    navy #0A2540 is invisible on dark        */
  --secondary-color: #8b84ff;    /* accent, lifted off #635BFF for contrast  */
  --bg-color: #12161c;           /* page                                     */
  --card-bg: #1a1f27;            /* raised surfaces                          */
  --text-dark: #e4e7ec;          /* primary text (name kept - it means
                                    "strong text", not literally dark)       */
  --text-light: #9aa4b2;         /* muted text                               */
  --border-color: #2b323d;
  --error-color: #ff6b61;        /* lifted for contrast on dark              */
  --success-color: #46d98f;
  /* On dark, an input is a WELL below the card surface, not a raised panel.
     Matching --card-bg would make it disappear into the card entirely. */
  --input-bg: #0e1218;
  --input-bg-focus: #10151c;
  --input-bg-disabled: #171b22;
  --input-border-hover: #3d4552;
  --focus-ring: rgba(139, 132, 255, 0.28);
  --table-header-bg: #10141a;
}

/* The sidebar is painted with the navy primary in light mode; on dark it
   needs its own surface or it becomes a pale slab against a dark page. */
:root[data-theme="dark"] .sidebar {
  background: #0e1218;
  border-right: 1px solid var(--border-color);
}

/* Cards and panels carry a light-mode drop shadow tuned for a white page;
   on dark, shadow reads as dirt. Swap to a hairline border instead. */
:root[data-theme="dark"] .card,
:root[data-theme="dark"] .auth-card {
  box-shadow: none;
  border: 1px solid var(--border-color);
}

/* Dark input colours now come from the --input-* tokens (see the form
   controls block above), so the old .form-control-only dark patch that was
   here is redundant and removed. */

/* Native form controls and scrollbars follow the page instead of staying
   stubbornly light (date pickers, select arrows, checkboxes). */
:root[data-theme="dark"] { color-scheme: dark; }

/* Tables: zebra striping and header fills that assume a white page. */
:root[data-theme="dark"] thead th { background: #10141a; color: var(--text-light); }
:root[data-theme="dark"] tbody tr:nth-child(even) { background: rgba(255, 255, 255, 0.02); }
:root[data-theme="dark"] tbody tr:hover { background: rgba(255, 255, 255, 0.045); }

/* LAYER 2 - the inline-style bridge - is now GENERATED, at the bottom of
   this file. It was hand-written and covered 38 of 87 inline colour
   literals; the other 49 stayed pale on a dark page (the notifications
   panel Craig reported was one of them). Hand-maintaining that list is a
   losing game - every inline colour anyone adds later is a hole nobody
   notices. Run `node tools/gen-dark-overrides.mjs` to see the coverage,
   `--write` to regenerate. See that file for the rules it applies. */

/* The theme toggle is a plain .nav-link (see dashboard.js's sidebar
   template) - it needs no styles of its own. It previously had a bespoke
   bordered-pill rule here with an emoji; removed 2026-08-26 on Craig's
   feedback. That rule also used --text-light/--border-color, which are
   light-theme greys and were nearly invisible against the navy sidebar. */



/* === BEGIN GENERATED DARK OVERRIDES (tools/gen-dark-overrides.mjs) === */
/* Do not edit by hand - run: node tools/gen-dark-overrides.mjs --write
   Each rule rewrites one inline colour literal for dark mode, keeping
   its hue so the meaning of the colour survives. !important is
   unavoidable: an inline style attribute outranks any normal rule. */

/* 47 pale surfaces -> dark, same hue */
:root[data-theme="dark"] [style*="background:#f2f2f2"] { background: #252b35 !important; }
:root[data-theme="dark"] [style*="background:#e8f5e9"] { background: #142e16 !important; }
:root[data-theme="dark"] [style*="background:#e3f2fd"] { background: #122330 !important; }
:root[data-theme="dark"] [style*="background:#fff8e1"] { background: #302912 !important; }
:root[data-theme="dark"] [style*="background:#fafafa"] { background: var(--card-bg) !important; }
:root[data-theme="dark"] [style*="background:#f5f5f5"] { background: var(--card-bg) !important; }
:root[data-theme="dark"] [style*="background:#eee"] { background: #252b35 !important; }
:root[data-theme="dark"] [style*="background:#ddd"] { background: #252b35 !important; }
:root[data-theme="dark"] [style*="background:#fff4f4"] { background: #301212 !important; }
:root[data-theme="dark"] [style*="background:#E6EBF1"] { background: #18202b !important; }
:root[data-theme="dark"] [style*="background:#f9fbfd"] { background: #122130 !important; }
:root[data-theme="dark"] [style*="background:#e6f4ea"] { background: #142e1c !important; }
:root[data-theme="dark"] [style*="background:#fdeaea"] { background: #301212 !important; }
:root[data-theme="dark"] [style*="background:#fff"]:not([style*="background:#fff8e1"]):not([style*="background:#fff4f4"]):not([style*="background:#fff3cd"]):not([style*="background:#fff8e6"]):not([style*="background:#fff8ec"]):not([style*="background:#ffffff"]) { background: var(--card-bg) !important; }
:root[data-theme="dark"] [style*="background:#f7f9fc"] { background: #121e30 !important; }
:root[data-theme="dark"] [style*="background:#fff3cd"] { background: #302912 !important; }
:root[data-theme="dark"] [style*="background:#e2d6f3"] { background: #1f1230 !important; }
:root[data-theme="dark"] [style*="background:#fde2d0"] { background: #301e12 !important; }
:root[data-theme="dark"] [style*="background:#d4edda"] { background: #142f1a !important; }
:root[data-theme="dark"] [style*="background:#d0e8f2"] { background: #122730 !important; }
:root[data-theme="dark"] [style*="background:#f7f9fb"] { background: #16212c !important; }
:root[data-theme="dark"] [style*="background:#fff8e6"] { background: #302812 !important; }
:root[data-theme="dark"] [style*="background:#eef4ff"] { background: #121d30 !important; }
:root[data-theme="dark"] [style*="background:#fdecea"] { background: #301512 !important; }
:root[data-theme="dark"] [style*="background:#eef6ff"] { background: #122030 !important; }
:root[data-theme="dark"] [style*="background:#eef9f0"] { background: #123018 !important; }
:root[data-theme="dark"] [style*="background:#eaf2ff"] { background: #121e30 !important; }
:root[data-theme="dark"] [style*="background:#eef2fb"] { background: #121b30 !important; }
:root[data-theme="dark"] [style*="background:#f7f7f7"] { background: var(--card-bg) !important; }
:root[data-theme="dark"] [style*="background:#eef5ff"] { background: #121f30 !important; }
:root[data-theme="dark"] [style*="background:#f3f9f4"] { background: #162c1a !important; }
:root[data-theme="dark"] [style*="background:#eefbf0"] { background: #123017 !important; }
:root[data-theme="dark"] [style*="background:#f0f6ff"] { background: #121e30 !important; }
:root[data-theme="dark"] [style*="background-color:#f9fbfd"] { background: #122130 !important; }
:root[data-theme="dark"] [style*="background:#fde8e6"] { background: #301512 !important; }
:root[data-theme="dark"] [style*="background:#e6f9ef"] { background: #123020 !important; }
:root[data-theme="dark"] [style*="background:#eef2fa"] { background: #121c30 !important; }
:root[data-theme="dark"] [style*="background-color:#fff"] { background: var(--card-bg) !important; }
:root[data-theme="dark"] [style*="background:#c7c7c7"] { background: #252b35 !important; }
:root[data-theme="dark"] [style*="background:#eef0f3"] { background: #1b2027 !important; }
:root[data-theme="dark"] [style*="background:#eafaf1"] { background: #12301f !important; }
:root[data-theme="dark"] [style*="background:#fff8ec"] { background: #302512 !important; }
:root[data-theme="dark"] [style*="background:#fde7e7"] { background: #301212 !important; }
:root[data-theme="dark"] [style*="background:#fde8e8"] { background: #301212 !important; }
:root[data-theme="dark"] [style*="background:#e8f7ee"] { background: #12301e !important; }
:root[data-theme="dark"] [style*="background:#ffffff"] { background: var(--card-bg) !important; }
:root[data-theme="dark"] [style*="background:#eef4fb"] { background: #122030 !important; }

/* 49 dark text colours -> light, same hue */
:root[data-theme="dark"] [style*="color:#666"] { color: #b8b8b8 !important; }
:root[data-theme="dark"] [style*="color:#777"] { color: #b8b8b8 !important; }
:root[data-theme="dark"] [style*="color:#1e7e34"] { color: #8ce4a0 !important; }
:root[data-theme="dark"] [style*="color:#b26a00"] { color: #e6c089 !important; }
:root[data-theme="dark"] [style*="color:#111"] { color: #b8b8b8 !important; }
:root[data-theme="dark"] [style*="color:#333"] { color: #b8b8b8 !important; }
:root[data-theme="dark"] [style*="color:#b00020"] { color: #e6899a !important; }
:root[data-theme="dark"] [style*="color:#1e7a34"] { color: #8ce3a1 !important; }
:root[data-theme="dark"] [style*="color:#a15c00"] { color: #e6be89 !important; }
:root[data-theme="dark"] [style*="color:#2e7d32"] { color: #97d99a !important; }
:root[data-theme="dark"] [style*="color:#8a6100"] { color: #e6ca89 !important; }
:root[data-theme="dark"] [style*="color:#d33"] { color: #e68989 !important; }
:root[data-theme="dark"] [style*="color:#6b4e00"] { color: #e6cd89 !important; }
:root[data-theme="dark"] [style*="color:#c0392b"] { color: #e5938a !important; }
:root[data-theme="dark"] [style*="color:#27ae60"] { color: #8ae5b1 !important; }
:root[data-theme="dark"] [style*="color:#856404"] { color: #e6ce89 !important; }
:root[data-theme="dark"] [style*="color:#5a3d7a"] { color: #b6a0cf !important; }
:root[data-theme="dark"] [style*="color:#8a4b1f"] { color: #e5b08a !important; }
:root[data-theme="dark"] [style*="color:#1e5c2a"] { color: #93dca1 !important; }
:root[data-theme="dark"] [style*="color:#134f63"] { color: #89cfe6 !important; }
:root[data-theme="dark"] [style*="color:#b8860b"] { color: #e6cb89 !important; }
:root[data-theme="dark"] [style*="color:#2c3e50"] { color: #a3b8cc !important; }
:root[data-theme="dark"] [style*="color:#ff2d2d"] { color: #e68989 !important; }
:root[data-theme="dark"] [style*="color:#2c5aa0"] { color: #8fafe0 !important; }
:root[data-theme="dark"] [style*="color:#222"] { color: #b8b8b8 !important; }
:root[data-theme="dark"] [style*="color:#8a6d1a"] { color: #e6ce89 !important; }
:root[data-theme="dark"] [style*="color:#8a94a3"] { color: #afb6c0 !important; }
:root[data-theme="dark"] [style*="color:#8a6300"] { color: #e6cc89 !important; }
:root[data-theme="dark"] [style*="color:#3a4d8f"] { color: #99a7d6 !important; }
:root[data-theme="dark"] [style*="color:#1a7a4c"] { color: #89e6ba !important; }
:root[data-theme="dark"] [style*="color:#444"] { color: #b8b8b8 !important; }
:root[data-theme="dark"] [style*="color:#4f46e5"] { color: #8e89e6 !important; }
:root[data-theme="dark"] [style*="color:#8a6d00"] { color: #e6d389 !important; }
:root[data-theme="dark"] [style*="color:#3b4a63"] { color: #a6b3ca !important; }
:root[data-theme="dark"] [style*="color:#F5A623"] { color: #e6c389 !important; }
:root[data-theme="dark"] [style*="color:#32D583"] { color: #89e6b7 !important; }
:root[data-theme="dark"] [style*="color:#999"] { color: #b8b8b8 !important; }
:root[data-theme="dark"] [style*="color:#7a5b12"] { color: #e6ca89 !important; }
:root[data-theme="dark"] [style*="color:#a06d00"] { color: #e6c889 !important; }
:root[data-theme="dark"] [style*="color:#2a8f4a"] { color: #91dfa9 !important; }
:root[data-theme="dark"] [style*="color:#a00"] { color: #e68989 !important; }
:root[data-theme="dark"] [style*="color:#1c6b36"] { color: #8ee1a9 !important; }
:root[data-theme="dark"] [style*="color:#888"] { color: #b8b8b8 !important; }
:root[data-theme="dark"] [style*="color:#2a6fd6"] { color: #89aee6 !important; }
:root[data-theme="dark"] [style*="color:#e6a700"] { color: #e6cd89 !important; }
:root[data-theme="dark"] [style*="color:#12161c"] { color: #a8b4c7 !important; }
:root[data-theme="dark"] [style*="color:#0A2540"] { color: #89b8e6 !important; }
:root[data-theme="dark"] [style*="color:#1a7a3c"] { color: #89e6aa !important; }
:root[data-theme="dark"] [style*="color:#1c2b4a"] { color: #97acd8 !important; }
/* === END GENERATED DARK OVERRIDES === */
