/*
 * MESPL Procurement Tracker — shared design system.
 * Every custom screen (dashboard, import wizard, and everything built after
 * it) should build on these tokens and components rather than inventing new
 * colors/spacing/shadows inline. Django admin is out of scope — untouched.
 */

:root {
  --color-bg: #f5f5f7;
  --color-surface: #ffffff;
  --color-surface-muted: #f2f2f7;
  --color-text: #1d1d1f;
  --color-text-muted: #6e6e73;
  --color-border: #e5e5ea;
  --color-primary: #0a84ff;
  --color-primary-tint: #eaf4ff;

  --radius-sm: 0.5rem;
  --radius-md: 0.875rem;
  --radius-lg: 1.25rem;
  --radius-full: 999px;

  --space-1: 0.25rem;
  --space-2: 0.5rem;
  --space-3: 0.75rem;
  --space-4: 1rem;
  --space-5: 1.25rem;
  --space-6: 1.5rem;
  --space-8: 2rem;

  --shadow-card: 0 1px 2px rgba(0, 0, 0, 0.04), 0 8px 24px rgba(0, 0, 0, 0.06);
  --shadow-nav: 0 1px 0 rgba(0, 0, 0, 0.06);

  --touch-min: 44px;
  --font-sans: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  /* Synced top scrollbar for wide tables (below) — deliberately thicker
     than a default OS scrollbar sliver so it's comfortably grabbable with
     a mouse and fine to drag on touch. */
  --table-scrollbar-height: 16px;
}

* {
  box-sizing: border-box;
}

html,
body {
  margin: 0;
  padding: 0;
  background: var(--color-bg);
  color: var(--color-text);
  font-family: var(--font-sans);
  -webkit-font-smoothing: antialiased;
}

a {
  color: var(--color-primary);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

/* ---------- Nav ---------- */

.app-nav {
  position: sticky;
  top: 0;
  z-index: 30;
  background: rgba(255, 255, 255, 0.86);
  backdrop-filter: saturate(180%) blur(12px);
  -webkit-backdrop-filter: saturate(180%) blur(12px);
  box-shadow: var(--shadow-nav);
}

.app-nav__bar {
  max-width: 56rem;
  margin: 0 auto;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-2) var(--space-3);
  padding: var(--space-2) var(--space-4);
  flex-wrap: wrap;
}

.app-nav__brand {
  font-weight: 600;
  font-size: 1.0625rem;
  color: var(--color-text);
  min-height: var(--touch-min);
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  /* Long as this brand string is, it must be able to wrap onto a second line
     on a narrow phone rather than forcing the whole bar wider than the
     viewport — see .app-nav__links below for the matching fix on the link
     row (both were the cause of the "Log out clipped off-screen" bug). */
  flex: 1 1 auto;
  min-width: 0;
}

.app-nav__logo {
  height: 30px;
  width: auto;
  max-width: 120px;
  object-fit: contain;
  flex-shrink: 0;
}

.app-nav__links {
  display: flex;
  align-items: center;
  /* This row used to be nowrap while its parent (.app-nav__bar) was allowed
     to wrap — so at phone widths the bar had room to wrap the brand+links
     onto separate lines, but the links themselves stayed on one unbroken
     line and simply ran off the right edge of the viewport (e.g. "Log out"
     fully clipped at 320-414px). Letting this row wrap too, plus a row-gap,
     is what actually fixes it — every link now flows onto as many lines as
     it needs instead of overflowing.  */
  flex-wrap: wrap;
  gap: var(--space-2) var(--space-4);
  font-size: 0.9375rem;
}

.app-nav__links a,
.app-nav__links button {
  color: var(--color-text-muted);
  min-height: var(--touch-min);
  display: inline-flex;
  align-items: center;
  background: none;
  border: none;
  font-family: var(--font-sans);
  font-size: inherit;
  cursor: pointer;
  padding: 0;
}

.app-nav__links a:hover,
.app-nav__links button:hover {
  color: var(--color-text);
  text-decoration: none;
}

.app-nav__user {
  color: var(--color-text-muted);
  font-size: 0.875rem;
  display: inline-flex;
  align-items: center;
  min-height: var(--touch-min);
}

/* ---------- Layout ---------- */

