/* ============================================================================
   Delivery Order Portal — design system

   Styled to sit alongside CSUL: the same deep-navy rail, the same white rounded
   brand card at its head, the same light-grey canvas and flat white panels. A
   user moving between the two products should not feel they have changed
   vendor.

   What is deliberately NOT copied is CSUL's dashboard furniture — the revenue
   charts and analytics tiles. This portal does one job, getting a Delivery
   Order issued, and decorating it with graphs it does not need would be
   theatre. The four counters it does carry are navigation, not analytics:
   each is a link to the queue it counts.

   The one deliberate departure is the AI layer, which uses the accent colours
   from the logo's squares. That part has no CSUL equivalent and should read as
   its own thing.

   Single hand-written stylesheet, no bundler, no framework — house convention.
   No React either: this is server-rendered EJS, and a framework would buy
   nothing here but a build step.
   ========================================================================== */

:root {
  /* CSUL's chrome, derived from its brand blue. */
  --navy: #002A66;           /* CSUL's blue: sidebar, every heading */
  --navy-deep: #001B44;      /* foot of the sidebar gradient */
  --navy-mid: #063C82;       /* sidebar hover wash, login backdrop bands */
  --primary: #003D91;        /* primary button, links — 10.1:1 on white */
  --primary-hover: #002A66;  /* hover darkens to the brand blue itself */
  --focus-ring: rgba(0, 61, 145, .35);

  /* The logo's squares. The AI layer wears these and nothing else does — it is
     the part of this portal CSUL has no equivalent for. */
  --logo-sky: #3FA8E0;
  --logo-cyan: #46C4D6;
  --logo-mint: #5FBE9B;
  --teal: #0F7A63;           /* retained for success / complete states */

  --bg: #EEF1F5;
  --card: #FFFFFF;
  --border: #DDE3EC;
  --border-strong: #C7D0DE;

  --text: #15243C;
  --text-secondary: #54627A;
  --text-muted: #8794A8;

  --success-bg: #E3F5EE; --success-text: #0F7A63; --success-border: #A6E2CD;
  --warning-bg: #FDF4E3; --warning-text: #8A5A0B; --warning-border: #F0CE8C;
  --danger-bg:  #FBEBEB; --danger-text:  #A62B2B; --danger-border:  #E9B4B4;
  --info-bg:    #E7EFFA; --info-text:    #003D91; --info-border:    #BBD0EC;

  /* Type scale — 16px base, set against CSUL side by side.
     History worth keeping, because two rounds were wasted on it. It began at a
     flat 14px, went DOWN to 13px chasing what looked like CSUL's density, and
     came back to 14px — a net change of nothing, on a screen already reported
     as hard to read. Comparing screenshots directly settled it: CSUL runs
     roughly 16px body with 17-18px navigation and near-heading-sized tile
     labels, and sets values at 600-700 rather than 500. It is not a dense grid
     at all; the density was an illusion from its tight row spacing.
     So: bigger, and heavier. Weight does as much work here as size. */
  --fs-2xs: 12px;   /* uppercase micro-labels: table headers, nav sections */
  --fs-xs:  13px;   /* badges, tags, hints */
  --fs-sm:  14px;   /* .small, secondary lines */
  --fs-base: 16px;  /* body, inputs, buttons, table cells */
  --fs-md:  17px;   /* card titles, brand name, sidebar links */
  --fs-lg:  20px;   /* h3, appbar title */
  --fs-xl:  24px;   /* h2 */
  --fs-2xl: 28px;   /* h1 */
  --fs-num: 38px;   /* KPI tile figures */

  /* Spacing, so density is a token rather than a number retyped in 40 rules. */
  --pad-card: 18px 20px;
  --pad-cell: 12px 14px;

  --radius: 10px;
  --radius-sm: 6px;
  --radius-lg: 12px;
  --shadow-card: 0 1px 2px rgba(0, 42, 102, .07), 0 4px 14px rgba(0, 42, 102, .05);
  --shadow-card-hover: 0 2px 4px rgba(0, 42, 102, .09), 0 10px 26px rgba(0, 42, 102, .09);

  --sidebar-w: 248px;
}

* { box-sizing: border-box; }

/* ── Type ──────────────────────────────────────────────────────────────── */
/* Self-hosted, deliberately. The CSP declares no font-src, so it falls back to
   default-src 'self' and a CDN would need the policy widened. Roboto is also
   absent from Windows, so a system-only stack would silently render Segoe UI
   and the change would look done without being done.

   Google serves Roboto as a VARIABLE font now — one file covers 100–900, which
   is why there is one @font-face here and not three. */
@font-face {
  font-family: Roboto;
  font-style: normal;
  font-weight: 100 900;
  font-display: swap;
  src: url('/fonts/roboto-variable.woff2') format('woff2-variations');
}

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font-family: Roboto, -apple-system, 'Segoe UI', system-ui, sans-serif;
  font-size: var(--fs-base);
  line-height: 1.55;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

a { color: var(--primary); }
/* 700. CSUL sets its headings and its data values heavy — "930 Shipment",
   "AN NAFI ENTERPRISES", "SI Job" are all bold, and that weight is most of why
   it reads as solid next to this. 500 made every heading recede into its own
   body copy. */
h1, h2, h3, h4 { margin: 0 0 6px; font-weight: 700; color: var(--navy); }
h1 { font-size: var(--fs-2xl); letter-spacing: -.015em; line-height: 1.25; }
h2 { font-size: var(--fs-xl); letter-spacing: -.01em; line-height: 1.3; }
h3 { font-size: var(--fs-lg); letter-spacing: -.005em; line-height: 1.35; }
h4 { font-size: var(--fs-md); line-height: 1.4; }
p { margin: 0 0 12px; }
code { font-family: ui-monospace, Consolas, monospace; font-size: .92em; }

/* One focus treatment for everything that is not an .input (which draws its
   own, below). Keyboard users get a ring; mouse users never see it. */
.btn:focus-visible,
.tile:focus-visible,
.tab:focus-visible,
summary:focus-visible {
  outline: 2px solid var(--primary);
  outline-offset: 2px;
}

/* ── Icons ─────────────────────────────────────────────────────────────── */
/* One inline sprite, referenced with <use>. The line icons inherit colour;
   the ai-* symbols carry their own gradient and ignore currentColor. */

.ic {
  width: 20px; height: 20px; flex: 0 0 auto;
  fill: none; stroke: currentColor; stroke-width: 1.6;
  stroke-linecap: round; stroke-linejoin: round;
}
.ic-sm { width: 16px; height: 16px; }
.ic-lg { width: 26px; height: 26px; }
.ic-xl { width: 40px; height: 40px; }

/* ── Shell ─────────────────────────────────────────────────────────────── */

.shell { display: flex; min-height: 100vh; }

.sidebar {
  width: var(--sidebar-w); flex: 0 0 var(--sidebar-w);
  background: linear-gradient(180deg, var(--navy) 0%, var(--navy-deep) 100%);
  color: #fff;
  display: flex; flex-direction: column;
  position: sticky; top: 0; height: 100vh;
  padding: 14px 12px 0;
}