.app-main {
  max-width: 56rem;
  margin: 0 auto;
  padding: var(--space-6) var(--space-4) var(--space-8);
}

.mt-4 {
  margin-top: var(--space-4);
}

/* ---------- Cards ---------- */

.card {
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-6);
}

.card + .card {
  margin-top: var(--space-4);
}

.card-header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.card-header .card-title,
.card-header .card-subtitle {
  margin: 0;
}

.card-header .card-subtitle {
  margin-top: var(--space-2);
}

/* A right-aligned row of buttons living inside a .card-header (e.g. the
   money summary card's Export buttons) — plain flex+gap, no margin-top
   (unlike .filter-actions, which assumes it sits below a form). */
.card-header-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.card-title {
  font-size: 1.375rem;
  font-weight: 600;
  margin: 0 0 var(--space-2);
}

.card-subtitle {
  color: var(--color-text-muted);
  margin: 0;
  font-size: 0.9375rem;
}

.card-subtitle + * {
  margin-top: var(--space-4);
}

.section-title {
  font-size: 1rem;
  font-weight: 600;
  margin: 0 0 var(--space-3);
}

.text-muted {
  color: var(--color-text-muted);
}

/* ---------- Stat tiles ---------- */

.stat-grid {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
}

@media (min-width: 640px) {
  .stat-grid {
    grid-template-columns: repeat(4, 1fr);
  }
}

.stat-tile {
  background: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  padding: var(--space-4) var(--space-5);
}

.stat-tile__label {
  color: var(--color-text-muted);
  font-size: 0.8125rem;
}

.stat-tile__value {
  font-size: 1.75rem;
  font-weight: 600;
  margin-top: var(--space-1);
}

.stat-tile__value--negative {
  color: #c62828;
}

/* ---------- Money strip (compact money summary — see SPEC.md's "Margin
   tracking" / "Compact display") ---------- */
/* A row of stats inside a single .card, NOT one .stat-tile per number —
   that's what produced five oversized cards with wrapping totals before
   this replaced it. Label small and muted above, value below, left-aligned. */

.money-strip {
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-4);
}

@media (min-width: 640px) {
  /* Not equal columns: "Ordered" and "Margin" each combine two figures
     onto one line ("₹X of ₹Y · Z%", "₹X · Y%") and need real room for
     that — an even 4-way split was too narrow at this font size and
     wrapped mid-phrase. Relies on stat order matching this ratio:
     Client total, Ordered, Vendor committed, Margin. */
  .money-strip {
    grid-template-columns: 1fr 1.7fr 1fr 1.7fr;
  }
}

.money-strip__label {
  color: var(--color-text-muted);
  font-size: 0.75rem;
}

.money-strip__value {
  font-size: 1.125rem;
  font-weight: 600;
  margin-top: var(--space-1);
}

.money-strip__value[title] {
  cursor: help;
  text-decoration: underline dotted var(--color-border);
  text-underline-offset: 3px;
}

.money-strip__value--negative {
  color: #c62828;
}

/* ---------- Tappable option rows (sheet picker etc.) ---------- */

.option-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.option-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--touch-min);
  padding: var(--space-3) var(--space-4);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  background: var(--color-surface);
  cursor: pointer;
  transition: border-color 0.15s ease, background-color 0.15s ease;
}

.option-row:hover {
  border-color: #c7c7cc;
}

.option-row:has(input:checked) {
  border-color: var(--color-primary);
  background: var(--color-primary-tint);
}

.option-row input[type="radio"] {
  width: 20px;
  height: 20px;
  accent-color: var(--color-primary);
  flex-shrink: 0;
}

.option-row__label {
  font-size: 0.9375rem;
}

/* Card-style option row (import-type picker etc.): a title plus multi-line
   helper text stacked underneath, instead of .option-row's default single
   line — needs top-aligned content since the body is now taller than the
   radio input. */
.option-row--card {
  align-items: flex-start;
}

.option-row--card .option-row__label {
  font-weight: 600;
}