/* CSUL puts the company in a white rounded card at the top of the navy rail. */
.brand-card {
  display: flex; align-items: center; gap: 10px;
  background: #fff; border-radius: var(--radius);
  padding: 12px 14px; margin-bottom: 14px;
  text-decoration: none; color: var(--navy);
  box-shadow: 0 2px 6px rgba(0, 0, 0, .12);
}
.brand-mark { width: 30px; height: 30px; flex: 0 0 auto; }
.brand-name { font-weight: 700; font-size: var(--fs-lg); line-height: 1.2; }

.side-user {
  display: flex; align-items: center; gap: 10px;
  padding: 0 4px 14px; margin-bottom: 6px;
  border-bottom: 1px solid rgba(255, 255, 255, .12);
}
.side-user-id { min-width: 0; flex: 1; }
.side-user-name {
  display: block; font-size: var(--fs-base); font-weight: 700; color: #fff;
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  font-family: ui-monospace, Consolas, monospace;
}
.side-user-role { display: block; font-size: var(--fs-xs); color: rgba(255, 255, 255, .6); }
.side-user-out { margin: 0; }

.avatar {
  width: 34px; height: 34px; border-radius: 50%;
  background: rgba(255, 255, 255, .14); color: #fff;
  border: 1px solid rgba(255, 255, 255, .25);
  font-size: 12px; font-weight: 700;
  display: flex; align-items: center; justify-content: center;
  flex-shrink: 0;
}

.icon-btn {
  background: none; border: none; padding: 6px; cursor: pointer;
  color: rgba(255, 255, 255, .7); border-radius: var(--radius-sm);
  display: flex; align-items: center;
}
.icon-btn:hover { color: #fff; background: rgba(255, 255, 255, .12); }

.side-nav { flex: 1; overflow-y: auto; padding: 6px 0 16px; }
.side-nav-title {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .09em;
  color: rgba(255, 255, 255, .42); padding: 14px 12px 6px; font-weight: 700;
}

.side-link {
  position: relative;
  display: flex; align-items: center; gap: 11px;
  padding: 10px 12px; margin-bottom: 3px;
  border-radius: var(--radius-sm);
  color: rgba(255, 255, 255, .88); text-decoration: none;
  font-size: var(--fs-md); font-weight: 500;
}
.side-link:hover { background: rgba(255, 255, 255, .10); color: #fff; }
.side-link.active { background: rgba(255, 255, 255, .16); color: #fff; font-weight: 700; }
/* The active rail marker, as CSUL draws it. */
.side-link.active::before {
  content: ''; position: absolute; left: -12px; top: 50%; transform: translateY(-50%);
  width: 3px; height: 22px; border-radius: 0 3px 3px 0; background: var(--logo-cyan);
}
/* The label takes the slack; the count must not — it is a span too, so it needs
   excluding explicitly or it stretches across the row. */
.side-link > span:not(.count) { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.side-link .count {
  flex: 0 0 auto;
  background: var(--logo-sky); color: #fff;
  font-size: var(--fs-xs); font-weight: 700; padding: 1px 7px; border-radius: 20px;
  font-variant-numeric: tabular-nums;
}

.side-foot {
  border-top: 1px solid rgba(255, 255, 255, .12);
  padding: 10px 12px 12px; font-size: var(--fs-xs);
  color: rgba(255, 255, 255, .55);
  display: flex; flex-direction: column; gap: 2px;
}
.side-foot-dim { color: rgba(255, 255, 255, .38); }

.shell-main { flex: 1; min-width: 0; display: flex; flex-direction: column; }

.appbar {
  background: #fff; border-bottom: 1px solid var(--border);
  padding: 0 26px; min-height: 62px;
  display: flex; align-items: center; justify-content: space-between; gap: 16px;
  position: sticky; top: 0; z-index: 20;
}
.appbar-title {
  font-size: var(--fs-lg); font-weight: 700; color: var(--navy); margin: 0;
  display: flex; align-items: baseline; gap: 8px; min-width: 0;
}
/* The count that used to live in the toolbar's duplicate <h3> — "Staff (2)". */
.appbar-count {
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-muted);
  font-variant-numeric: tabular-nums; white-space: nowrap;
}
.appbar-right { display: flex; align-items: center; gap: 14px; }
.appbar-meta {
  display: flex; align-items: center; gap: 6px;
  font-size: var(--fs-sm); color: var(--text-secondary); white-space: nowrap;
}
.appbar-sep { width: 1px; height: 18px; background: var(--border); }

.dash-body { padding: 24px 26px 48px; max-width: 1280px; width: 100%; }
.dash-toolbar {
  display: flex; justify-content: space-between; align-items: center;
  margin-bottom: 16px; gap: 12px; flex-wrap: wrap;
}
.dash-toolbar h3 { margin: 0; }
/* A toolbar left with nothing but its buttons, now the duplicate heading is
   gone. `space-between` would park a lone button on the left. */
.dash-toolbar-end { justify-content: flex-end; }

.impersonation-banner {
  background: var(--warning-bg); color: var(--warning-text);
  border-bottom: 1px solid var(--warning-border);
  padding: 8px 24px; font-size: var(--fs-base); font-weight: 600;
  display: flex; justify-content: space-between; align-items: center; gap: 12px;
}

/* ── Cards ─────────────────────────────────────────────────────────────── */
/* One shell with three optional parts. A plain `.card` with content straight
   inside still works exactly as before; a card that wants a titled header opts
   in with `.card-framed` and moves its padding to the parts. */

.card {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); box-shadow: var(--shadow-card);
  padding: var(--pad-card); margin-bottom: 16px;
}
.card > h3, .card > h4 { margin-bottom: 10px; }

.card-framed { padding: 0; }
.card-framed > .card-head { padding: 14px 20px; }
.card-framed > .card-body { padding: 18px 20px; }
.card-framed > .card-foot { padding: 12px 20px; }

.card-head {
  display: flex; align-items: center; gap: 10px;
  border-bottom: 1px solid var(--border);
}
.card-head > .ic { color: var(--primary); }
.card-title { font-size: var(--fs-md); font-weight: 700; color: var(--navy); margin: 0; }
.card-sub { font-size: var(--fs-xs); color: var(--text-muted); margin: 1px 0 0; }
.card-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }
.card-body > :last-child { margin-bottom: 0; }
.card-foot {
  border-top: 1px solid var(--border); background: #FAFBFD;
  border-radius: 0 0 var(--radius) var(--radius);
}

/* The body IS a table, which draws its own edges. */
.card-flush { padding: 0; }
.card-flush > .card-head { padding: 14px 20px; }
/* A titled block above a flush table, where a bordered .card-head would double
   up with the table's own top rule. */
.card-head-block { padding: 18px 20px 0; }

/* A closing note under a form, separated by a hairline rather than a gap. */
.note-top { border-top: 1px solid var(--border); padding-top: 12px; margin-top: 4px; }

/* State accents — these replace per-view inline border-color overrides. */
.card-ok { border-color: var(--success-border); }
.card-warn { border-color: var(--warning-border); }
.card-danger { border-color: var(--danger-border); }
/* A card whose subject is the automated pass, marked by a hairline of accent
   colour down its left edge. */
.card-accent-ai { border-left: 3px solid var(--logo-sky); }

.side-card {
  background: #F9FAFB; border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px; margin-bottom: 16px;
}
.side-card h4 {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-muted); margin: 0 0 10px;
}
.side-card .row {
  display: flex; justify-content: space-between; gap: 12px;
  font-size: var(--fs-sm); padding: 5px 0;
}
.side-card .row span:last-child {
  text-align: right; font-weight: 500; font-variant-numeric: tabular-nums;
}