.option-row__body {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

.option-row__helper {
  font-size: 0.8125rem;
  line-height: 1.4;
}

.project-row {
  justify-content: space-between;
  color: inherit;
  text-decoration: none;
}

.project-row:hover {
  text-decoration: none;
}

.project-row__name {
  font-weight: 600;
  font-size: 1rem;
}

.project-row__meta {
  color: var(--color-text-muted);
  font-size: 0.8125rem;
  margin-top: 2px;
}

.project-row__stats {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: 0.875rem;
  flex-wrap: wrap;
  justify-content: flex-end;
}

/* ---------- Buttons ---------- */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: var(--touch-min);
  padding: 0 var(--space-5);
  border-radius: var(--radius-full);
  font-size: 0.9375rem;
  font-weight: 600;
  border: none;
  cursor: pointer;
  transition: opacity 0.15s ease;
  font-family: var(--font-sans);
}

.btn:hover {
  opacity: 0.88;
  text-decoration: none;
}

.btn-primary {
  background: var(--color-primary);
  color: #fff;
}

.btn-secondary {
  background: var(--color-surface-muted);
  color: var(--color-text);
}

.btn-danger {
  background: #c62828;
  color: #fff;
}

/* ---------- Forms ---------- */

.field {
  margin-bottom: var(--space-4);
}

.field label {
  display: block;
  font-size: 0.875rem;
  font-weight: 500;
  margin-bottom: var(--space-2);
}

.input,
select,
textarea,
input[type="text"],
input[type="password"],
input[type="date"],
input[type="file"] {
  width: 100%;
  min-height: var(--touch-min);
  padding: 0 var(--space-3);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: 0.9375rem;
  font-family: var(--font-sans);
  background: var(--color-surface);
  color: var(--color-text);
}

input[type="file"] {
  padding: var(--space-2) var(--space-3);
}

textarea {
  padding: var(--space-3);
  min-height: 5.5rem;
  resize: vertical;
}

.input:focus,
select:focus,
textarea:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(10, 132, 255, 0.18);
}

.options-card {
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  padding: var(--space-3) var(--space-4);
  background: var(--color-surface);
}

.filter-grid,
.action-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(9rem, 1fr));
  gap: var(--space-3);
  align-items: end;
}

.filter-grid .field,
.action-grid .field {
  margin-bottom: 0;
}

.filter-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-top: var(--space-4);
}

.checkbox-row {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  min-height: var(--touch-min);
  font-size: 0.9375rem;
}

.checkbox-row input[type="checkbox"] {
  width: 20px;
  height: 20px;
  accent-color: var(--color-primary);
  flex-shrink: 0;
}

/* ---------- Tables ---------- */

.table-wrap {
  overflow-x: auto;
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-card);
  background: var(--color-surface);
  -webkit-overflow-scrolling: touch;
}

/* Synced top scrollbar (static/js/app.js inserts one of these immediately
   before every .table-wrap) — the only reachable horizontal scrollbar on a
   long table used to be the native one at the very bottom of it, which on
   a 500+ row table means scrolling past the whole thing first. This one
   sits right above the header instead and stays there (position: sticky)
   as the page scrolls, mirroring .table-wrap's own native scrollbar exactly
   (app.js keeps them in sync both ways) — dragging either one moves the
   table the same way.
   A fully custom thumb/track, not a styled native scrollbar: real
   `::-webkit-scrollbar` customization turned out not to reliably paint
   across browsers/platforms when we checked — a plain absolutely-positioned
   div app.js drives directly has no such dependency and looks identical
   everywhere. Always reserves this height, even when the table doesn't
   currently overflow (app.js just hides the thumb then) — so its
   appearance/disappearance never shifts the layout. */
.table-scroll-top {
  position: sticky;
  top: 0;
  z-index: 2;
  height: var(--table-scrollbar-height);
  margin-bottom: var(--space-2);
  background: var(--color-surface-muted);
  border-radius: var(--radius-full);
}

.table-scroll-top__thumb {
  position: absolute;
  top: 0;
  left: 0;
  height: 100%;
  background: #b5b5bd;
  border-radius: var(--radius-full);
  cursor: grab;
  /* Comfortably grabbable per the design language, not a thin sliver —
     matches --table-scrollbar-height rather than some thinner inline bar. */
  touch-action: none;
}

.table-scroll-top__thumb:hover {
  background: #9a9aa2;
}

.table-scroll-top__thumb:active {
  background: #83838b;
  cursor: grabbing;
}

table.app-table {
  border-collapse: collapse;
  width: 100%;
  font-size: 0.875rem;
}

table.app-table thead th {
  position: sticky;
  /* Offset by the top scrollbar's height, not 0, so the two don't overlap
     if they're ever both stuck at once. */
  top: var(--table-scrollbar-height);
  z-index: 1;
  background: var(--color-surface-muted);
  text-align: left;
  padding: var(--space-3);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
  font-weight: 600;
}

table.app-table thead select {
  min-width: 8.5rem;
  min-height: 36px;
  font-size: 0.8125rem;
}

.th-label {
  font-weight: 400;
  color: var(--color-text-muted);
  font-size: 0.75rem;
  margin-bottom: var(--space-1);
  max-width: 10rem;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

table.app-table tbody td {
  padding: var(--space-3);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
  max-width: 16rem;
  overflow: hidden;
  text-overflow: ellipsis;
}

table.app-table tbody tr {
  min-height: var(--touch-min);
  transition: background-color 0.15s ease;
}

table.app-table tbody tr:hover {
  background: var(--color-surface-muted);
}

table.app-table tbody tr:has(input:checked) {
  background: var(--color-primary-tint);
}

table.app-table tbody tr.is-selectable {
  cursor: pointer;
}

table.app-table tbody tr label {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  margin: 0;
  cursor: pointer;
  font-weight: 600;
}

table.app-table td.checkbox-cell {
  width: 2.5rem;
  white-space: nowrap;
}

/* ---------- Description column: word-truncated preview, inline expand ---------- */

/* Wider than the default 16rem column cap: truncatewords already limits the
   collapsed preview to ~10 words, so the column needs enough room to show that
   whole phrase on its own line — otherwise the generic ellipsis rule clips it
   again, mid-word, before the clean word-boundary "…" ever shows. */
table.app-table tbody td[data-col="description"] {
  max-width: 30rem;
}

table.app-table tbody td.is-expanded {
  white-space: normal;
  max-width: 30rem;
  overflow: visible;
  text-overflow: clip;
}

.description-toggle {
  display: block;
  width: 100%;
  text-align: left;
  background: none;
  border: none;
  padding: 0;
  margin: 0;
  font: inherit;
  color: inherit;
  cursor: pointer;
  overflow: hidden;
  text-overflow: ellipsis;
}

/* `display: block` above has the same specificity as the UA stylesheet's
   `[hidden] { display: none }` and would otherwise win by source order,
   silently keeping the button visible after JS sets the hidden attribute. */
.description-toggle[hidden] {
  display: none;
}

.description-toggle:hover .description-preview {
  text-decoration: underline;
  text-decoration-style: dotted;
}

.description-full {
  white-space: normal;
  margin-top: var(--space-2);
}

.description-full p {
  margin: 0 0 var(--space-2);
}

.btn-link {
  background: none;
  border: none;
  padding: 0;
  color: var(--color-primary);
  font-family: var(--font-sans);
  font-size: inherit;
  cursor: pointer;
  min-height: auto;
}

.btn-link:hover {
  text-decoration: underline;
}

.btn-link--danger {
  color: #c62828;
}

table.app-table td form {
  margin: 0;
}

/* ---------- Vendors screen row actions ---------- */

.vendor-row-actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  flex-wrap: wrap;
  white-space: nowrap;
}

table.app-table select.status-select {
  min-width: 11rem;
  min-height: 36px;
  font-size: 0.8125rem;
}

tr.history-row td {
  background: var(--color-surface-muted);
  white-space: normal;
  max-width: none;
}

.history-log-list {
  list-style: none;
  margin: var(--space-2) 0 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  font-size: 0.8125rem;
}

.history-log-list li {
  padding-bottom: var(--space-2);
  border-bottom: 1px solid var(--color-border);
}

.history-log-list li:last-child {
  border-bottom: none;
  padding-bottom: 0;
}

/* ---------- Messages ---------- */