/* ── Collapsible cards ─────────────────────────────────────────────────── */
/* Native <details>/<summary>: no JS for the open/close itself, and the keyboard
   and screen-reader behaviour is correct for free. public/js/collapse.js only
   adds persistence and the print fix. */

.card-collapse > summary.card-head { cursor: pointer; user-select: none; list-style: none; }
.card-collapse > summary.card-head::-webkit-details-marker { display: none; }
.card-collapse:not([open]) > summary.card-head { border-bottom: none; }
.card-chevron { transition: transform .15s ease; color: var(--text-muted); margin-left: 2px; }
.card-collapse[open] > summary .card-chevron { transform: rotate(180deg); }
@media (prefers-reduced-motion: reduce) { .card-chevron { transition: none; } }
@media print {
  .card-chevron { display: none; }
  .card-collapse > summary.card-head { cursor: auto; }
}

/* ── Buttons ───────────────────────────────────────────────────────────── */

.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: 6px;
  padding: 9px 16px; border-radius: var(--radius-sm);
  font-size: var(--fs-base); font-weight: 500; font-family: inherit;
  border: 1px solid transparent; cursor: pointer; text-decoration: none;
}
.btn-primary { background: var(--primary); color: #fff; }
.btn-primary:hover { background: var(--primary-hover); }
.btn-secondary { background: #fff; color: var(--primary); border-color: var(--border); }
.btn-secondary:hover { background: #F0F4FA; border-color: var(--info-border); }
.btn-danger { background: #fff; color: var(--danger-text); border-color: var(--danger-border); }
.btn-danger:hover { background: var(--danger-bg); }
.btn-block { width: 100%; }
.btn-sm { padding: 7px 12px; font-size: var(--fs-sm); }
.btn:disabled, .btn[aria-disabled='true'] { opacity: .5; cursor: not-allowed; }
/* Set by public/js/loading.js while the form it submits is in flight. */
.btn.is-busy { pointer-events: none; opacity: .8; }

/* ── Forms ─────────────────────────────────────────────────────────────── */

.input, select.input, textarea.input {
  width: 100%; padding: 10px 12px;
  border: 1px solid var(--border); border-radius: var(--radius-sm);
  font-size: var(--fs-base); font-family: inherit; color: var(--text); background: #fff;
}
.input:focus {
  outline: 2px solid var(--primary); outline-offset: -1px;
  border-color: var(--primary); box-shadow: 0 0 0 3px var(--focus-ring);
}
.input[aria-invalid='true'] { border-color: var(--danger-border); background: var(--danger-bg); }
textarea.input { min-height: 90px; resize: vertical; }

.label {
  font-size: var(--fs-sm); font-weight: 600; color: var(--text-secondary);
  margin-bottom: 6px; display: block;
}
.field { margin-bottom: 14px; }
.field-hint { font-size: var(--fs-xs); color: var(--text-muted); margin: 4px 0 0; }

.form-narrow { max-width: 480px; }
.form-wide { max-width: 720px; }
.form-row { display: grid; grid-template-columns: 1fr 1fr; gap: 16px; }
.form-actions { display: flex; gap: 8px; margin-top: 20px; }

fieldset {
  border: 1px solid var(--border); border-radius: var(--radius);
  padding: 16px 20px; margin: 0 0 16px;
}
legend { font-size: var(--fs-xs); font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text-muted); padding: 0 6px; }

/* ── Badges ────────────────────────────────────────────────────────────── */

/* CSUL's chips are square-cornered rectangles, not lozenges — "Non-DG", "FCL",
   "20SD,40RC", "Opened", "BRANCH OFFICE 13" are all 4px-radius blocks of solid
   colour. The 20px pill this used to be was the single biggest thing making the
   portal look like a different product. */
.badge {
  font-size: var(--fs-2xs); font-weight: 700; padding: 4px 9px;
  letter-spacing: .03em; line-height: 1.25;
  border-radius: 4px; display: inline-block; white-space: nowrap;
  border: 1px solid transparent;
}
.badge-success { background: #C9E9D9; color: #0B6247; }
.badge-warning { background: #FBE3C2; color: #8A5209; }
.badge-danger  { background: #F8D3D3; color: #962626; }
.badge-info    { background: #CFDDF5; color: #12448F; }
.badge-neutral { background: #E3E7EE; color: #4A5768; }
/* CSUL's outlined variant — used where the chip is a label, not a state. */
.badge-outline { background: transparent; color: var(--text-secondary); border-color: var(--border-strong); }
/* And its solid navy one, for the primary state on a row. */
.badge-solid   { background: var(--navy); color: #fff; }

/* ── Tables ────────────────────────────────────────────────────────────── */

.table-wrap { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; font-size: var(--fs-base); }
th {
  text-align: left; color: var(--text-muted); font-weight: 700;
  font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .05em;
  padding: 11px 14px; border-bottom: 1px solid var(--border); white-space: nowrap;
}
td { padding: var(--pad-cell); border-bottom: 1px solid var(--border); color: var(--text); }
tr:last-child td { border-bottom: none; }
tbody tr:hover { background: #FAFBFC; }
td.num, th.num { text-align: right; font-variant-numeric: tabular-nums; }
.mono { font-family: ui-monospace, Consolas, monospace; font-variant-numeric: tabular-nums; }

.empty-state { padding: 48px 24px; text-align: center; color: var(--text-muted); }
.empty-state h3 { color: var(--text-secondary); }

/* ── Flash messages ────────────────────────────────────────────────────── */

.flash {
  padding: 12px 16px; border-radius: var(--radius); margin-bottom: 16px;
  font-size: var(--fs-base); border: 1px solid;
  background: var(--info-bg); color: var(--info-text); border-color: var(--info-border);
}
.flash-success { background: var(--success-bg); color: var(--success-text); border-color: var(--success-border); }
.flash-warn    { background: var(--warning-bg); color: var(--warning-text); border-color: var(--warning-border); }
.flash-error   { background: var(--danger-bg);  color: var(--danger-text);  border-color: var(--danger-border); }
.flash strong { display: block; margin-bottom: 2px; }

/* ── Login ─────────────────────────────────────────────────────────────── */
/*
   CSUL's sign-in is a white card floating over a photograph of a container
   terminal under a blue wash. We do not have their photograph and will not
   pass off a stock one as theirs, so the backdrop is built: a deep blue wash
   over an abstract container-stack pattern, drawn in CSS. Same weight, same
   colour temperature, nothing borrowed that is not ours to borrow.
*/

.login-canvas {
  position: relative; min-height: 100vh;
  display: flex; align-items: center; justify-content: center; padding: 40px 20px;
  background:
    linear-gradient(135deg, rgba(0, 42, 102, .94) 0%, rgba(6, 60, 130, .88) 45%, rgba(0, 27, 68, .96) 100%),
    repeating-linear-gradient(90deg, #063C82 0 38px, #04336F 38px 76px),
    #002A66;
  overflow: hidden;
}
/* The container stacks: two rows of blocks, low contrast, behind the wash. */
.login-canvas::before,
.login-canvas::after {
  content: ''; position: absolute; left: -5%; right: -5%; pointer-events: none;
  background-image:
    linear-gradient(90deg, rgba(255,255,255,.07) 0 54px, transparent 54px 62px);
  background-size: 62px 100%;
}
.login-canvas::before { bottom: 0; height: 26vh; opacity: .55; }
.login-canvas::after  { bottom: 26vh; height: 13vh; opacity: .3; transform: translateX(24px); }

.login-card {
  position: relative; z-index: 1;
  background: #fff; border-radius: 14px; padding: 38px 34px;
  width: 100%; max-width: 400px;
  box-shadow: 0 18px 50px rgba(6, 24, 48, .35);
}
.login-logo {
  width: 56px; height: 56px; margin-bottom: 18px;
  background: url('/img/csul-mark.svg') no-repeat center / contain;
}
.login-card h3 {
  font-size: var(--fs-xl); margin: 0 0 4px; letter-spacing: .04em;
  text-transform: uppercase; font-weight: 700;
}
.login-card .sub { font-size: var(--fs-base); color: var(--text-secondary); margin: 0 0 24px; }
.tenant-tag {
  display: inline-flex; align-items: center; gap: 6px;
  background: var(--info-bg); padding: 6px 10px; border-radius: var(--radius-sm);
  font-size: var(--fs-sm); color: var(--primary); font-weight: 500; margin-bottom: 18px;
}
.login-foot { text-align: center; font-size: var(--fs-sm); color: var(--text-secondary); margin-top: 14px; }

/* Underline inputs with a leading icon, the way CSUL draws its login. */
.field-icon { position: relative; display: flex; align-items: center; gap: 10px; }
.field-icon > .ic { color: var(--primary); margin-bottom: 2px; }
.field-icon .input {
  border: none; border-bottom: 2px solid var(--border);
  border-radius: 0; padding: 8px 2px; background: transparent;
}
.field-icon .input:focus { outline: none; border-bottom-color: var(--primary); }
.divider { display: flex; align-items: center; gap: 10px; margin: 18px 0; font-size: var(--fs-sm); color: var(--text-muted); }
.divider::before, .divider::after { content: ''; flex: 1; height: 1px; background: var(--border); }

/* ── Stepper (case status tracker) ─────────────────────────────────────── */

.stepper { display: flex; margin-bottom: 28px; }
.step { flex: 1; display: flex; flex-direction: column; align-items: center; position: relative; }
.step .dot {
  width: 26px; height: 26px; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  font-size: var(--fs-xs); font-weight: 700; z-index: 1;
  background: #EEF0F3; color: var(--text-muted);
}
.step .lbl { font-size: var(--fs-xs); margin-top: 6px; color: var(--text-muted); text-align: center; }
.step.done .dot { background: var(--teal); color: #fff; }
.step.done .lbl { color: var(--text-secondary); }
.step.current .dot { background: var(--warning-bg); border: 2px solid var(--warning-text); color: var(--warning-text); }
.step.current .lbl { color: var(--text); font-weight: 600; }
.step.failed .dot { background: var(--danger-bg); border: 2px solid var(--danger-text); color: var(--danger-text); }
.step-line { position: absolute; top: 13px; left: -50%; width: 100%; height: 2px; background: var(--border); z-index: 0; }
.step.done .step-line { background: var(--teal); }
.step:first-child .step-line { display: none; }

/* ── Checklist rows ────────────────────────────────────────────────────── */

/* ⚠️ `display: block`, NOT flex. `.ci-row` inside does the row layout, and as a
   flex ITEM it shrank to its content — so its own justify-content had no free
   space and every action button sat jammed against the label instead of at the
   right edge. Only the stacked (typed-field) rows looked right, because those
   were already block. One display mode for both, so they cannot diverge again. */
.checklist-item {
  display: block;
  padding: 16px 0; border-bottom: 1px solid var(--border);
}
.checklist-item:last-child { border-bottom: none; }
.ci-left { display: flex; align-items: center; gap: 12px; min-width: 0; flex: 1; }
.ci-icon {
  width: 36px; height: 36px; border-radius: 9px;
  display: flex; align-items: center; justify-content: center; flex-shrink: 0;
  background: #EEF1F6; color: var(--text-muted);
  border: 1px solid transparent;
}
.ci-icon .ic { width: 19px; height: 19px; stroke-width: 2.1; }
/* State reads off colour AND shape, not a coloured square alone. */
.ci-icon-done { background: var(--success-bg); color: var(--success-text); border-color: var(--success-border); }
.ci-icon-warn { background: var(--warning-bg); color: var(--warning-text); border-color: var(--warning-border); }
.ci-icon-todo { background: #EEF1F6; color: var(--text-secondary); border-color: var(--border); }
/* Used on the finalize sheet, where the mark is a plain glyph. */
.ci-icon-ok { background: var(--success-bg); color: var(--success-text); font-weight: 700; }
.ci-icon-bad { background: var(--danger-bg); color: var(--danger-text); font-weight: 700; }
.ci-title { font-size: var(--fs-base); font-weight: 600; margin: 0; }
.ci-tag { font-size: var(--fs-xs); color: var(--text-muted); margin: 2px 0 0; }
.ci-tag.ok { color: var(--success-text); }
/* The actions column: always hard right, never wrapping under the label. */
.ci-actions { display: flex; gap: 6px; align-items: center; flex: 0 0 auto; margin-left: auto; }
@media (max-width: 640px) {
  .ci-row { flex-wrap: wrap; }
  .ci-actions { margin-left: 44px; }
}

/* ── Review detail ─────────────────────────────────────────────────────── */

.review-layout { display: grid; grid-template-columns: 1fr 300px; gap: 24px; align-items: start; }
.doc-card { border: 1px solid var(--border); border-radius: var(--radius); padding: 14px 16px; margin-bottom: 12px; }
.doc-card.flagged { border-color: var(--warning-border); background: var(--warning-bg); }
.doc-card.blocked { border-color: var(--danger-border); background: var(--danger-bg); }
.doc-card-head { display: flex; justify-content: space-between; align-items: center; gap: 12px; margin-bottom: 10px; }
.doc-card-head .name { font-size: var(--fs-base); font-weight: 600; }
.field-grid { display: grid; grid-template-columns: 1fr 1fr; gap: 10px 18px; font-size: var(--fs-base); }
.field-grid .k { color: var(--text-muted); font-size: var(--fs-xs); font-weight: 600; }
.field-grid .v { color: var(--text); font-weight: 600; }
.field-grid .v.mismatch { color: var(--danger-text); }
.field-grid .full { grid-column: 1 / -1; }
/* Two widths of auto-fit grid, replacing the inline grid-template-columns that
   every one of these call sites used to carry. */
.field-grid-auto { grid-template-columns: repeat(auto-fit, minmax(200px, 1fr)); gap: 10px 20px; }
.field-grid-auto-lg { grid-template-columns: repeat(auto-fit, minmax(220px, 1fr)); gap: 12px 20px; }
.ai-note { font-size: var(--fs-sm); color: var(--warning-text); margin: 10px 0 0; }

/* ── Row cards ─────────────────────────────────────────────────────────── */
/* CSUL does not render its Sea Import list as a table. Each shipment is a card:
   an identity block on the left with its status chips, and a grid of small
   grey-label / dark-value pairs beside it. Copied, because a list of cases is
   the same shape of thing and the table read as a different product entirely.

   Still a list of links, not a grid — the whole card is not clickable, because a
   card with six links inside it and one on the outside is a guessing game. */

.rowcards { display: flex; flex-direction: column; gap: 12px; }
.rowcard {
  background: var(--card); border: 1px solid var(--border);
  border-radius: var(--radius); padding: 16px 18px;
  display: grid; grid-template-columns: minmax(200px, 260px) 1fr auto;
  gap: 16px 22px; align-items: start;
  transition: border-color .15s ease, box-shadow .15s ease;
}
.rowcard:hover { border-color: var(--border-strong); box-shadow: var(--shadow-card); }
/* A case needing the agent's attention earns an edge, the way CSUL marks a hold. */
.rowcard-attn { border-left: 3px solid var(--warning-text); }
.rowcard-done { border-left: 3px solid var(--success-text); }

.rowcard-id { min-width: 0; }
.rowcard-id .no {
  font-size: var(--fs-lg); font-weight: 700; color: var(--navy);
  text-decoration: none; font-variant-numeric: tabular-nums;
}
.rowcard-id .no:hover { text-decoration: underline; }
.rowcard-id .sub {
  display: block; font-size: var(--fs-base); font-weight: 500; color: var(--text-secondary);
  margin-top: 2px; overflow-wrap: anywhere;
}
.rowcard-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 9px; }

/* The label-over-value grid CSUL uses for Job # / HBL # / MBL # / File #. */
.rowcard-grid {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 12px 20px; min-width: 0;
}
.kv { min-width: 0; }
.kv .k {
  display: block; font-size: var(--fs-xs); font-weight: 600;
  text-transform: uppercase; letter-spacing: .06em; color: var(--text-muted);
  margin-bottom: 2px;
}
.kv .v { display: block; font-size: var(--fs-base); font-weight: 600; color: var(--text); overflow-wrap: anywhere; }
.kv .v.mono { font-family: ui-monospace, Consolas, monospace; }
.kv .v.link { color: var(--primary); }

.rowcard-act { display: flex; flex-direction: column; gap: 6px; align-items: stretch; }

@media (max-width: 860px) {
  .rowcard { grid-template-columns: 1fr; }
  .rowcard-act { flex-direction: row; }
}

/* ── Toolbar controls ──────────────────────────────────────────────────── */
/* CSUL pairs a rounded field with a solid circular button. */

.search-round { display: flex; align-items: center; gap: 8px; }
.search-round .input { border-radius: 22px; padding-left: 16px; }
.icon-round {
  width: 40px; height: 40px; flex: 0 0 auto; border-radius: 50%;
  background: var(--primary); color: #fff; border: 0; cursor: pointer;
  display: inline-flex; align-items: center; justify-content: center;
}
.icon-round:hover { background: var(--primary-hover); }

/* ── Tabs ──────────────────────────────────────────────────────────────── */
/* CSUL's shape: a horizontal row on a hairline, the active item underlined in
   the brand blue. Replaces the pill buttons that used to fake this. */

.tabs {
  display: flex; align-items: flex-end; gap: 2px;
  border-bottom: 1px solid var(--border);
  margin-bottom: 18px; overflow-x: auto; scrollbar-width: thin;
  /* 🔴 overflow-y is NOT decorative here. Per CSS overflow, `visible` on one
     axis computes to `auto` when the other axis is not `visible` — so
     overflow-x:auto alone gave the strip its own VERTICAL scrollbar, parked
     against the right edge of every tabbed page. Hidden, not auto: the strip is
     one row and has nothing to scroll down to. */
  overflow-y: hidden;
}
.tab {
  display: inline-flex; align-items: center; gap: 7px;
  padding: 12px 18px; margin-bottom: -1px;
  font-size: var(--fs-base); font-weight: 600; font-family: inherit;
  color: var(--text-secondary); text-decoration: none; white-space: nowrap;
  background: none; border: 0; border-bottom: 3px solid transparent; cursor: pointer;
}
.tab:hover { color: var(--navy); background: #F3F6FB; }
.tab.active { color: var(--navy); font-weight: 700; border-bottom-color: var(--primary); }
.tab-count {
  font-size: var(--fs-2xs); font-weight: 700; padding: 1px 7px; border-radius: 20px;
  background: #EDF0F4; color: var(--text-secondary); font-variant-numeric: tabular-nums;
}
.tab.active .tab-count { background: var(--info-bg); color: var(--info-text); }
.tab-count-warn { background: var(--warning-bg); color: var(--warning-text); }
.tab-count-danger { background: var(--danger-bg); color: var(--danger-text); }
/* A severity tone outranks "this tab is active" — the count says how bad, the
   underline says where you are, and the two must not fight. Written long-hand
   because `.tab.active .tab-count` is the more specific selector. */
.tab.active .tab-count-warn { background: var(--warning-bg); color: var(--warning-text); }
.tab.active .tab-count-danger { background: var(--danger-bg); color: var(--danger-text); }
.tabs-right { margin-left: auto; }

/* An icon per tab, muted until the tab is the one you are on. The ai-* symbols
   carry their own gradient and ignore this, which is the point — the AI tab
   stays recognisably the AI tab. */
.tab > .ic { color: var(--text-muted); }
.tab.active > .ic { color: var(--primary); }
.tab .spinner { width: 12px; height: 12px; border-width: 2px; color: var(--primary); }

/* ── Tab panels ────────────────────────────────────────────────────────── */
/* The tabs are real links to ?tab=<slug> and every panel is rendered by the
   server with the inactive ones carrying `hidden`, so a click works with
   JavaScript blocked. public/js/tabs.js only removes the round trip.

   ⚠️ Panels are HIDDEN, never detached. #screening-panel is polled while it is
   off screen, and an <iframe> that leaves the document stops loading. */
.tabpanel[hidden] { display: none; }
.tabpanel > :last-child { margin-bottom: 0; }
.tabpanel:focus { outline: none; }

@media print {
  /* Paper has no tabs. Print the lot, the same decision collapse.js makes for
     a closed <details>. */
  .tabs { display: none; }
  .tabpanel[hidden] { display: block !important; }
}

/* ── Scrolling card bodies ─────────────────────────────────────────────── */
/* Applied to the bodies that hold a LIST which grows without bound — the
   checklist and the timeline. Deliberately not applied to short cards: a
   scrollbar on a five-line shipment strip reads as a rendering bug, and a cap
   it never reaches would be dead CSS.

   The findings list is capped too. Leaving it uncapped was argued on the
   grounds that a BLOCKER must never sit below a fold — but a list long enough
   to need a cap is long enough to push the PAGE past one screen, so the blocker
   is below a fold either way and only the containment is lost. What actually
   answers the objection is the count in the card head and on the tab: seeing
   three findings under a badge reading "7 to fix" is what tells someone to keep
   scrolling. `.card-scroll-lg` gives it more room, because a flag with an
   expected/found pair is four times the height of a checklist row. */
.card-scroll {
  max-height: var(--card-scroll-max, 460px);
  overflow-y: auto;
  /* ⚠️ Explicit, not inherited. Per CSS overflow, `visible` on one axis computes
     to `auto` when the other is not `visible` — the same rule that gave .tabs a
     stray vertical scrollbar. Without this, every capped card would grow a
     horizontal bar it has nothing to scroll to. */
  overflow-x: hidden;
  /* A trackpad flick that reaches the end of the list must not then start
     scrolling the page underneath it. */
  overscroll-behavior: contain;
  scrollbar-width: thin;
  scrollbar-color: var(--border-strong) transparent;
}
/* Flags are tall — message, an expected/found pair, and an Open-document row.
   The list-card cap would show barely two. */
.card-scroll-lg { --card-scroll-max: 620px; }
/* When the scroller is a wrapper INSIDE a padded body rather than the body
   itself, the bar would otherwise sit hard against the flag cards' right edge. */
.card-body > .card-scroll { padding-right: 6px; }
.card-scroll::-webkit-scrollbar { width: 10px; }
.card-scroll::-webkit-scrollbar-track { background: transparent; }
.card-scroll::-webkit-scrollbar-thumb {
  background: var(--border-strong); border-radius: 20px;
  border: 3px solid var(--card);
}
.card-scroll::-webkit-scrollbar-thumb:hover { background: var(--text-muted); }

@media print {
  /* Paper has no scrollbars — print the whole list, the same call collapse.js
     makes when it expands every <details>. */
  .card-scroll { max-height: none; overflow: visible; }
}

/* ── Timeline ──────────────────────────────────────────────────────────── */
/* A rail with a dot per event, newest first. It earned the room when the case
   history moved out of the 300px sidebar and into a tab of its own. */

.timeline { list-style: none; margin: 0; padding: 0; }
.tl-item { position: relative; padding: 0 0 18px 28px; }
.tl-item::before {
  content: ''; position: absolute; left: 5px; top: 16px; bottom: 0;
  width: 2px; background: var(--border);
}
.tl-item:last-child { padding-bottom: 0; }
.tl-item:last-child::before { display: none; }
.tl-dot {
  position: absolute; left: 0; top: 5px; width: 12px; height: 12px;
  border-radius: 50%; background: var(--card);
  border: 2px solid var(--border-strong);
}
.tl-latest > .tl-dot {
  background: var(--primary); border-color: var(--primary);
  box-shadow: 0 0 0 3px var(--focus-ring);
}
.tl-title { font-size: var(--fs-base); font-weight: 600; color: var(--text); }
.tl-action {
  font-size: var(--fs-2xs); font-weight: 700; text-transform: uppercase;
  letter-spacing: .05em; color: var(--text-muted); margin-left: 6px;
}
.tl-meta { font-size: var(--fs-xs); color: var(--text-muted); margin-top: 1px; }
.tl-note { font-size: var(--fs-sm); color: var(--text-secondary); margin-top: 4px; }

/* ── Waiting ───────────────────────────────────────────────────────────── */

@keyframes dop-spin { to { transform: rotate(360deg); } }
.spinner {
  display: inline-block; flex: 0 0 auto;
  width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid currentColor; border-right-color: transparent;
  animation: dop-spin .6s linear infinite;
}
.spinner-lg { width: 26px; height: 26px; border-width: 3px; }

.page-loader {
  position: fixed; inset: 0; z-index: 90;
  display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 12px;
  background: rgba(238, 241, 245, .82); backdrop-filter: blur(2px);
  color: var(--navy); font-size: var(--fs-base); font-weight: 500; text-align: center;
  padding: 24px;
}
.page-loader[hidden] { display: none; }

/* A spinner that cannot spin still has to say "working". */
@media (prefers-reduced-motion: reduce) {
  .spinner { animation: none; border-right-color: currentColor; opacity: .45; }
  .page-loader { backdrop-filter: none; }
}
@media print { .page-loader, .spinner { display: none !important; } }

/* ── Document viewer modal ─────────────────────────────────────────────── */
/* The iframe is same-origin but sandboxed without allow-scripts, and the bytes
   arrive under their own narrow CSP — see routes/documents.routes.js. */

.modal { position: fixed; inset: 0; z-index: 100; display: flex; }
.modal[hidden] { display: none; }
.modal-backdrop { position: absolute; inset: 0; background: rgba(0, 27, 68, .55); }
.modal-panel {
  position: relative; margin: auto; z-index: 1;
  background: var(--card); border-radius: var(--radius-lg);
  box-shadow: 0 24px 60px rgba(0, 27, 68, .4);
  width: min(1080px, 94vw); max-height: 92vh;
  display: flex; flex-direction: column; overflow: hidden;
}
.modal-head {
  display: flex; align-items: center; gap: 10px;
  padding: 12px 16px; border-bottom: 1px solid var(--border);
}
.modal-head .name {
  font-size: var(--fs-md); font-weight: 500; color: var(--navy);
  min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.modal-body {
  position: relative;
  flex: 1; min-height: 0; background: #F4F6F9;
  display: flex; align-items: center; justify-content: center;
}
.modal-frame { width: 100%; height: 78vh; border: 0; background: #fff; display: block; }
.modal-img { max-width: 100%; max-height: 78vh; display: block; }
/* Overlaid, not a sibling in the flex row: the viewer has to be in the document
   (and taking up its space) to load at all, so the spinner sits on top of it
   rather than standing in for it. */
.modal-wait {
  position: absolute; inset: 0; z-index: 1;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 10px; color: var(--text-secondary); background: #F4F6F9;
}
.modal-fallback { padding: 28px; max-width: 560px; }
.modal-fallback .flash { margin-bottom: 0; }
.is-modal-open { overflow: hidden; }
@media print { .modal { display: none !important; } }

/* ── Utilities ─────────────────────────────────────────────────────────── */

/* Visible to a screen reader, to nobody else. The checklist state icons carry
   their meaning in colour and shape, which is no meaning at all when read aloud. */
.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;
}

.muted { color: var(--text-muted); }
.secondary { color: var(--text-secondary); }
.small { font-size: var(--fs-sm); }
.nowrap { white-space: nowrap; }
.stack > * + * { margin-top: 12px; }
.row-flex { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
.spacer { flex: 1; }
.grow { flex: 1; }
.min-0 { min-width: 0; }
.text-danger { color: var(--danger-text); }
.text-warning { color: var(--warning-text); }
.text-success { color: var(--success-text); }
.text-center { text-align: center; }
.self-center { align-self: center; }
.strong { font-weight: 600; }
/* An inline form that must not break the row it sits in. */
.form-inline { display: inline; margin: 0; }
.form-flush { margin: 0; }
.summary-btn { list-style: none; cursor: pointer; }
.summary-btn::-webkit-details-marker { display: none; }
.details-body { padding: 12px 0 4px; }
.badge-link { text-decoration: none; margin-right: 4px; }
.check-label { display: flex; gap: 8px; align-items: flex-start; margin-bottom: 6px; }
.pager { display: flex; gap: 8px; align-items: center; justify-content: center; margin-top: 16px; }
.pre-wrap { white-space: pre-wrap; }
.timeline-row { padding: 6px 0; border-bottom: 1px solid var(--border); }
.timeline-row:last-child { border-bottom: none; }
.mt-0 { margin-top: 0; } .mb-0 { margin-bottom: 0; } .mt-16 { margin-top: 16px; } .mb-24 { margin-bottom: 24px; }
.mt-2 { margin-top: 2px; } .mt-6 { margin-top: 6px; } .mt-8 { margin-top: 8px; }
.mt-10 { margin-top: 10px; } .mt-12 { margin-top: 12px; } .mt-14 { margin-top: 14px; }
.mt-48 { margin-top: 48px; }
.mb-4 { margin-bottom: 4px; } .mb-6 { margin-bottom: 6px; }
.mb-8 { margin-bottom: 8px; } .mb-10 { margin-bottom: 10px; }
.mb-12 { margin-bottom: 12px; } .mb-16 { margin-bottom: 16px; }
.ml-6 { margin-left: 6px; }
.dash-body-narrow { max-width: 640px; }

/* ── KPI tiles ─────────────────────────────────────────────────────────── */
/* CSUL fills its count tiles with a soft colour and prints the figure in the
   darker version of it — the colour is the whole panel, not a stripe on its
   edge. Copied, because these sit in the same eyeline as CSUL's own.
   The colour is the tile's IDENTITY and does not change with the count: a tile
   that went amber at zero would read as a bug. A zero mutes its figure instead,
   because nothing waiting should not shout. */

.tile-grid { display: grid; grid-template-columns: repeat(auto-fit, minmax(190px, 1fr)); gap: 14px; margin-bottom: 22px; }
.tile {
  background: var(--tile-bg, var(--card));
  border: 1px solid var(--tile-border, var(--border));
  border-radius: var(--radius); padding: 18px 20px;
  text-decoration: none; display: block;
  color: var(--tile-fg, var(--navy));
  transition: filter .15s ease, transform .15s ease, box-shadow .15s ease;
}
.tile:hover { filter: brightness(.97); box-shadow: var(--shadow-card); transform: translateY(-1px); }
.tile .n {
  font-size: var(--fs-num); font-weight: 700; line-height: 1.1;
  color: inherit; font-variant-numeric: tabular-nums;
}
/* CSUL writes "930 Shipment / Liner Agency" — the label is nearly as large as
   the figure, sentence case, in the tile's own colour. Not a muted uppercase
   caption. That contrast is most of why its tiles read as substantial. */
.tile .l {
  font-size: var(--fs-md); font-weight: 600; color: inherit; opacity: .82;
  letter-spacing: 0; text-transform: none; margin-top: 1px;
}
/* Two classes deliberately: this has to beat .tile-action & co. whatever order
   they end up in below. */
.tile.tile-zero { --tile-bg: #EFF1F5; --tile-border: #DFE3EA; --tile-fg: #6B7789; }
@media (prefers-reduced-motion: reduce) {
  .tile { transition: none; }
  .tile:hover { transform: none; }
}

.tile-action        { --tile-bg: #FBE3C2; --tile-border: #F0CE9B; --tile-fg: #8A5209; }
.tile-review        { --tile-bg: #CFDDF5; --tile-border: #B2C8EC; --tile-fg: #12448F; }
.tile-draft         { --tile-bg: #DBE0E8; --tile-border: #C6CDD9; --tile-fg: #414D5E; }
.tile-issued        { --tile-bg: #C9E9D9; --tile-border: #A6D8C1; --tile-fg: #0B6247; }
.tile-queue-ops     { --tile-bg: #CFDDF5; --tile-border: #B2C8EC; --tile-fg: #12448F; }
.tile-queue-ops-head { --tile-bg: #DDD5F2; --tile-border: #C6BAE8; --tile-fg: #43308F; }
.tile-queue-finance { --tile-bg: #C9E9D9; --tile-border: #A6D8C1; --tile-fg: #0B6247; }
.tile-staff         { --tile-bg: #CBD9EF; --tile-border: #AEC3E4; --tile-fg: #002A66; }
.tile-agents        { --tile-bg: #C9E6F2; --tile-border: #A6D4E6; --tile-fg: #145A78; }

/* ── The username, shown once on the invite screen ──────────────────────── */

.username-callout {
  border: 1px solid var(--border); border-left: 3px solid var(--teal);
  border-radius: var(--radius); background: var(--bg);
  padding: 12px 14px; margin-bottom: 18px;
}
.username-callout .k {
  font-size: var(--fs-xs); text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted);
}
.username-callout .v { font-size: var(--fs-2xl); font-weight: 700; color: var(--navy); margin: 2px 0 4px; }

/* The tenant code is fixed; the editable part sits next to it. */
.username-compose { display: flex; align-items: stretch; }
.username-compose .prefix {
  display: flex; align-items: center; padding: 0 10px;
  background: var(--bg); border: 1px solid var(--border); border-right: none;
  border-radius: var(--radius) 0 0 var(--radius);
  font-family: ui-monospace, Consolas, monospace; font-weight: 600; color: var(--text-secondary);
}
.username-compose .input { border-radius: 0 var(--radius) var(--radius) 0; }

/* ── Checklist items that carry typed values as well as (or instead of) a file ── */

.ci-row { display: flex; justify-content: space-between; align-items: center; gap: 12px; }
.ci-fields { margin: 10px 0 2px 46px; padding-left: 14px; border-left: 2px solid var(--border); }
.ci-field-form { display: flex; flex-wrap: wrap; gap: 12px 18px; align-items: flex-start; }
.ci-field { min-width: 210px; }
.ci-field .label { margin-bottom: 4px; }
.ci-field .input { max-width: 260px; }
.ci-field-actions { align-self: flex-end; padding-bottom: 2px; }

@media (max-width: 700px) {
  .ci-fields { margin-left: 0; }
}

/* ── One-field search: input and button on one line ────────────────────── */

.search-row { display: flex; gap: 8px; align-items: stretch; }
.search-row .input { flex: 1; min-width: 0; }
.search-row .btn { white-space: nowrap; }

@media (max-width: 560px) {
  .search-row { flex-direction: column; }
}

/* ── Screening: progress and findings ──────────────────────────────────── */

/* The AI layer wears the logo's accent colours — it is the part of this portal
   CSUL does not have, and it should be recognisable on sight. */

.screening-bar { height: 6px; border-radius: 3px; background: var(--border); overflow: hidden; margin-top: 14px; }
.screening-bar-fill {
  height: 100%; border-radius: 3px; transition: width .4s ease;
  background: linear-gradient(90deg, var(--logo-cyan), var(--logo-sky), var(--logo-mint));
}

.ai-chip {
  display: inline-flex; align-items: center; gap: 6px;
  background: linear-gradient(135deg, rgba(70,196,214,.14), rgba(63,168,224,.14));
  border: 1px solid rgba(63, 168, 224, .35);
  color: var(--primary); font-size: var(--fs-xs); font-weight: 500;
  padding: 3px 9px; border-radius: 20px; white-space: nowrap;
}
.card-head-icon { display: flex; align-items: center; gap: 9px; margin-bottom: 12px; }
.card-head-icon h4 { margin: 0; }

/* While a run is in flight the sparkle breathes, so the page reads as working
   rather than stalled. Respects reduced-motion. */
@keyframes ai-pulse { 0%, 100% { opacity: 1; } 50% { opacity: .45; } }
.ai-working { animation: ai-pulse 1.8s ease-in-out infinite; }
@media (prefers-reduced-motion: reduce) { .ai-working { animation: none; } }

/* ── The scanner ───────────────────────────────────────────────────────── */
/* Screening is the longest wait in the app — a minute or two during which a
   progress bar alone reads as a stalled page. A robot reading a sheet says
   "working" at a glance, and costs nothing: no image, no library, one keyframe
   each. Everything here stops under prefers-reduced-motion, where the same
   information is still carried by the text and the bar. */

.scanner {
  display: flex; align-items: center; gap: 18px; flex-wrap: wrap;
  padding: 18px 20px; border-radius: var(--radius);
  background: linear-gradient(135deg, rgba(70,196,214,.10), rgba(63,168,224,.07) 55%, rgba(95,190,155,.10));
  border: 1px solid rgba(63, 168, 224, .28);
}
.scanner-bot { position: relative; flex: 0 0 auto; color: var(--primary); line-height: 0; }
/* The beam from the robot down onto the page. */
.scanner-beam {
  position: absolute; left: 50%; top: 100%; width: 2px; height: 14px;
  transform: translateX(-50%);
  background: linear-gradient(180deg, var(--logo-cyan), transparent);
  animation: beam-flicker 1.4s ease-in-out infinite;
}
@keyframes beam-flicker { 0%, 100% { opacity: .9; } 50% { opacity: .25; } }

/* The sheet being read. */
.scanner-doc {
  position: relative; flex: 0 0 auto;
  width: 62px; height: 76px; border-radius: 5px; overflow: hidden;
  background: #fff; border: 1px solid var(--border);
  padding: 11px 9px; display: flex; flex-direction: column; gap: 6px;
  box-shadow: 0 2px 6px rgba(0, 42, 102, .10);
}
.scanner-line { display: block; height: 4px; border-radius: 2px; background: #DCE3EC; }
.scanner-line:nth-child(1) { width: 100%; }
.scanner-line:nth-child(2) { width: 74%; }
.scanner-line:nth-child(3) { width: 88%; }
.scanner-line:nth-child(4) { width: 56%; }
/* The band that travels down the sheet. */
.scanner-sweep {
  position: absolute; left: 0; right: 0; height: 26px;
  background: linear-gradient(180deg, transparent, rgba(70,196,214,.55), transparent);
  animation: sweep 2.1s cubic-bezier(.45, 0, .55, 1) infinite;
}
@keyframes sweep {
  0% { top: -26px; }
  100% { top: 76px; }
}

.scanner-text { flex: 1; min-width: 170px; }
.scanner-title { font-size: var(--fs-md); font-weight: 600; color: var(--navy); }

@media (prefers-reduced-motion: reduce) {
  .scanner-sweep { animation: none; top: 24px; opacity: .4; }
  .scanner-beam { animation: none; }
}
@media print { .scanner { display: none; } }

/* A finding is a small record, not a coloured warning strip: a severity chip,
   the sentence, the two values that disagree, and a way to open the document it
   is about. The tint is a hairline and a very pale wash — enough to rank them at
   a glance, not so much that five findings turn the page into a hazard sign. */

.flag {
  border: 1px solid var(--border); border-left: 3px solid var(--border-strong);
  border-radius: var(--radius); padding: 12px 14px; background: var(--card);
}
.flag + .flag { margin-top: 10px; }
.flag-head { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

.flag-sev {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .04em;
  text-transform: uppercase; padding: 3px 9px; border-radius: 20px;
  background: #EEF0F3; color: var(--text-secondary);
}
.flag-sev .ic { width: 13px; height: 13px; }
.flag-sev-danger  { background: var(--danger-bg);  color: var(--danger-text); }
.flag-sev-warning { background: var(--warning-bg); color: var(--warning-text); }
.flag-sev-info    { background: var(--info-bg);    color: var(--info-text); }

.flag-where {
  font-size: var(--fs-xs); font-weight: 500; color: var(--text-secondary);
  text-transform: capitalize;
}
.flag-src {
  display: inline-flex; align-items: center; gap: 5px;
  font-size: var(--fs-2xs); color: var(--text-muted); white-space: nowrap;
}
.flag-msg { font-size: var(--fs-base); margin: 8px 0 0; color: var(--text); line-height: 1.45; }

/* The two values that disagree, side by side and legible — this is the part a
   reviewer actually reads. */
.flag-compare {
  display: grid; grid-template-columns: repeat(auto-fit, minmax(210px, 1fr));
  gap: 8px; margin-top: 10px;
}
.flag-cell {
  background: #F7F9FB; border: 1px solid var(--border);
  border-radius: var(--radius-sm); padding: 7px 10px;
  display: flex; flex-direction: column; gap: 2px; min-width: 0;
}
.flag-cell .k { font-size: var(--fs-2xs); text-transform: uppercase; letter-spacing: .05em; color: var(--text-muted); }
.flag-cell .v { font-size: var(--fs-sm); font-weight: 500; color: var(--text); overflow-wrap: anywhere; }
.flag-cell-bad { background: var(--danger-bg); border-color: var(--danger-border); }
.flag-cell-bad .v { color: var(--danger-text); }

.flag-actions { display: flex; gap: 6px; margin-top: 10px; flex-wrap: wrap; }
.flag-resolved-note { font-size: var(--fs-xs); color: var(--text-secondary); margin: 10px 0 0; }

.flag-blocker { border-left-color: var(--danger-text);  background: #FEF8F8; }
.flag-major   { border-left-color: var(--warning-text); background: #FFFCF6; }
.flag-minor   { border-left-color: var(--info-text); }
.flag-info    { border-left-color: var(--border-strong); }
/* A resolved flag stays visible and stays readable — it is the audit trail of
   a human overriding an automated finding, not clutter to be hidden. */
.flag-resolved { opacity: .7; background: var(--card); }
.flag-resolved .flag-msg { text-decoration: line-through; text-decoration-color: var(--text-muted); }
.flag-resolve { display: flex; gap: 6px; margin-top: 10px; }
.flag-resolve .input { flex: 1; font-size: var(--fs-sm); padding: 7px 9px; }

.field-grid .v.low-confidence { color: var(--text-secondary); border-bottom: 1px dotted var(--warning-text); }

/* ── Responsive: agent-facing screens are used on phones ───────────────── */

@media (max-width: 860px) {
  .review-layout { grid-template-columns: 1fr; }
  .form-row { grid-template-columns: 1fr; }
  .dash-body { padding: 16px; }
  .stepper { overflow-x: auto; }
  .step { min-width: 72px; }
  .modal-panel { width: 100vw; max-height: 100vh; border-radius: 0; }
  .modal-frame { height: 82vh; }
}