.app-messages {
  list-style: none;
  margin: 0 0 var(--space-4);
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.app-message {
  background: #fff8e1;
  color: #8a5a00;
  padding: var(--space-3) var(--space-4);
  border-radius: var(--radius-md);
  font-size: 0.875rem;
}

.app-message--error {
  background: #ffefed;
  color: #b3261e;
}

/* ---------- Pills: one consistent color per status / issue flag ---------- */

.pill {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  font-size: 0.8125rem;
  font-weight: 600;
  white-space: nowrap;
}

.pill-not-started {
  background: #eceff1;
  color: #455a64;
}

.pill-enquiry-sent {
  background: #e3f2fd;
  color: #1565c0;
}

.pill-quote-received {
  background: #ede7f6;
  color: #5e35b1;
}

.pill-po-issued {
  background: #ede7f6;
  color: #4527a0;
}

.pill-drawing-under-approval {
  background: #fff8e1;
  color: #b28704;
}

.pill-drawing-approved-code-1 {
  background: #e0f2f1;
  color: #00695c;
}

.pill-drawing-approved-with-comments-code-2 {
  background: #f1f8e9;
  color: #558b2f;
}

.pill-drawing-returned-for-revision-code-3 {
  background: #fbe9e7;
  color: #d84315;
}

.pill-under-manufacturing {
  background: #fff3e0;
  color: #e65100;
}

.pill-ready-for-dispatch {
  background: #e1f5fe;
  color: #0277bd;
}

.pill-dispatched {
  background: #e8f5e9;
  color: #2e7d32;
}

.pill-delivered-at-site {
  background: #e8f5e9;
  color: #1b5e20;
}

.pill-closed {
  background: #eceff1;
  color: #37474f;
}

.pill-rate-revision-pending {
  background: #fff8e1;
  color: #b28704;
}

.pill-delivery-delayed {
  background: #ffebee;
  color: #c62828;
}

.pill-vendor-not-responding {
  background: #fce4ec;
  color: #ad1457;
}

.pill-client-clearance-pending {
  background: #fff3e0;
  color: #e65100;
}

.pill-quality-issue {
  background: #ffebee;
  color: #b71c1c;
}

.pill-payment-hold {
  background: #ffebee;
  color: #b71c1c;
}

.pill-other {
  background: #eceff1;
  color: #455a64;
}

/* PO approval_status — same one-consistent-color-per-value convention as
   the status/issue-flag pills above (class name = slugify of the label). */
.pill-draft {
  background: #eceff1;
  color: #455a64;
}

.pill-pending-approval {
  background: #fff8e1;
  color: #b28704;
}

.pill-approved {
  background: #e8f5e9;
  color: #2e7d32;
}

.pill-rejected {
  background: #ffebee;
  color: #c62828;
}

/* Final sign-off stage (SPEC.md's "PO document generation") — distinct from
   the approval_status pills above since a PO can be Approved and still be
   anywhere in this separate document-finalization stage. */
.pill-not-requested {
  background: #eceff1;
  color: #455a64;
}

.pill-pending-final-signoff {
  background: #ede7f6;
  color: #5e35b1;
}

.pill-final-approved {
  background: #e0f2f1;
  color: #00695c;
}

.pill-sent {
  background: #e3f2fd;
  color: #1565c0;
}

/* Negative margin (SPEC.md's "Margin tracking") — the vendor costs more
   than what was budgeted. Deliberately the same red as pill-rejected: this
   needs to read as unambiguously bad at a glance, not just "different". */
.pill-negative-margin {
  background: #ffebee;
  color: #c62828;
}

/* "Unverified" vendor chip (SPEC.md's "Vendor verification & cleanup") —
   sits next to a vendor name in tables/dropdowns. Deliberately smaller and
   lower-contrast than the status/issue pills above: it's a subtle "someone
   should look at this" note, not an alert. */
.pill-unverified {
  background: #f5f5f5;
  color: #757575;
  font-size: 0.6875rem;
  font-weight: 600;
  padding: 1px var(--space-2);
}

/* ---------- Table toolbar: Columns button ---------- */

.table-toolbar {
  display: flex;
  align-items: center;
  gap: var(--space-4);
  flex-wrap: wrap;
}

/* ---------- Pagination ---------- */

.pagination-bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.pagination-bar p {
  margin: 0;
}

.pagination-controls {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* ---------- Dashboard: status pill wrap, grouped exception lists ---------- */

.pill-wrap {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.dash-group {
  margin-top: var(--space-5);
}

.dash-group:first-child {
  margin-top: var(--space-4);
}

.dash-group__title {
  display: flex;
  align-items: center;
  min-height: var(--touch-min);
  font-weight: 600;
  font-size: 0.9375rem;
}

.dash-line-row {
  padding: var(--space-2) 0;
  border-bottom: 1px solid var(--color-border);
}

.dash-group .dash-line-row:last-child {
  border-bottom: none;
}

.dash-line-row__main {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.dash-line-row__desc {
  font-size: 0.9375rem;
}

.dash-line-row__meta {
  font-size: 0.8125rem;
  margin-top: var(--space-1);
}

.dash-resolve-form {
  display: flex;
  gap: var(--space-2);
  margin-top: var(--space-2);
}

.dash-resolve-form input[type="text"] {
  flex: 1;
  min-width: 0;
}

/* ---------- Column visibility (Columns button — see app.js) ---------- */

#line-table-wrapper.hide-col-item_no [data-col="item_no"],
#line-table-wrapper.hide-col-description [data-col="description"],
#line-table-wrapper.hide-col-qty [data-col="qty"],
#line-table-wrapper.hide-col-uom [data-col="uom"],
#line-table-wrapper.hide-col-scope [data-col="scope"],
#line-table-wrapper.hide-col-category [data-col="category"],
#line-table-wrapper.hide-col-vendor [data-col="vendor"],
#line-table-wrapper.hide-col-po [data-col="po"],
#line-table-wrapper.hide-col-rate [data-col="rate"],
#line-table-wrapper.hide-col-amount [data-col="amount"],
#line-table-wrapper.hide-col-status [data-col="status"],
#line-table-wrapper.hide-col-issue [data-col="issue"],
#line-table-wrapper.hide-col-remark [data-col="remark"] {
  display: none;
}

/* ---------- PO quote comparison grid ---------- */
/* Desktop: a wide table, vendors as columns. Phone: vendor cards instead —
   a comparison grid genuinely doesn't work at phone width even with
   horizontal scroll (SPEC.md's "PO quote comparison"), so this is a real
   second rendering of the same data, not just a squeezed table. Toggled by
   the same 640px breakpoint app.js's MOBILE_QUERY uses for Columns. */

.comparison-cards-phone {
  display: none;
}

@media (max-width: 640px) {
  .comparison-table-desktop {
    display: none;
  }
  .comparison-cards-phone {
    display: block;
  }
}

/* Compact input for a dense per-item x per-vendor rate grid — the standard
   44px touch-min .input/select/textarea rule (see Forms above) is right for
   primary controls but would make a multi-row x multi-column grid absurdly
   tall; table.app-table thead select already carries the same kind of
   compact override for the same reason. */
.rate-cell-input {
  width: 6.5rem;
  min-height: 36px;
  padding: 0 var(--space-2);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-sm);
  font-size: 0.8125rem;
  font-family: var(--font-sans);
  background: var(--color-surface);
  color: var(--color-text);
}

.rate-cell-input:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px rgba(10, 132, 255, 0.18);
}

.comparison-cell-amount {
  display: block;
  margin-top: var(--space-1);
  color: var(--color-text-muted);
  font-size: 0.75rem;
}

/* The "Approve" row's cell holds a remark input + button, not plain text —
   the generic table.app-table tbody td rule above (nowrap + ellipsis +
   16rem cap) was written for data cells and silently clipped the button
   behind an ellipsis the moment the remark placeholder made the row wider
   than the column, with no visual sign anything was cut off beyond a
   sliver of the button's edge. */
table.app-table td.comparison-approve-cell {
  white-space: normal;
  overflow: visible;
  text-overflow: clip;
  max-width: none;
}

table.app-table td.comparison-approve-cell form {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-2);
}

table.app-table td.comparison-approve-cell .rate-cell-input {
  width: 100%;
  min-width: 9rem;
}

.rank-badge {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  border-radius: var(--radius-full);
  font-size: 0.75rem;
  font-weight: 700;
  white-space: nowrap;
}

.rank-badge--l1 {
  background: #e8f5e9;
  color: #2e7d32;
}

.rank-badge--other {
  background: #eceff1;
  color: #455a64;
}

.rank-badge--unranked {
  background: #f2f2f7;
  color: var(--color-text-muted);
}

.vendor-card {
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  background: var(--color-surface);
}

.vendor-card + .vendor-card {
  margin-top: var(--space-4);
}

.vendor-card--selected {
  border-color: #2e7d32;
  box-shadow: 0 0 0 2px rgba(46, 125, 50, 0.18);
}

.vendor-card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-3);
  flex-wrap: wrap;
}

.vendor-card__name {
  font-weight: 600;
  font-size: 1rem;
}

.vendor-card__totals {
  margin-top: var(--space-3);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2) var(--space-4);
  font-size: 0.875rem;
}

.vendor-card__totals dt {
  color: var(--color-text-muted);
  font-size: 0.75rem;
}

.vendor-card__totals dd {
  margin: 0;
  font-weight: 600;
}

.comparison-details {
  margin-top: var(--space-4);
}

.comparison-details summary {
  cursor: pointer;
  font-weight: 600;
  font-size: 0.875rem;
  min-height: var(--touch-min);
  display: flex;
  align-items: center;
  color: var(--color-primary);
}

.comparison-details table {
  width: 100%;
  margin-top: var(--space-2);
  font-size: 0.8125rem;
  border-collapse: collapse;
}

.comparison-details td,
.comparison-details th {
  padding: var(--space-2);
  border-bottom: 1px solid var(--color-border);
  text-align: left;
}

/* ---------- Per-project money table (dashboard) ---------- */
/* Same desktop-table / phone-cards split as the PO quote comparison grid
   above: a wide per-project money table genuinely doesn't work at phone
   width even with horizontal scroll, so phone gets a real second rendering
   (one card per project) rather than a squeezed table. */

.money-cards-phone {
  display: none;
}

@media (max-width: 640px) {
  .money-table-desktop {
    display: none;
  }
  .money-cards-phone {
    display: block;
  }
}

table.app-table.money-table th a {
  color: inherit;
}

table.app-table tbody tr.money-row--negative {
  background: #fff5f4;
}

table.app-table tfoot td {
  padding: var(--space-3) var(--space-4);
  border-top: 2px solid var(--color-border);
  font-weight: 600;
  white-space: nowrap;
}

.money-card {
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-lg);
  padding: var(--space-4);
  background: var(--color-surface);
}

.money-card + .money-card {
  margin-top: var(--space-4);
}

.money-card--negative {
  border-color: #c62828;
  box-shadow: 0 0 0 2px rgba(198, 40, 40, 0.14);
}

.money-card--total {
  background: var(--color-surface-muted);
}

.money-card__name {
  font-weight: 600;
  font-size: 1rem;
}

.money-card__totals {
  margin-top: var(--space-3);
  display: grid;
  grid-template-columns: repeat(2, 1fr);
  gap: var(--space-2) var(--space-4);
  font-size: 0.875rem;
}

.money-card__totals dt {
  color: var(--color-text-muted);
  font-size: 0.75rem;
}

.money-card__totals dd {
  margin: 0;
  font-weight: 600;
}

/* ---------- Login page ---------- */

.login-page {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-6);
  padding: var(--space-6) var(--space-4);
}

.login-page__brand {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-3);
  text-align: center;
}

.login-page__logo {
  height: 72px;
  width: auto;
  max-width: 100%;
  object-fit: contain;
}

.login-page__company {
  font-size: 1.25rem;
  font-weight: 600;
  color: var(--color-text);
}

.login-page__card {
  width: 100%;
  max-width: 22rem;
}

.login-page__submit {
  width: 100%;
  margin-top: var(--space-2);
}

/* ---------- Two-factor auth (enrolment + code challenge) ---------- */

.twofa-card {
  max-width: 26rem;
  margin-inline: auto;
}

.twofa-qr {
  display: flex;
  justify-content: center;
  margin: var(--space-5) 0;
}

.twofa-qr img {
  width: 200px;
  height: 200px;
  max-width: 100%;
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
}

.twofa-key {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  word-break: break-all;
  background: var(--color-surface-muted);
  padding: var(--space-1) var(--space-2);
  border-radius: var(--radius-sm);
}

.twofa-code-input {
  letter-spacing: 0.35em;
  font-size: 1.25rem;
  text-align: center;
}

.twofa-secondary {
  margin-top: var(--space-4);
}
