/*
 * This is a manifest file that'll be compiled into application.css.
 *
 * With Propshaft, assets are served efficiently without preprocessing steps. You can still include
 * application-wide styles in this file, but keep in mind that CSS precedence will follow the standard
 * cascading order, meaning styles declared later in the document or manifest will override earlier ones,
 * depending on specificity.
 *
 * Consider organizing styles into separate files for maintainability.
 */

/* The palette, lifted from settleupfunding.com on 2026-09-10 so the portal and
   the website read as one company. Nothing here is invented: every value comes
   from the punch-list design section, which took them off the site itself.

   This replaced a cool blue-grey ramp. The warmth is the point — "App
   background — warm, not the current blue-grey" is the note against
   --surface-sunken, and a near-white #FAFAFB ground was the single thing that
   most made the portal look unrelated to the marketing site.

   **There is no dark mode, deliberately.** The old palette carried one, with
   measured ratios for every token; it was removed with this change rather than
   translated, because the design supplies no dark values and the source website
   has no dark treatment to lift them from. Inventing a second palette would
   mean choosing forest green, accent green and four pill colours for a mode
   nobody has reviewed, and shipping the old cool greys under @media would leave
   two unrelated designs in one file. If dark mode returns it should come from
   the same place this did.

   Ratios below are against --surface and --surface-sunken; the minimum for text
   is 4.5:1. If you change a value here, re-check it against both grounds and
   against its own tint. */
:root {
  /* Forest green. The header bar, the sign-in panel, the Funded pill. */
  --forest: #17332B;
  /* Accent green. Primary buttons, the active nav underline, eyebrow rules.
     A fill behind dark text rather than a text colour: 2.5:1 on white, so it
     never carries type on the page ground. */
  --accent: #5BA83F;
  /* The text that sits on --accent. Near-black rather than white: 8.9:1 on the
     accent fill, where white would be 2.6:1. */
  --on-accent: #10211A;
  /* Cream. Text and the logo box on forest green. 13.1:1 on --forest. */
  --cream: #F2F0EA;

  /* ---- The logo's own two colours ------------------------------------
     Measured off the logo file SettleUp sent on 2026-09-11, not sampled by
     eye: #69AB4E and #282927 are the two fills that account for every solid
     pixel of the mark.

     **Deliberately separate from --accent and --ink, and they must stay
     separate.** Those are UI colours with contrast ratios tuned against the
     page grounds, and they are free to be adjusted for legibility. These two
     are the company's identity and are not ours to adjust — the mark has to
     match the letterhead, the website and the signature block, none of which
     this stylesheet governs. They were the same value once and the mark
     drifted the moment --accent was tuned, which is the reason for the split.

     Used only by the brand mark. Nothing else on the page should reach for
     them: type set in #282927 would be an off-black that matches nothing
     else, and a button filled with #69AB4E would sit beside --accent buttons
     looking like a rendering fault. */
  --brand-green: #69AB4E;
  --brand-black: #282927;

  --ink: #1A2420;
  --ink-muted: #5F6B64;
  /* Faint, but never below the 4.5:1 body-text minimum: this carries content,
     not decoration — the form's hints, the "—" that means a question went
     unanswered, and "Not sent yet" against a client text that did not go.
     4.6:1 on white and 4.5:1 on the sunken surface it sits on in table rows.
     The design's own #8A948C is 2.9:1 and fails that, so it is used only for
     the uppercase micro-labels the mockups put it on, never for body text. */
  --ink-faint: #6B766E;
  /* The design's lightest grey. Decoration only — table chevrons, a disabled
     input's placeholder. Never text that has to be read. */
  --ink-ghost: #8A948C;

  --rule: #E3E5DF;
  --rule-strong: #D9DCD4;
  /* Table row dividers, lighter than the card border they sit inside. */
  --rule-soft: #EDEEE9;

  --surface: #FFFFFF;
  --surface-sunken: #F7F6F2;
  /* Table headers and the footer bar: a half-step off white, still warm. */
  --surface-raised: #FBFAF7;
  --surface-foot: #F1F0EB;
  /* The row under the cursor. Green-tinted rather than grey, so a hovered row
     reads as part of this palette. */
  --surface-hover: #F4F7F2;
  /* A filled button under the cursor. Darker than --accent, keeping the same
     near-black text: 7.4:1. */
  --accent-hover: #4E9235;

  /* Green tint. The reassurance notes — the §1.9 cap, the decision-time line.
     Not a status: nothing is wrong and nothing has settled, it is the colour
     the design gives to a sentence that exists to reassure. */
  --tint: #EDF5E8;
  --tint-ink: #2E6B3C;
  --tint-rule: #CFE3C4;

  /* Provisional figures are not a warning about a mistake — they are a warning
     that the number will move. Amber, not red: nothing is wrong. */
  --caution: #8A6410;
  --caution-bg: #FBF5E6;
  --caution-rule: #E6D3A3;
  /* The underwriting block's own accent, which the design gives a darker amber
     than the note text so the section rule reads against the card. */
  --caution-rule-strong: #B4780A;

  /* A field the form rejected. 7.6:1 on white and 6.9:1 on its own tint, so
     the message reads as text rather than only as a colour. */
  --error: #A32A2F;
  --error-bg: #FDF1F0;
  --error-rule: #E8B4B6;

  /* Settled is the Approved pill's pair, so the two cannot drift. */
  --settled: #2E7D46;
  --settled-bg: #E4F0E5;
  --settled-rule: #BFD9C4;


  /* Links. 6.1:1 on white, and still green rather than blue so a bare link in
     a sentence belongs to the same family as everything around it. */
  --link: #2E6B3C;
  --link-hover: #1D4A28;

  /* The four status pills, background over text. Straight from the design. */
  --pill-submitted-bg: #FBF1DC;
  --pill-submitted-ink: #96690A;
  /* Declined at the firm's own request. Neutral grey as of round three A6,
     replacing a blue tint: Trent's note is that this "is not a negative event
     -- it is a record the firm asked for -- so it should read neutral rather
     than alarming". Blue was alarming in the other direction, reading as a
     state with something to do about it. */
  --pill-declined-bg: #ECEEEA;
  --pill-declined-ink: #5F6B64;
  --pill-approved-bg: #E4F0E5;
  --pill-approved-ink: #2E7D46;
  --pill-funded-bg: #17332B;
  --pill-funded-ink: #A8DC90;
  /* **The loudest chip on the page, and the only one that is.** A request
     SettleUp opened from the client's own call is the single status that asks
     the *firm* to do something; every other one is a report. Saturated amber
     rather than a tint, so it is findable down a column of pale pills without
     reading as an error -- nothing has gone wrong, somebody is just waiting.
     6.79:1, comfortably past AA. */
  --pill-answer-bg: #E8A33D;
  --pill-answer-ink: #3A2405;
  /* Paid off is the one chip drawn as an outline rather than a fill. It is the
     end of the line and wants to recede: a firm scanning for what still needs
     attention should read past it, which a solid chip fights. */
  --pill-paid-off-bg: #FFFFFF;
  --pill-paid-off-ink: #17332B;

  /* Type. Both faces come from Google Fonts in one stylesheet link; the stacks
     fall back to the same shapes the rest of the system assumes.

     Playfair Display never runs as body text — page titles, dollar figures and
     stat numbers only. That restraint is in the design and is easy to lose:
     it is a display face, and at 15px it reads as a mistake.

     **Every font-size is in rem, against a 16px root, with the design's pixel
     value kept beside it in a comment.** A px font-size ignores the reader's
     own text-size setting: measured in a browser at a 24px root, the px values
     did not move at all — body stayed at 15px and the table headers at 10px —
     while the few rem values grew, so the secondary line under a client's name
     came out larger than the body text above it. Page zoom scales px as well,
     which is what makes this look fine to everyone except the person it fails.

     Only type. Borders, rules, glyph boxes and the header's 68px min-height
     stay in px, because a 1px rule is a 1px rule at any text size.
     `test/integration/stylesheet_test.rb` holds the line. */
  --f-display: "Playfair Display", Georgia, "Times New Roman", serif;
  --f-body: "Poppins", -apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif;

  /* Square corners — no radius anywhere in the system. A token rather than a
     literal 0 so the one-line way to undo that decision is here, not spread
     across forty rules. */
  --radius: 0;

  --measure: 52rem;
  /* The width of the page body, which the account bar above it matches. Prose
     by default; a wide page sets --page-measure on <body> instead. */
  --page-measure: var(--measure);
  --measure-wide: 68rem;
}
*,
*::before,
*::after {
  box-sizing: border-box;
}

/* The header bar is full-bleed forest green and has to reach the top and both
   edges of the window, so the page's own padding moves off <body> and onto the
   content below it (`.page-body`). Padding here would leave a warm-grey band
   above the bar on every screen. */
body {
  display: flex;
  flex-direction: column;
  min-height: 100vh;
  margin: 0;
  padding: 0;
  background: var(--surface-sunken);
  color: var(--ink);
  font-family: var(--f-body);
  font-size: 0.9375rem; /* 15px */
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

/* Everything between the header and the footer. Carries the page padding <body>
   used to, and grows so a short page still pushes the footer to the bottom of
   the window rather than leaving it floating mid-screen. */
main {
  flex: 1 0 auto;
  padding: 2.25rem 2.5rem 3rem;
}

/* **Every centred page is `width: 100%`, and it is not redundant.** <body> is a
   flex column, so each <main> is a flex item, and a flex item whose cross-axis
   margin is `auto` does not stretch — `margin: 0 auto` on the page classes
   below opts every one of them out of the stretch that would otherwise give
   them the full line. What they get instead is shrink-to-fit, which is the
   width of their widest content: a populated queue holds a table and fills the
   measure, so the page looks correct and the bug stays invisible. Empty it, and
   the same page is as wide as the one sentence saying nothing matched — the
   column narrows and re-centres, and the heading and tabs visibly move.

   So the width is stated rather than inferred from the contents. Any new page
   class that centres itself with `margin: 0 auto` belongs in this list, or it
   will collapse on whichever of its states holds the least. */
.dashboard,
.queue,
.detail,
.requests,
.form-page {
  width: 100%;
}

/* The sign-in split is full-bleed and manages its own padding.

   Scoped to the split rather than to `.layout--bare`, and that distinction is
   load-bearing: on the other signed-out screens `.auth` *is* the <main>, so a
   rule on `main` zeroes the card's own padding and the fields run to its edge.
   Those pages take the ordinary page padding instead. */
main.auth-split {
  padding: 0;
}

/* Page titles, dollar figures and stat numbers. Never body text — it is a
   display face, and at 15px it reads as a mistake. */
h1,
.display {
  font-family: var(--f-display);
  font-weight: 500;
  letter-spacing: -0.015em;
}

/* Every link that is not a button and does not carry its own colour. Bare
   anchors — back links, a client name in a table, "Funded clients" in a
   sentence — had no rule at all and fell back to the browser default, which
   was 1.76:1 on the dark surface.
   Visited is the same value deliberately: which admin pages somebody has
   opened is not information the screen should encode, and the default purple
   is the least readable colour on the dark palette.

   **`:where()` so this never outranks a component.** `a:visited` on its own is
   specificity (0,1,1) — a pseudo-class counts as a class — which beat every
   single-class rule that colours a link, so a *visited* nav tab turned blue
   while its unvisited neighbours stayed grey. `:where()` scores zero, so this
   is a default any component overrides rather than a rule they have to fight. */
:where(a, a:visited) {
  color: var(--link);
}

a:hover {
  text-decoration-thickness: 2px;
}

/* Money and dates are compared down the column, so they need consistent digit
   widths. Tabular figures keep them from shifting against each other. */
.client__payoff,
.client__facts dd {
  font-variant-numeric: tabular-nums;
}

/* ---- Flash ------------------------------------------------------------- */

.flash {
  max-width: var(--measure);
  margin: 0 auto 1.5rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
  font-size: 0.9375rem;
}

.flash--alert {
  border-color: var(--caution-rule);
  background: var(--caution-bg);
  color: var(--caution);
}

.flash--notice {
  border-color: var(--settled-rule);
  background: var(--settled-bg);
  color: var(--settled);
}

/* ---- Dashboard --------------------------------------------------------- */

.dashboard,
.sign-in {
  max-width: var(--measure);
  margin: 0 auto;
}

/* **Every page heading in the app is the rule below.** Five screens set their
   own until 2026-09-08 and had drifted: the firm lists disagreed with each
   other on size, spacing and subtitle, while the admin queue and detail pages
   set no size at all and took the browser default -- so "Funding requests" and
   "Funded clients" rendered visibly different from one another and from
   "Incoming requests".

   Grouped rather than restated, so a sixth screen inherits the treatment
   instead of inventing a seventh. The sign-in page keeps its own smaller
   heading: it is a single centred card rather than a page of content, and
   1.75rem on it reads as shouting. */
.dashboard__header,
.requests__header,
.queue__header,
.detail__header,
.form-page__header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 1rem;
  margin-bottom: 2rem;
}

/* Refer an attorney sits beside New funding request, and wraps under it on a
   phone rather than squeezing it. */
.requests__actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
}

/* 40px, the design's page-title size. Family, weight and tracking come from the
   `h1` rule near the top of this file, which every page title shares. */
.dashboard__header h1,
.requests__header h1,
.queue__header h1,
.detail__header h1,
.form-page__header h1 {
  margin: 0;
  font-size: 2.5rem; /* 40px */
  line-height: 1.05;
}

.dashboard__firm,
.requests__subtitle,
.queue__subtitle,
.detail__meta {
  margin: 0.5rem 0 0;
  color: var(--ink-muted);
  font-size: 0.8125rem; /* 13px */
}

.empty {
  padding: 2rem;
  border: 1px dashed var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink-muted);
  text-align: center;
}

/* The line under the worklist pointing at the agreements set aside. Quiet on
   purpose: it is a way back to rows nobody is working, not a second queue, and
   sizing it like one would put it in competition with the table above. */
.queue__footnote {
  margin: 1rem 0 0;
  color: var(--ink-muted);
  font-size: 0.875rem;
}

/* The reason field on the set-aside form. Full width because the sentence is
   the whole value of the record later, and a short box invites a short answer
   -- "wrong" tells the next reader nothing. */
.set-aside .field {
  margin-bottom: 1rem;
}

.set-aside input[type="text"] {
  width: 100%;
}

/* ---- Summary ------------------------------------------------------------ */

/* The counts at the top of a firm's own lists, which are also the filter for
   the list below them.

   Deliberately quiet: the counts orient somebody arriving, and the rows beneath
   are what they came to read. A panel with its own fill would compete with the
   client cards, which carry the payoff a firm is actually here for.

   A form rather than a dl, because each tile is a real submit button — see the
   partial. The grid rules are unchanged from the figures this replaced. */
.summary {
  display: grid;
  /* `auto-fit` with a 1fr track, which is what the money figures used, stretches
     whatever tiles there are across the full width — fine for three, but it puts
     half a screen between Active and Paid off on the dashboard's two. Fixed
     columns keep the rhythm the same on both pages whether there are two tiles
     or four, and the row stops short rather than spreading to fill. */
  /* One row of equal columns, as the design's stat row is. `auto-fit` at a
     fixed 11rem wrapped the requests page's four tiles onto two rows at this
     measure while the dashboard's two sat side by side, so the same component
     had two different rhythms. Equal fractions keep every tile on one line and
     let the row use the width it has. */
  grid-template-columns: repeat(var(--summary-columns, 2), minmax(0, 1fr));
  gap: 1.25rem;
  margin: 0 0 1.75rem;
  /* Only a bottom rule. The firm nav directly above already draws its own
     border-bottom, so a border-top here landed a second line immediately under
     it — two rules with a hairline of gap between them. */
  padding: 0.25rem 0 1.25rem;
  border-bottom: 1px solid var(--rule);
}

/* Four tiles at 1fr each is a 34px figure in a 70px column on a phone. Two per
   row instead, which keeps the label on one line. */
@media (max-width: 40rem) {
  .summary {
    grid-template-columns: repeat(2, minmax(0, 1fr));
    gap: 1rem;
  }
}

/* A button has to be reset back to reading as a figure: the browser's own
   background, border, font and centring would otherwise make the row look like
   a toolbar. What stays is the cursor and the focus ring — it is a control, and
   it must look like one to the keyboard. */
.summary__item {
  min-width: 0;
  appearance: none;
  background: none;
  border: 0;
  /* The accent lives on the left border, so the unselected state reserves the
     same space and the row does not shift by 3px when somebody clicks. */
  border-left: 3px solid transparent;
  padding: 0.125rem 0 0.125rem 0.625rem;
  margin: 0;
  font: inherit;
  text-align: left;
  cursor: pointer;
  display: block;
  width: 100%;
}

.summary__item:hover .summary__label,
.summary__item:hover .summary__value {
  color: var(--ink);
}

.summary__item:focus-visible {
  outline: 2px solid var(--forest);
  outline-offset: 2px;
  border-radius: var(--radius);
}

/* The tile that is on. Unmistakable rather than subtle, because the whole point
   is that somebody can tell at a glance which bucket the list below is showing —
   a filter you cannot see is worse than no filter. The accent border carries it,
   with the label and figure stepped forward to --link while the others sit back
   in grey. Colour is not the only signal: aria-pressed says the same thing to a
   screen reader, and the border is a shape rather than a hue. */
.summary__item--on {
  border-left-color: var(--accent);
  /* A faint wash the width of the tile, so the selection reads as a block rather
     than as a 3px line somebody has to notice. Tinted from the accent rather than
     grey, so it belongs to the border it sits beside. */
  background: color-mix(in srgb, var(--accent) 8%, transparent);
}

.summary__item--on .summary__label {
  color: var(--forest);
}

.summary__item--on .summary__value {
  color: var(--ink);
}

/* Weight as well as colour, so the selected tile is still obvious to somebody
   who cannot separate the two mid-tones. */
.summary__item--on .summary__label {
  font-weight: 700;
}

/* A job rather than a state (the admin queue's Payment failed): amber, because
   it is something to chase, and always marked rather than only when on, since
   it sits outside the sum the other tiles make and a reader adding them up
   needs to see which one to leave out. */
.summary__item--job {
  border-left-color: var(--caution-rule-strong);
  background: var(--caution-bg);
}

.summary__item--job .summary__label {
  color: var(--caution);
}

.summary__label {
  display: block;
  margin-bottom: 0.25rem;
  color: var(--ink-ghost);
  font-size: 0.65625rem; /* 10.5px */
  font-weight: 600;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

/* A stat number, which the design sets in the display face at 34px — one of the
   three places Playfair is allowed. */
.summary__value {
  display: block;
  margin: 0;
  font-family: var(--f-display);
  font-size: 2.125rem; /* 34px */
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.01em;
  color: var(--ink);
  /* Compared down the column against the cards below, so the digits must not
     shift width against them. */
  font-variant-numeric: tabular-nums;
}

/* An unselected figure sits back from the selected one, so the row reads as one
   bucket chosen out of several rather than as several equal headings. */
.summary__item:not(.summary__item--on) .summary__value {
  color: var(--ink-muted);
}

/* ---- Client cards ------------------------------------------------------ */

.clients {
  display: grid;
  gap: 1rem;
  margin: 0;
  padding: 0;
  list-style: none;
}

.client {
  padding: 1.5rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
}

.client__name {
  margin: 0 0 1.25rem;
  font-size: 1.125rem;
  font-weight: 620;
}

.client__facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 1.25rem;
  margin: 0;
}

.client__facts > div {
  min-width: 0;
}

.client__facts dt {
  margin-bottom: 5px;
  color: var(--ink-muted);
  font-size: 0.65625rem; /* 10.5px */
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
}

.client__facts dd {
  margin: 0;
  font-size: 0.9375rem; /* 15px */
}

/* The payoff is what the firm came to read. It outranks every other fact on
   the card, so it is the only figure that gets size and weight.
   Scoped through .client__facts to outrank the `dd` base rule above — as a bare
   class it loses on specificity and silently renders at body size. */
.client__facts .client__payoff {
  font-family: var(--f-display);
  font-size: 1.625rem; /* 26px */
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.015em;
}

/* Absence of a price is information, not a blank. Italic and muted so it reads
   as an explanation rather than as a value. */
.client__facts .client__unavailable {
  color: var(--ink-muted);
  font-style: italic;
}

.client__facts .client__status {
  font-weight: 570;
  color: var(--settled);
}

/* A settled advance is finished business. Muting the whole card keeps the
   active ones visually first without hiding it. */
.client--paid-off {
  background: var(--surface-sunken);
}

.client--paid-off .client__name {
  color: var(--ink-muted);
}

/* The §1.8 cap clause, under "does not change further". Its own line and
   quieter than the date it follows: it explains a value rather than being one,
   and the fact it explains is reassuring rather than urgent. */
.client__capped {
  display: block;
  margin-top: 0.25rem;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.4;
}

/* ---- Notes -------------------------------------------------------------- */

/* A short tinted line that reassures rather than warns: how fast a decision
   comes, that the §1.9 cap can only move the figure down. The design gives
   these the green tint, which is not a status — nothing is wrong and nothing
   has settled — so it is its own token pair rather than --settled.

   Inline-flex so the tint wraps the sentence rather than running the width of
   the page, which would read as a banner. */
.note {
  display: inline-flex;
  align-items: center;
  gap: 9px;
  margin: 0 0 26px;
  padding: 8px 13px;
  font-size: 0.78125rem; /* 12.5px */
  font-weight: 500;
  line-height: 1.5;
}

.note--reassure {
  background: var(--tint);
  color: var(--tint-ink);
}

/* A firm's answer that went against the client the portal matched (the case
   question, Trent, 2026-10-10): worth Olivia's eye, not an alarm. */
.note--caution {
  background: var(--caution-bg);
  color: var(--caution);
}

/* ---- Payoff note -------------------------------------------------------- */

/* Deliberately quiet. This replaced an amber §1.9 caution that rendered under
   every active row and could never clear, because nothing in the app supplies
   net proceeds — a permanent alarm on every line reads as noise and trains firms
   to ignore it. The line's job now is smaller and factual: name the source of
   the figure and give a route to question it. Muted body text is correct for
   that; escalating it back to a caution would restore the original problem. */
.client__payoff-note {
  margin: 1rem 0 0;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.45;
}

/* A settled payment is reassurance, not an alert: the firm already knows they
   paid, and this only confirms it landed. Same muted weight as the payoff note. */
.client__facts .client__payment {
  color: var(--ink-muted);
}

/* The lien-satisfied statement on a settled card. The one line on a paid-off
   card that is not a fact in a list, and the reason a firm opens it — so it
   gets the settled colour rather than the muted body grey the notes use. */
.client__satisfied {
  margin: 1.25rem 0 0;
  color: var(--settled);
  font-weight: 550;
  font-size: 0.9375rem;
  line-height: 1.45;
}

/* The payoff form. Separated from the attribution note above it, which is prose
   about the figure — without the gap the field's label reads as the next
   sentence of that paragraph rather than as a label. */
.payoff__form {
  margin-top: 1.25rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
}

/* ---- Carried answers on a repeat request -------------------------------- */

/* An answer prefilled from the previous request that the attorney still has to
   confirm. A case that was pre-suit in March is in litigation by September, and
   the second advance must not be underwritten off the first one's facts.

   Marked rather than blanked, because a glance-and-confirm beats a retype — and
   marked only on the answers that actually move, since a highlight on every
   field would mean nothing. The rule is a left edge rather than a fill: it has
   to read as "look at this", not as "something is wrong here", which is what
   the error styling already says. */
.field--carried {
  padding-left: 0.75rem;
  border-left: 3px solid var(--caution-rule);
}

.field--carried .field__hint {
  color: var(--ink-muted);
}

/* ---- Client lookup -------------------------------------------------------- */

/* The existing clients offered under "Client name" on a new request (round
   three A7). Laid over the fields below rather than pushing them down: the list
   comes and goes on every keystroke, and a form that jumps while somebody types
   is one they stop looking at. */
.lookup__anchor {
  position: relative;
}

.lookup__list {
  position: absolute;
  top: 100%;
  z-index: 10;
  left: 0;
  right: 0;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-top: 0;
  box-shadow: 0 8px 20px rgba(23, 51, 43, 0.13);
}

.lookup__option,
.lookup__new {
  display: block;
  width: 100%;
  padding: 0.7rem 0.9rem;
  border: 0;
  border-bottom: 1px solid var(--rule);
  background: none;
  font: inherit;
  text-align: left;
  text-decoration: none;
  color: var(--ink);
  cursor: pointer;
}

.lookup__new {
  border-bottom: 0;
  background: var(--surface-sunken);
  color: var(--link);
  font-size: 0.875rem;
}

/* One rule for the mouse and the arrow keys, since focus never leaves the
   field: `aria-selected` is the keyboard's highlight. */
.lookup__option:hover,
.lookup__new:hover,
.lookup [aria-selected="true"] {
  background: var(--tint);
}

.lookup__name,
.lookup__action,
.lookup__meta {
  display: block;
}

.lookup__name {
  font-weight: 600;
}

/* What picking the row does. Link-coloured because it is the action, where the
   line under it is only context. */
.lookup__action {
  margin-top: 0.15rem;
  font-size: 0.8125rem;
  font-weight: 500;
  color: var(--link);
}

.lookup__meta {
  margin-top: 0.15rem;
  font-size: 0.78rem;
  color: var(--ink-muted);
}

@media (forced-colors: active) {
  .lookup [aria-selected="true"] {
    outline: 2px solid Highlight;
    outline-offset: -2px;
  }
}

/* The strip above a repeat request: who this is, and what they already have
   with us — shown before the box asking how much more. */
.existing-client {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem 1.25rem;
  flex-wrap: wrap;
  margin: 1.25rem 0;
  padding: 0.9rem 1.1rem;
  background: var(--tint);
  border: 1px solid var(--tint-rule);
  border-left: 3px solid var(--accent);
}

.existing-client__title {
  margin: 0;
  font-weight: 600;
}

.existing-client__figures {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 1.1rem;
  margin: 0.25rem 0 0;
  font-size: 0.8125rem;
  color: var(--ink-muted);
}

.existing-client__figures strong {
  color: var(--ink);
  font-variant-numeric: tabular-nums;
}

.existing-client__reset {
  font-size: 0.8125rem;
  white-space: nowrap;
}

/* "Which case is this for?" (Trent, 2026-10-10): a firm's answer for a client
   we already know. One row per choice, the whole row clickable. */
.case-question {
  border-color: var(--caution-rule);
  background: var(--caution-bg);
}

.case-question__options,
.choice-list {
  display: grid;
  gap: 0.5rem;
  margin-top: 0.75rem;
}

.case-question__option,
.choice-list__option {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  padding: 0.75rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-raised);
  cursor: pointer;
}

.case-question__option input,
.choice-list__option input {
  margin-top: 0.25rem;
}

.case-question__option:has(input:checked),
.choice-list__option:has(input:checked) {
  border-color: var(--accent);
}

.case-question__meta,
.choice-list__meta {
  display: block;
  font-size: 0.8125rem;
  color: var(--ink-muted);
}

/* A labelled group of `.choice-list` radios inside a form's fieldset. */
.choice-group {
  display: grid;
  gap: 0.375rem;
}

.choice-group .choice-list {
  margin-top: 0;
}

/* ---- Document downloads ------------------------------------------------- */

/* The signed agreement, and the payoff confirmation beside it. These were
   13.5px plain text under a grey paragraph and they disappeared — a paralegal
   pulling the executed copy for a case file could not find the one thing on the
   card they had come for.

   So the link gets weight: larger than body text, semibold, and underlined
   standing still rather than only on hover. Underlined because this is the one
   control on a card that is not a button, and colour alone is not enough to
   tell it apart from the prose it sits in. */
.client__agreement {
  margin: 1.25rem 0 0;
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.25rem 0.5rem;
}

.download {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.96875rem; /* 15.5px */
  font-weight: 600;
  text-decoration: underline;
  text-underline-offset: 0.18em;
}

/* Sized in `em`, so it tracks the link rather than being re-measured every time
   the type moves. Aligned by nudging rather than by baseline: a stroked glyph
   has no baseline to sit on, and left alone it rides high against the cap
   height of the text beside it. */
.download__icon {
  width: 1em;
  height: 1em;
  flex: none;
  translate: 0 0.06em;
}

/* The execution date. Deliberately quiet — it qualifies the link rather than
   competing with it, and the link is what the eye should land on. Kept on the
   baseline of the link's first line by the flex row above, so a card whose
   title wraps does not strand the date. */
.download__date {
  color: var(--ink-muted);
  font-size: 0.8125rem;
}

/* ---- Payoff flow (D6) --------------------------------------------------- */

/* The per-card action area. Separated from the note above it rather than
   crowding it, because this is the one control on the card that spends money. */
.client__actions {
  margin: 1rem 0 0;
}

/* Stands where "Request another advance" would be. The action area already
   supplies the gap above it, so the note's own margin would double it. */
.client__actions > .client__repeat-withheld {
  margin: 0;
}

/* The last payoff attempt failed. Caution rather than error: nothing is
   broken, the balance is simply still open, and the button below it is the
   remedy. Sits above that button, so it takes the gap below rather than above. */
.client__actions > .client__payment-failed {
  margin: 0 0 0.75rem;
  padding: 0.625rem 0.75rem;
  border-left: 3px solid var(--caution-rule-strong);
  background: var(--caution-bg);
  color: var(--caution);
}

/* The confirmation screen. Narrower than the dashboard on purpose — there is
   one decision to make here, and the page should not look like a list. */
/* The accent top border the design gives the payoff card. This is the last
   screen before money moves, and it is the only card on the page. */
.payoff {
  max-width: 32rem;
  margin: 1.5rem 0 0;
  padding: 30px 32px;
  border: 1px solid var(--rule);
  border-top: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface);
}

.payoff__facts {
  display: grid;
  gap: 1rem;
  margin: 0;
}

.payoff__facts dt {
  margin: 0 0 6px;
  color: var(--ink-muted);
  font-size: 0.65625rem; /* 10.5px */
  font-weight: 600;
  letter-spacing: 0.13em;
  text-transform: uppercase;
}

.payoff__facts dd {
  margin: 0;
}

/* The number being agreed to. The largest figure in the app at 52px, because
   this is the last screen before money moves and it should be unmissable. */
.payoff__facts .payoff__amount {
  font-family: var(--f-display);
  font-size: 3.25rem; /* 52px */
  font-weight: 500;
  line-height: 1;
  letter-spacing: -0.02em;
  font-variant-numeric: tabular-nums;
}

/* The advances a case payoff is made of, under its total. */
.payoff__advances {
  margin: 1.25rem 0 0;
}

.payoff__note {
  margin: 1rem 0 0;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.45;
}

.payoff__actions {
  display: flex;
  gap: 0.75rem;
  align-items: center;
  margin: 1.5rem 0 0;
}

/* ---- Site header ------------------------------------------------------- */

/* The header has to line up with the page beneath it, and the pages are not all
   one width: firm-facing screens are --measure (prose), while the admin queue is
   68rem (a table to scan). A fixed --measure here left the email and Sign out
   floating 16rem short of the queue's right edge. The width comes from a custom
   property the body sets, so the two cannot drift.

   Keep `--page-measure` on the inner element below rather than hardcoding a
   width. This is the second element to inherit the trick and the reason it was
   made a property in the first place. */
.layout--wide {
  --page-measure: var(--measure-wide);
}

/* Full-bleed forest green bar, 68px tall, reaching both edges of the window
   while the content inside it stops at the page's measure. Two elements because
   one cannot do both.

   The bar is a solid dark field, so everything in it — links, the email, Sign
   out — is set against --forest rather than inheriting the page's ink. */
.site-header {
  background: var(--forest);
}

/* The bar spans the window, and both ends sit on the window's edges rather than
   the page's.

   It used to be capped at the page's measure like <main> and the footer, so the
   wordmark sat above the page title. That cannot hold the section tabs. Six of
   them need 781px, and the cap left 644px on a wide admin page and 388px on a
   narrow one, so the last two were permanently scrolled out of sight — with the
   scrollbar hidden, "Firms a…" cut mid-word and System health not visible at
   all. Anchoring the wordmark to a 52rem centred page is what reserves that
   space: on a 1416px window it costs 332px on the left before a tab can start,
   which is most of what the strip was missing.

   So the alignment is the thing that gave way. It was worth having when the bar
   was a wordmark and an account block, and it is not worth a nav that hides two
   of its six sections. The page below still centres on its own measure; only
   the chrome above it now uses the full width. */
.site-header__inner {
  display: flex;
  align-items: center;
  gap: 2.5rem;
  min-height: 68px;
  padding: 0 2.5rem;
  font-size: 0.875rem;
}

.site-header__wordmark {
  flex: none;
  text-decoration: none;
}

/* ---- The mark ----------------------------------------------------------- */

/* The white box that carries the mark and wordmark. Inline-flex so it hugs its
   contents: it is a box around the logo, not a bar across the header. */
/* `align-self`/`width` so it hugs its contents wherever it lands: as a flex
   child of the sign-in panel's column it would otherwise stretch the full
   width of the panel, and as a block on the centred auth card it would fill
   the card. It is a box around the logo, not a bar across whatever holds it. */
.brand-mark {
  display: inline-flex;
  align-self: flex-start;
  width: fit-content;
  align-items: center;
  gap: 9px;
  padding: 8px 13px;
  background: #ffffff;
}

.brand-mark__glyph {
  display: block;
  flex: none;
  width: 26px;
  height: 26px;
}

.brand-mark--large {
  gap: 11px;
  padding: 11px 17px;
}

.brand-mark--large .brand-mark__glyph {
  width: 34px;
  height: 34px;
}

/* The logo's own black on the white box rather than --ink: the wordmark is the
   website's, it is not tinted green there, and it has to match the mark beside
   it — which is measured from the same file. */
.brand-mark__name {
  display: block;
  color: var(--brand-black);
  font-size: 1.0625rem; /* 17px */
  font-weight: 700;
  letter-spacing: -0.01em;
  line-height: 1.05;
}

/* "UP" in the logo's green, not --accent: it sits directly beside the mark's
   green triangle, so the two being a shade apart reads as a rendering fault
   rather than as a palette decision. */
.brand-mark__name em {
  color: var(--brand-green);
  font-style: normal;
}

.brand-mark__sub {
  display: block;
  color: var(--brand-black);
  font-size: 0.53125rem; /* 8.5px */
  font-weight: 600;
  letter-spacing: 0.3em;
  line-height: 1.25;
  text-transform: uppercase;
}

.brand-mark--large .brand-mark__name {
  font-size: 1.375rem; /* 22px */
}

.brand-mark--large .brand-mark__sub {
  font-size: 0.65625rem; /* 10.5px */
}

/* The section tabs sit between the wordmark and the account block. `.filters`
   carries its own top margin and bottom rule for the index pages it used to live
   on; inside the dark bar both would show. */
/* Full bar height, so a tab's underline lands exactly on the bar's bottom edge.
   `align-self: stretch` rather than padding: the tabs are flex children of the
   68px row, and 24px of padding either side of a 20px line box overflowed it,
   which is what hung the active underline below the bar. */
/* The bar is one row: the tabs scroll sideways rather than wrapping onto a
   second line, which grew the 68px row and pushed the account block out of
   alignment. `min-width: 0` is what lets the flex item shrink below the tabs'
   combined width at all — without it `flex: 1 1 auto` still floors at the
   content size and nothing ever overflows to scroll. The scrollbar is hidden
   because its trough draws across the bottom edge of the bar, which is exactly
   where the active tab's underline sits. */
.site-header .filters {
  flex: 1 1 auto;
  align-self: stretch;
  align-items: stretch;
  gap: 28px;
  margin: 0;
  border-bottom: 0;
  min-width: 0;
  flex-wrap: nowrap;
  overflow-x: auto;
  scrollbar-width: none;
}

.site-header .filters::-webkit-scrollbar {
  display: none;
}

/* The tabs run the full height of the bar so the active underline sits on its
   bottom edge. `margin-bottom: 0` undoes the -1px the in-page tabs use to pull
   themselves onto a container's bottom rule; there is no rule here, and the
   offset left the underline floating a pixel below the bar. */
/* `flex: none` and `white-space: nowrap` are what make the scroll above work.
   Without them a flex item shrinks below its content width by default, so six
   tabs needing 793px in a 624px bar each compress and break their label across
   two lines rather than overflowing — and a strip that never overflows never
   scrolls. Holding each tab at its natural width pushes the excess out to the
   container, which is the thing that scrolls. */
.site-header .filters__tab {
  align-items: center;
  padding: 0;
  margin-bottom: 0;
  border-bottom: 2px solid transparent;
  color: #B9C7BC;
  font-size: 0.84375rem; /* 13.5px */
  font-weight: 500;
  flex: none;
  white-space: nowrap;
}

.site-header .filters__tab:hover {
  border-bottom-color: rgba(91, 168, 63, 0.45);
  color: var(--cream);
}

.site-header .filters__tab--current {
  border-bottom-color: var(--accent);
  color: var(--cream);
}

.site-header__account {
  display: flex;
  flex: none;
  align-items: center;
  gap: 1rem;
  margin-left: auto;
}

.site-header__user {
  color: #9FB0A4;
  font-size: 0.78125rem; /* 12.5px */
}

/* Sign out is the quiet button everywhere else in the app, but on the dark bar
   that button's light fill and dark ink would read as the primary action in the
   header. An outline in the bar's own greens instead. */
.site-header__account .button {
  padding: 6px 13px;
  border: 1px solid #35513F;
  background: transparent;
  color: var(--cream);
  font-size: 0.75rem; /* 12px */
  letter-spacing: normal;
  text-transform: none;
}

.site-header__account .button:hover {
  border-color: var(--accent);
  background: rgba(91, 168, 63, 0.14);
  color: var(--cream);
}

/* The header is the widest thing on a narrow screen, so it wraps rather than
   forcing the page to scroll sideways — the failure StylesheetTest exists to
   catch. The account block keeps its own line and the tabs scroll if they must.

   It used to take `order: -1` here, which put the signed-in email and Sign out
   button above the wordmark (round four S3). That reversed the reading order
   against the markup, which has the brand first — so a phone showed the logo
   underneath the account while a screen reader and the tab key still met it
   first. The wrap alone gives the account its own line, which was the whole
   intent; `flex-basis` is what claims that line, not a reordering. */
@media (max-width: 40rem) {
  .site-header__inner {
    flex-wrap: wrap;
    gap: 0.5rem 1rem;
  }

  /* `flex: none` and `margin-left: auto` both come from the desktop rule and
     both have to go: the first would ignore the basis below, the second would
     strand the email and Sign out against the right edge on a line of their
     own. Left-aligned under the wordmark is the order a phone reads in. */
  .site-header__account {
    flex: 1 0 100%;
    margin-left: 0;
  }

  .site-header .filters {
    flex-basis: 100%;
    overflow-x: auto;
  }
}

/* ---- Site footer ------------------------------------------------------- */

/* How to reach SettleUp, on every page. Two screens said "contact us" and
   neither said how; this is the answer, and it is why those lines can now be
   links. Quiet on purpose — it is a reference, not a call to action. */
/* A full-bleed bar on its own ground, as the design has it, rather than a rule
   under the page's measure. `margin-top: auto` is what pins it to the bottom of
   a short page — <body> is a flex column and <main> grows. */
.site-footer {
  margin-top: auto;
  border-top: 1px solid var(--rule);
  background: var(--surface-foot);
  color: var(--ink-muted);
  font-size: 0.75rem; /* 12px */
}

/* The inner row matches the header's: same measure, same side padding, so the
   two ends of the page line up with each other and with the content between. */
.site-footer__inner {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 20px;
  max-width: calc(var(--page-measure, var(--measure)) + 5rem);
  margin: 0 auto;
  padding: 20px 2.5rem;
}

.site-footer p {
  margin: 0;
}

/* The two halves wrap to their own lines on a narrow screen rather than
   breaking mid-address: "162 W Main St #305," above "Cartersville, GA 30120"
   reads as two addresses. The flex parent already wraps; this keeps each half
   whole when it does. */
.site-footer__who,
.site-footer__contact {
  min-width: max-content;
}

/* Below the wrap the two lines are stacked, and space-between then spreads
   them to both edges of a phone screen. Left-aligned is what the eye expects
   of a stacked block. */
@media (max-width: 40rem) {
  .site-footer__inner {
    justify-content: flex-start;
    gap: 8px;
  }
}

/* ---- Eyebrows ----------------------------------------------------------- */

/* A short accent rule then an uppercase label. The design puts one above every
   page title and every section heading, and it is the main thing that makes a
   screen look like it belongs to this system rather than to Bootstrap.

   The rule is a pseudo-element rather than a border so it can be its own length
   independent of the text beside it. */
.eyebrow {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 10px;
  color: var(--ink-muted);
  font-size: 0.65625rem; /* 10.5px */
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.eyebrow::before {
  content: "";
  flex: none;
  width: 26px;
  height: 2px;
  background: var(--accent);
}

/* On forest green the label goes light green: --ink-muted is 1.6:1 there. */
.eyebrow--on-dark {
  color: #7FC862;
  margin-bottom: 20px;
}

.eyebrow--on-dark::before {
  width: 34px;
}

/* ---- Auth pages -------------------------------------------------------- */

/* The sign-in screen only: a forest green panel beside the form. The other
   signed-out screens (password reset, invitation, sign-in link) are a single
   centred card — they are errands, not a front door, and a sales pitch above
   "choose a new password" would be strange.

   `flex: 1` rather than `min-height: 100vh`, now that the footer renders here
   too: <body> is already a 100vh flex column, so a 100vh child pushes the
   footer a full screen below the fold on the one page most likely to be read
   by somebody checking whether this site is genuine. Growing into the space
   <body> has left fills the viewport exactly as before, footer included. */
.auth-split {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  flex: 1;
}

.auth-split__panel {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: 2.5rem;
  padding: 52px 56px;
  background: var(--forest);
}

.auth-split__pitch {
  margin: 0 0 20px;
  color: var(--cream);
  font-family: var(--f-display);
  font-size: 2.875rem; /* 46px */
  font-weight: 500;
  line-height: 1.1;
  letter-spacing: -0.015em;
  text-wrap: balance;
}

.auth-split__blurb {
  max-width: 42ch;
  margin: 0;
  color: #B9C7BC;
  font-size: 0.9375rem; /* 15px */
  line-height: 1.65;
}

.auth-split__foot {
  margin: 0;
  color: #7E8F84;
  font-size: 0.75rem; /* 12px */
}

.auth-split__form {
  display: flex;
  align-items: center;
  justify-content: center;
  padding: 52px 56px;
}

/* Inside the split the card is the column, so it drops the border and the
   centring the standalone auth pages need. */
.auth-split .auth {
  width: 100%;
  max-width: 380px;
  padding: 0;
  border: 0;
  background: none;
}

/* A half-width dark panel below 52rem would push the form off the fold, so the
   columns stack and the panel keeps only its brand and pitch. */
@media (max-width: 52rem) {
  .auth-split {
    grid-template-columns: minmax(0, 1fr);
    min-height: 0;
  }

  .auth-split__panel {
    gap: 1.5rem;
    padding: 2rem 1.5rem;
  }

  .auth-split__pitch {
    font-size: 2rem; /* 32px */
  }

  .auth-split__foot {
    display: none;
  }

  .auth-split__form {
    padding: 2rem 1.5rem 3rem;
  }
}

/* The standalone signed-out card: password reset, invitation, sign-in link.

   `display: flex` and not for the layout, which is a plain column either way.
   The ERB leaves a newline between the last partial and `</main>`, and as a
   block that whitespace text node forms a line box *below* `.auth__help` — a
   22px strip of white under the tinted footer, on every one of these screens.
   A flex container discards whitespace-only children outright. (Grid does not:
   it gives them a row, which is why this is not `display: grid`.) */
.auth {
  display: flex;
  flex-direction: column;
  max-width: 26rem;
  margin: 3rem auto;
  padding: 2rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
}

/* The help block fills the card's full width and sits on its bottom edge, so
   the card gives up its own bottom padding and the block's padding becomes the
   edge. */
.auth:has(> .auth__help) {
  padding-bottom: 0;
}

/* On these screens `.auth` *is* the <main>, which `flex: 1 0 auto` tells to
   grow into the viewport — so the card stretched taller than its content and
   left a band of white below the tinted help block. A card is the size of what
   is in it; it is the page around it that should absorb the spare height. */
main.auth {
  flex-grow: 0;
}

.auth h1 {
  margin: 0 0 0.625rem;
  font-size: 2.125rem; /* 34px */
}

.auth__intro {
  margin: 0 0 1.875rem;
  color: var(--ink-muted);
  font-size: 0.84375rem; /* 13.5px */
  line-height: 1.6;
}

.auth__form {
  display: grid;
  gap: 1rem;
}

/* `align-content: start` because a `.field-row` stretches every field to its
   tallest neighbour, and a stretched grid hands the spare height to its tracks
   — so a hint under one select pushed the select beside it down, level with
   nothing. Invisible until a field grew a hint its neighbour lacked (F5). */
.field {
  display: grid;
  align-content: start;
  gap: 0.375rem;
}

/* Uppercase micro-label, which is what the design puts over every input. The
   checkbox rows opt out below — "Keep me signed in" is a sentence beside a box,
   not a label over a field, and setting it in caps reads as shouting.
   `.field__label` is the same micro-label over a group of radios, which has
   no single control to label. */
.field label,
.field__label {
  font-size: 0.6875rem; /* 11px */
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* Every control a `.field` can hold, not just `input`.
   
   Selects and textareas were styled only under `.fieldset`, which the funding
   request form uses — so the Role dropdown on the invite form, a `.field` with
   no fieldset around it, rendered as a raw 19px browser select at 13.3px beside
   two 46px styled inputs. Keying on `.field` is what `input` already did; the
   other two were the exception. */
.field input,
.field select,
.field textarea {
  width: 100%;
  padding: 12px 13px;
  /* The stronger of the two rules. A control's edge has to read against the
     white card it sits on, where --rule is for dividers *inside* one. */
  border: 1px solid var(--rule-strong);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.90625rem; /* 14.5px */
}

/* A <select> ignores author `line-height` — it computes `normal` whatever the
   rule says — so matching the input's font and padding still left it 43px
   against 46px. Stating the height the input arrives at is what closes it:
   one line at 1.5, plus the 0.625rem padding top and bottom, plus both
   borders. Not a magic number; the same arithmetic the input does implicitly. */
.field select {
  height: calc(1.5em + 24px + 2px);
}

.field textarea {
  resize: vertical;
}

.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible {
  outline: 2px solid var(--forest);
  outline-offset: 1px;
}

.field__hint {
  margin: 0;
  color: var(--ink-faint);
  font-size: 0.8125rem;
}

/* A checkbox and its label on one line, which `.field` alone cannot do: it is a
   grid whose controls are width: 100%, so a bare checkbox inside one stretches
   to the width of the form. The two-column grid keeps the box its own size and
   lets a label that wraps stay aligned under itself rather than under the box.

   The label also drops `.field label`'s muted colour and weight — those suit a
   caption above an input, but here the label *is* the sentence being agreed to,
   and a consent line has to read as body text rather than as a field name. */
.field--check {
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: 0.5rem;
}

.field--check input {
  width: auto;
  margin-top: 0.15rem;
}

.field--check label {
  font-size: 0.8125rem; /* 13px */
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: var(--ink);
}

/* Accent green fill, near-black text, uppercase with 0.09em tracking, square
   corners. The arrow the design puts on the right is a per-button decision
   rather than a rule here — see `.button__arrow`. */
.button {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  padding: 15px 24px;
  border: 1px solid transparent;
  border-radius: var(--radius);
  background: var(--accent);
  color: var(--on-accent);
  /* Stated, not inherited: a <button> takes neither the family nor the
     line-height from the page, so `a.button` and `button.button` sized
     themselves differently — 44.5px against 40px once the font matched. */
  font-family: inherit;
  font-size: 0.78125rem; /* 12.5px */
  line-height: 1.5;
  font-weight: 600;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  /* a.button would otherwise carry the underline links get */
  text-decoration: none;
  cursor: pointer;
}

/* The icons the design puts inside buttons. Sized in `em` so they track the
   button's own type — which is set at three different sizes across the app —
   and `aria-hidden` in the markup, since the label already says what the button
   does.

   The arrow trails a button that carries you forward to a next screen; the plus
   leads one that adds something to the screen you are on. Both appear on the
   request detail page, which is where that difference has to read. */
.button__arrow,
.button__plus {
  flex: none;
  width: 1.15em;
  height: 1.15em;
}

/* The gap either side comes from the button's own `gap`, so each icon only
   pulls back the slack at the button's edge. */
.button__arrow {
  margin-right: -2px;
}

.button__plus {
  margin-left: -2px;
}

/* A full-width button centres its label, and the arrow should sit beside the
   text rather than out at the right edge — the auth screens' submits are the
   only full-width buttons in the app. */
.auth > form > .button {
  justify-content: center;
}

/* Hover has to be visible at a glance, not a 10% opacity shift nobody notices —
   these buttons text clients and create advances, so knowing which one is under
   the cursor matters. Darkening the fill reads on any background; the shadow and
   the press-down confirm it.

   Keyed on the class rather than the element: button_to renders a <button> and
   form.submit an <input type=submit>, and scoping the interactive states to
   .auth__form's inputs left every other submit button without them — which is
   why Deny and Add note behaved differently from Start review and Approve. */
.button {
  transition: background 0.12s ease, border-color 0.12s ease, box-shadow 0.12s ease, color 0.12s ease;
}

.button:hover {
  background: var(--accent-hover);
  border-color: var(--accent-hover);
  color: var(--on-accent);
  box-shadow: 0 1px 3px rgba(23, 51, 43, 0.25);
}

.button:active {
  box-shadow: none;
  transform: translateY(1px);
}

/* Keyboard users get the same signal as mouse users, plus a ring — :hover alone
   leaves a tab-through invisible. */
.button:focus-visible {
  outline: 2px solid var(--forest);
  outline-offset: 2px;
}

@media (prefers-reduced-motion: reduce) {
  .button {
    transition: none;
  }

  .button:active {
    transform: none;
  }
}

.auth__aside {
  margin: 1.25rem 0 0;
  font-size: 0.875rem;
}

.auth__aside a {
  color: var(--ink-muted);
}

/* The mark above every unauthenticated form.

   Centred against left-aligned form fields on purpose: it is a masthead, not
   the first row of the form, and aligning it with the labels below made it read
   as a heading for the email field.

   The white box the mark sits in is invisible against the white card on these
   screens, which is correct rather than a bug — the box exists to carry the
   mark's black half over forest green, and there is no forest green here. */
/* Centred with auto margins rather than `text-align` on the card: the mark is
   the only centred thing on it, and centring the card's text meant every rule
   below had to undo it. */
.auth > .brand-mark {
  display: flex;
  margin: 0 auto 1.75rem;
}

/* A checkbox and its label on one line. `.field` is a grid of stacked label and
   control, which is right for every other field and wrong for this one — it put
   "Keep me signed in" above a full-width checkbox stretched to the form's
   width. */
.field--check {
  display: flex;
  align-items: center;
  gap: 0.5rem;
}

.field--check input[type="checkbox"] {
  /* Undoes `.field input { width: 100% }`, which a checkbox must not inherit. */
  width: auto;
  margin: 0;
  /* Large enough to hit without aiming. The browser default is ~13px, which is
     below the 24px minimum a pointer target should offer. */
  min-width: 1.125rem;
  min-height: 1.125rem;
  accent-color: var(--accent);
}

.field--check label {
  /* The label is the thing being read here, not a caption above a control, so
     it takes body colour rather than the muted grey the stacked fields use —
     and it stays sentence case, where the stacked fields' labels are uppercase
     micro-labels. "Keep me signed in" set in caps reads as shouting. */
  color: var(--ink);
  font-size: 0.8125rem; /* 13px */
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  cursor: pointer;
}

/* The sign-in link, offered beside the password rather than under it. Ruled off
   above so it reads as a second route in rather than a footnote to the form —
   at six sign-ins a year it is the ordinary way in for most firms. */
.auth__alt {
  margin: 1.5rem 0 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
  text-align: center;
  font-size: 0.9375rem;
}

/* A lone `button_to` standing in for the auth form, on the sign-in link's
   landing page. `button_to` renders its own <form>, so it sits outside
   `.auth__form`'s grid and its button came out shrink-to-fit — a small
   left-aligned control where every other auth page has a full-width one.
   The class goes on the <button>, not the wrapping <form>, which is the same
   trap the account bar's Sign out hit. */
.auth > form > .button {
  width: 100%;
}

/* "Forgot your password?" under the link route. Both are ways out of a form
   that is not working, so they read as one group: centred like the rule above
   them, and close enough to be a pair rather than two afterthoughts at
   different indents. `.auth__aside` keeps its own left-aligned spacing on the
   pages that carry it alone. */
.auth__alt + .auth__aside {
  margin-top: 0.5rem;
  text-align: center;
}

/* Who to call. Below everything, on the sunken surface, because it is the
   answer to a question the form could not solve — not a step in it.

   It bleeds to the card's left and right edges with negative margins, but
   **not** to the bottom one: a negative bottom margin does not pull a flex
   container's content edge up past the item's box, so the card drops its own
   bottom padding instead (see `.auth:has()` above) and this block supplies it. */
.auth__help {
  margin: 1.75rem -2rem 0;
  padding: 1.25rem 2rem;
  border-top: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-sunken);
  font-size: 0.875rem;
  color: var(--ink-muted);
}

/* Inside the split the card has no padding to bleed out to, so the negative
   margins above would hang the block over both edges of the column. */
.auth-split .auth__help {
  margin-right: 0;
  margin-bottom: 0;
  margin-left: 0;
  padding-right: 0;
  padding-left: 0;
  background: none;
}

.auth__help-lead {
  margin: 0;
}

.auth__help-contact {
  margin: 0.375rem 0 0;
}

/* ---- Form pages -------------------------------------------------------- */

.form-page {
  max-width: var(--measure);
  margin: 0 auto;
}

.form-page__intro {
  margin: 0;
  max-width: 44ch;
  color: var(--ink-muted);
}

.request-form {
  display: grid;
  gap: 1.5rem;
}

.fieldset {
  display: grid;
  gap: 1rem;
  margin: 0;
  padding: 24px 26px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
}

/* The section heading inside a card, with the accent rule the design puts
   before every one. A <legend> cannot be a flex container in every browser —
   its rendering is special-cased — so the rule is drawn by a pseudo-element on
   an inline-block rather than by `display: flex` as `.eyebrow` does it. */
.fieldset legend {
  display: inline-block;
  padding: 0;
  margin: 0 0 4px;
  color: var(--ink-muted);
  font-size: 0.65625rem; /* 10.5px */
  font-weight: 600;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

.fieldset legend::before {
  content: "";
  display: inline-block;
  width: 22px;
  height: 2px;
  margin-right: 12px;
  background: var(--accent);
  vertical-align: middle;
}

/* The underwriting block's rule is amber rather than green: it is the section
   that appears when a request crosses $5,000, and the design marks it as the
   one asking more of the firm. */
.fieldset--underwriting legend::before {
  background: var(--caution-rule-strong);
}

/* Paired inputs sit side by side on a wide screen and stack on a narrow one
   without a media query. */
.field-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem;
}

/* The browser's own [hidden] rule is a bare element default, so any class here
   that sets a `display` outranks it and the attribute stops hiding anything.
   That has already cost us twice — the conditional underwriting questions
   (.fieldset / .field) stayed on screen after the controller hid them, and the
   restored-draft notice (.form__restored) rendered on every load with a Dismiss
   button that could not dismiss it.

   So this is deliberately global rather than a third named selector: four
   controllers toggle `hidden`, and the next display-setting class would
   reintroduce the same bug silently. !important because the attribute is an
   explicit instruction to hide — nothing styling an element should outvote it. */
[hidden] {
  display: none !important;
}

/* The dollar sign sits inside the input's border so the prefix reads as part
   of the field rather than as a label beside it. */
.field__prefixed {
  display: flex;
  align-items: center;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
}

.field__prefixed:focus-within {
  outline: 2px solid var(--ink);
  outline-offset: 1px;
}

.field__prefix {
  padding: 0 0.25rem 0 0.75rem;
  color: var(--ink-muted);
}

.field__prefixed input {
  border: none;
  background: transparent;
  padding-left: 0;
}

.field__prefixed input:focus-visible {
  outline: none;
}

.errors {
  padding: 1rem 1.25rem;
  border: 1px solid var(--caution-rule);
  border-left-width: 3px;
  border-radius: var(--radius);
  background: var(--caution-bg);
  color: var(--caution);
}

/* The summary takes focus when a rejected form comes back, so it needs a ring
   like anything else that can hold focus — otherwise a sighted keyboard user
   sees the page move and cannot tell where they now are. */
.errors:focus-visible {
  outline: 2px solid var(--caution);
  outline-offset: 2px;
}

.errors__title {
  margin: 0 0 0.5rem;
  font-size: 0.9375rem;
  font-weight: 600;
}

.errors ul {
  margin: 0;
  padding-left: 1.25rem;
  font-size: 0.875rem;
}

.form-actions {
  display: flex;
  align-items: center;
  gap: 1rem;
}

/* The secondary action: an outline rather than a fill, so it reads as the
   quieter of two choices without competing with the primary one.
   This used to set only `color`, which left --ink-muted text on the inherited
   dark fill at 2.83:1 — under the 4.5:1 minimum, and hard to read in practice. */
.button--quiet {
  padding: 14px 20px;
  background: transparent;
  color: var(--forest);
  border-color: var(--forest);
}

.button--quiet:hover {
  background: var(--forest);
  border-color: var(--forest);
  color: var(--cream);
  box-shadow: none;
}

/* Deny texts a client and cannot be recalled, so it should not look like the
   mild secondary action it shared a style with. Outlined rather than filled —
   it is still the rarer choice and should not out-shout Approve — but in the
   caution colour, and the hover fills it so there is no doubt what is about to
   happen. */
.button--caution {
  background: var(--surface);
  color: var(--caution);
  border-color: var(--caution-rule);
}

.button--caution:hover {
  background: var(--caution);
  border-color: var(--caution);
  color: var(--surface);
  box-shadow: 0 1px 3px rgba(23, 51, 43, 0.2);
}

.button--caution:focus-visible {
  outline-color: var(--caution);
}

/* Required-field marker. Colour alone is never the signal — the glyph carries
   it, and the input's own `required` attribute carries it to assistive tech —
   so this is only reinforcement for people who can see it. */
.field__required {
  color: var(--caution);
  font-weight: 700;
  margin-left: 0.125rem;
}

.form__legend {
  margin: 0 0 1.25rem;
  font-size: 0.875rem;
  color: var(--ink-muted);
}

/* "We put back what you had typed." Informational rather than a problem, so it
   takes the settled palette rather than the caution one — a firm returning to
   their own work has not made a mistake. Tinted *and* ruled, because a print or
   a high-contrast mode drops the background and the message still has to read as
   a distinct thing rather than a stray line above the form. */
.form__restored {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  margin: 0 0 1.25rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--settled-rule);
  border-radius: var(--radius);
  background: var(--settled-bg);
  color: var(--settled);
  font-size: 0.875rem;
}

/* ---- Fields that need attention ---------------------------------------- */
/* Rails wraps an invalid field in .field_with_errors, which ships with no
   styling at all — so a rejected form showed a summary at the top and gave no
   clue which of a dozen fields it meant. On the full underwriting form that is
   a hunt.

   Colour is not the only signal: the summary at the top of the form names each
   field and gives the reason in words, and the label turns bold as well as red.
   This is reinforcement for people who can see it, not the only route. */
.field_with_errors {
  display: contents;
}

.field_with_errors label {
  color: var(--error);
  font-weight: 600;
}

.field_with_errors input,
.field_with_errors select,
.field_with_errors textarea {
  border-color: var(--error);
  background: var(--error-bg);
}

.field_with_errors input:focus-visible,
.field_with_errors select:focus-visible,
.field_with_errors textarea:focus-visible {
  outline-color: var(--error);
}

@media (forced-colors: active) {
  .field_with_errors input,
  .field_with_errors select,
  .field_with_errors textarea {
    border-color: revert;
    background: revert;
  }
}


/* ---- Table rows --------------------------------------------------------- */
/* No striping. The design's tables are white throughout, separated by a hairline
   rule, and the row under the cursor is what gets a fill — alternating bands
   would compete with the status pills, which are the thing in these rows that is
   supposed to be scannable by colour.

   The stripe was a scanning aid for a seven-column row, and what replaces it is
   the hover treatment below: a green wash plus a 3px accent marker on the first
   cell, which tracks one row rather than banding all of them. */
.queue__table tbody tr:hover,
.requests__table tbody tr:hover {
  background: var(--surface-hover);
}

/* The accent marker on the hovered row. The transparent border is on every row
   so nothing shifts sideways by 3px when the cursor arrives. */
.queue__table tbody td:first-child,
.requests__table tbody td:first-child {
  border-left: 3px solid transparent;
}

.queue__table tbody tr:hover td:first-child,
.requests__table tbody tr:hover td:first-child {
  border-left-color: var(--accent);
}

/* The hover fill is decoration. In forced-colors mode the system palette owns
   the background, and painting one here would fight it. The accent marker is
   left alone: a border is a shape, which forced-colors keeps. */
@media (forced-colors: active) {
  .queue__table tbody tr:hover,
  .requests__table tbody tr:hover {
    background: revert;
  }
}


/* ---- Focus ------------------------------------------------------------- */
/* Every interactive element needs a visible focus ring, not just the ones that
   look like buttons: a keyboard user tabbing through the app has no other way
   to tell where they are. These sit on varied backgrounds, so the ring is
   stated explicitly rather than left to the browser. */
.filters__tab:focus-visible,
.search__clear:focus-visible,
.detail__back a:focus-visible,
.empty a:focus-visible,
.site-header__wordmark:focus-visible,
.site-footer a:focus-visible,
.site-header__user + form button:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: var(--radius);
}

/* A tab is a navigation target, so hovering it should read as "you can go
   here" rather than as nothing at all. */
.filters__tab {
  transition: color 0.12s ease, border-color 0.12s ease;
}


/* ---- A firm's own funding requests -------------------------------------- */
/* The same shape as the admin queue, and deliberately so: it is a table to scan
   rather than prose. Its own classes because what it shows differs — a firm sees
   status and never SettleUp's commentary. */

.requests {
  max-width: var(--page-measure, var(--measure));
  margin: 0 auto;
}

.requests__scroll {
  overflow-x: auto;
}

.requests__table {
  width: 100%;
  border-collapse: collapse;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.requests__table th,
.requests__table td {
  padding: 15px 18px;
  text-align: left;
  /* The lighter of the two rules: the row divider sits inside the card border,
     and matching them made the table read as a grid rather than as rows. */
  border-bottom: 1px solid var(--rule-soft);
  vertical-align: top;
}

/* Uppercase micro-label on the raised surface, which is what separates the
   header from the white rows without a heavier rule. */
.requests__table th {
  padding: 14px 18px;
  background: var(--surface-raised);
  border-bottom: 1px solid var(--rule);
  font-size: 0.625rem; /* 10px */
  text-transform: uppercase;
  letter-spacing: 0.13em;
  color: var(--ink-muted);
  font-weight: 600;
}

.requests__table tr:last-child td {
  border-bottom: 0;
}

.requests__sub {
  display: block;
  font-size: 0.8125rem;
  color: var(--ink-muted);
}

/* "+1 other advance" under a client's name (round four F1), where the nesting
   used to say it with a heading. Smaller and tabular like the admin list's own
   marker, which says the same thing for the same reason — and sitting under the
   file number, so the line the firm actually came in holding stays first. */
.requests__related {
  margin-top: 2px;
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

/* The whole row opens the request, and the mechanism is a real anchor rather
   than a click handler on the <tr>. The View column it replaced held a button
   duplicating what clicking the row should already do, and the horizontal space
   is needed for search and, once item 24 lands, a matter number.

   **Three tables use this**: the firm's requests, the admin queue, and Signed
   agreements. The queue came second and for a sharper reason than tidiness: its
   eight columns needed 1072px inside a 1006px scroll container, so the Open
   button was clipped to a black sliver down the right edge and read as a stray
   border. Dropping the column is what makes the table fit; the anchor is how
   the row stays openable.

   Signed agreements came third (2026-09-11) and is the one that anchors
   somewhere else: its link sits on the signed date rather than a name, because
   the client name there is transcribed from the agreement and can be blank —
   and the row whose client could not be read is exactly the one somebody needs
   to open. Nothing about the rules below changes; only which cell carries the
   anchor.

   `position: relative` on the row plus an absolutely-positioned ::after on the
   link is what stretches one cell's anchor across the other four. The anchor
   stays where it is in the markup — on the client's name, which is what somebody
   is aiming at anyway — so it can be tabbed to, opened in a new tab, and read
   as a link by a screen reader. A JavaScript row can do none of those, and
   stops working entirely when the asset pipeline has a bad day.

   The row's existing :hover fill is what says it is clickable; nothing else
   needs to change. */
.requests__row {
  position: relative;
}

/* Deliberately NOT positioned. A `position: relative` here would make the
   anchor its own ::after's containing block, so `inset: 0` would resolve to the
   three words of the client's name instead of the row — the overlay measured
   108x19px and only the name was clickable, which is exactly how this was found
   in the browser. The row has to stay the nearest positioned ancestor. */
.requests__link {
  color: inherit;
  text-decoration: none;
  font-weight: 500;
}

.requests__link::after {
  content: "";
  position: absolute;
  /* Resolved against .requests__row, which is the nearest positioned ancestor
     precisely because the anchor above is not one. */
  inset: 0;
}

/* Underlined on hover and on focus, so the row's fill is not the only signal —
   a fill alone is invisible in forced-colors mode and to anyone who cannot
   distinguish it from the even-row stripe. */
/* A second control inside a clickable row — the agreement download on a
   client's other advances. Lifted above the stretched link's overlay, which
   otherwise covers the whole row and swallows the click. `position` alone is
   enough today, because the control comes after the row link in the DOM and
   so paints over it; the `z-index` keeps that true if a column is reordered. */
.requests__row-action {
  position: relative;
  z-index: 1;
}

.requests__link:hover,
.requests__link:focus-visible {
  text-decoration: underline;
}

/* The anchor carries a ring of its own, always. A keyboard user must never be
   left with no indicator, so this is the rule that has to survive on its own. */
.requests__link:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 2px;
  border-radius: var(--radius);
}

/* Where :has() is supported, the ring moves to the whole row — what is about to
   open is the row, not three words of a name. Written as a progressive upgrade
   rather than a replacement: dropping the anchor's own outline unconditionally
   would leave a browser without :has() showing no focus at all, which is worse
   than a ring in the wrong place. `outline-offset` is negative because the row
   spans the table's full width and a positive offset would draw outside the
   scroll container. */
@supports selector(:has(*)) {
  .requests__row:has(.requests__link:focus-visible) {
    outline: 2px solid var(--ink);
    outline-offset: -2px;
  }

  .requests__row:has(.requests__link:focus-visible) .requests__link:focus-visible {
    outline: none;
  }
}

/* ---- The requests list on a phone --------------------------------------- */

/* Below 40rem the table stops being a table and each row becomes a card.
   Trent's round three asked for this against a measurement: five columns need
   roughly 800px, so at 375px the list was ~500px of sideways dragging to read.
   The page never broke -- `.requests__scroll` kept it scrollable -- which is
   why it survived this long; it was merely unusable on the device partners
   actually check things from.

   **This is a restyle, not a second rendering.** The markup below is the same
   table, so the desktop list and the phone list cannot come to disagree about a
   row, and every controller test that asserts on `tbody tr` keeps working. The
   cell labels come from `data-label` in the ERB, because the header row is
   hidden here and a bare "$4,000.00" with nothing naming it is worse than the
   scroll was.

   Only the firm's table takes this. `.requests__row` and `.requests__link` are
   shared with the admin queue and Signed agreements, so every rule below is
   scoped under `.requests__table` -- SettleUp's own screens are used at a desk
   and are not part of this ask. */
@media (max-width: 40rem) {
  /* `display: block` on the table and its parts is what drops the column
     geometry. The elements keep their roles for assistive tech, which reads the
     table from the markup rather than from the layout. */
  .requests__table,
  .requests__table tbody,
  .requests__table tr,
  .requests__table td {
    display: block;
    width: 100%;
  }

  /* The header row is the one part with nothing to say here: its labels have
     moved into the cells. `visually-hidden`'s clip rectangle rather than
     `display: none`, so a screen reader still has the column names to associate
     cells with -- hiding them outright would leave five unlabelled cells. */
  .requests__table thead {
    position: absolute;
    width: 1px;
    height: 1px;
    margin: -1px;
    padding: 0;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
    border: 0;
  }

  /* The table's own border and radius move onto each card, so the list reads as
     a stack of cards rather than one long box with lines across it. */
  .requests__table {
    border: 0;
    border-radius: 0;
    background: none;
  }

  .requests__table tr {
    /* Still the overlay's containing block: `position: relative` comes from
       `.requests__row` above and must survive here, or tapping a card opens
       whatever row the overlay resolved against instead. The stylesheet test
       pins that rule; this comment is why nothing here may override it.

       A flex column so the cells can be reordered below without touching the
       markup: the table's column order is the right reading order across a row
       and the wrong one down a card. */
    display: flex;
    flex-direction: column;
    margin-bottom: 0.75rem;
    padding: 0.875rem 1rem;
    border: 1px solid var(--rule);
    border-radius: var(--radius);
    background: var(--surface);
  }

  .requests__table tr:last-child {
    margin-bottom: 0;
  }

  /* Label left, value right, on one line each. `gap` rather than
     `justify-content: space-between` alone so a long value wraps under its own
     label instead of colliding with it. */
  .requests__table td {
    display: flex;
    justify-content: space-between;
    align-items: baseline;
    gap: 1rem;
    padding: 0.3125rem 0;
    border-bottom: 0;
    text-align: left;
  }

  .requests__table td::before {
    content: attr(data-label);
    /* Never shrinks and never wraps: the label is the shorter half and the one
       that has to stay readable, so the value is what gives way. */
    flex: none;
    font-size: 0.75rem;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--ink-muted);
  }

  /* The value side, as one shrinkable box against the label.
     `min-width: 0` because a flex item's default `min-width: auto` refuses to
     shrink below its content -- which is what pushed a long status past the
     card's edge. `text-align: right` keeps the values on a common edge the way
     the column did.

     The Submitted cell is why this is a wrapper rather than a property on the
     cell: `received_on` renders the date and the time as two `display: block`
     spans, and in a flex cell each became its own flex item sitting beside the
     label -- 278px of content in a 258px box, which is the 3px of page overflow
     this whole media query exists to remove. Wrapped, they stack again. */
  .requests__table td > * {
    min-width: 0;
  }

  .requests__table td:not([data-label="Client"]) {
    text-align: right;
  }

  /* `received_on` puts the date and the time in two `display: block` spans, so
     in a flex cell they became two items beside the label rather than one
     stacked value. Restoring the stack means giving the date its own full line
     within the value side; `flex-basis: 100%` on the second span wraps it under
     the first, and the cell wraps rather than overflowing. */
  .requests__table td[data-label="Submitted"] {
    flex-wrap: wrap;
  }

  .requests__table td[data-label="Submitted"] .received__date {
    margin-left: auto;
  }

  .requests__table td[data-label="Submitted"] .received__time {
    flex-basis: 100%;
    text-align: right;
  }

  /* The client's cell is the card's heading: name on its own line, at heading
     size, with no label beside it. The name is what the card is *about*, and a
     "CLIENT" tag in front of it would say less than the name already does. */
  .requests__table td[data-label="Client"] {
    /* First in the card, whatever its column position. Trent's ask was "name
       and status on top", and it is also the only cell that says which client
       the card is about -- a card whose first line is a date makes the reader
       scan past it to find that out. The table keeps Submitted first because
       across a row the date is the scanning key; down a card it is a detail. */
    order: -1;
    display: block;
    padding-top: 0;
    padding-bottom: 0.5rem;
    font-size: 1.0625rem; /* 17px */
    font-weight: 600;
  }

  .requests__table td[data-label="Client"]::before {
    content: none;
  }

  /* The file number sits under the name as it does on the desktop row, but it
     is the one value here that keeps its own line rather than becoming a
     label/value pair -- it is part of naming the client, not a fact about the
     request. */
  .requests__table .requests__sub {
    font-weight: 400;
  }

  /* The value side of each row. Tabular figures so the amounts in a stack of
     cards line up on the decimal the way the column did. */
  .requests__table td:not([data-label="Client"]) {
    font-variant-numeric: tabular-nums;
  }

  /* Nothing to scroll once the columns are gone, and leaving `overflow-x: auto`
     on would keep a horizontal scrollbar that now has nothing to reach. */
  .requests__scroll {
    overflow-x: visible;
  }
}

/* ---- Admin: the incoming request queue --------------------------------- */

/* SettleUp's own screens, drawn on the same tokens as the firm-facing portal so
   the internal tool reads as the same product rather than something bolted on.
   The queue is wider than --measure: it is a table to scan, not prose to read. */

.visually-hidden {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* Hidden until tabbed to, then a real target sitting over the page. Not
   .visually-hidden with a focus override: this one has to take up space and
   be clickable once it appears, which a clip-path cannot undo cleanly.

   **Hidden by moving it up, not left.** `left: -9999px` put it 9999px outside
   the left edge, and overflow to the left grows the scrollable area in a
   left-to-right document exactly as overflow to the right does — so every page
   in the app scrolled sideways, whatever its content. It read as a too-wide
   table because the widest screens are the ones with tables on them, but the
   firm's 6-column list did it too, and so did sign-in.

   `top: -100%` with `left: 0` overflows upwards instead, which no browser adds
   to the scrollable area: CSS says overflow above the block-start edge is not
   reachable by scrolling. The element still occupies space and is still
   clickable once focused, which is the reason this is not `.visually-hidden`. */
.skip-link {
  position: absolute;
  left: 0;
  top: -100%;
  z-index: 10;
  padding: 0.5rem 1rem;
  background: var(--surface);
  color: var(--ink);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  text-decoration: none;
}

.skip-link:focus {
  top: 0;
  outline: 2px solid var(--ink);
  outline-offset: 2px;
}

.queue,
.detail {
  max-width: var(--page-measure, var(--measure));
  margin: 0 auto;
}

/* ---- Filters ---- */

.filters {
  display: flex;
  flex-wrap: wrap;
  gap: 30px;
  border-bottom: 1px solid var(--rule);
  margin-bottom: 1.25rem;
}

.filters__tab {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0 0 13px;
  color: var(--ink-muted);
  text-decoration: none;
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
  font-size: 0.875rem; /* 14px */
  font-weight: 500;
}

.filters__tab:hover {
  color: var(--ink);
}

/* The active tab is forest green — the darkest thing in the palette, against
   muted grey for the rest, so the selected section reads without needing a
   second signal. It was --link previously, which is now itself green and would
   have left the two nearly indistinguishable. */
.filters__tab--current {
  color: var(--forest);
  border-bottom-color: var(--forest);
  font-weight: 600;
}

/* The admin tabs' count badge. Square like everything else, and on the raised
   surface rather than the sunken one so it reads against the white card the
   tabs sit on. */
.filters__count {
  font-variant-numeric: tabular-nums;
  font-size: 0.8125rem;
  color: var(--ink-muted);
  background: var(--surface-raised);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 0 0.4rem;
}

/* Failed payoffs waiting on a phone call. Caution, the queue's colour for
   "ours to move", rather than error: nothing is broken, somebody needs to
   call a firm. The only nav badge that is not grey, because it is the only
   one that is a job rather than a tally. */
.filters__count--alert {
  color: var(--caution);
  background: var(--caution-bg);
  border-color: var(--caution-rule);
  font-weight: 600;
}

.filters__tab--current .filters__count {
  color: var(--forest);
  border-color: var(--rule-strong);
}

/* ---- Search ---- */

.search {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.5rem;
  margin-bottom: 1.5rem;
}

.search__label {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}

/* The same input the firm-facing form uses: `.field input` sets this border,
   padding and radius, and the search box was carrying none of it — a bare
   browser default sitting next to styled buttons, which is what astra read as
   unfinished. Matching the button's own 0.625rem vertical padding and 6px
   radius is also what makes the field and the button beside it line up. */
.search__field {
  flex: 1 1 18rem;
  min-width: 0;
  padding: 0.625rem 0.75rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  /* Safari renders type=search as a rounded capsule with its own inner padding
     and ignores the rest, so without this the box looks like a different
     control from every other input on the site. */
  -webkit-appearance: none;
  appearance: none;
}

.search__field::placeholder {
  color: var(--ink-faint);
}

.search__field:focus-visible {
  outline: 2px solid var(--ink);
  outline-offset: 1px;
}

/* Sits with the field rather than under it: it is an action on the search, not
   a third control in the row. */
.search__clear {
  color: var(--ink-muted);
  font-size: 0.875rem;
  white-space: nowrap;
}

.search__clear:hover {
  color: var(--ink);
}

/* Sits with Clear rather than as a third button: it acts on the search above
   it, and a filled button here would compete with Search itself. */
.search__export {
  font-size: 0.875rem;
  white-space: nowrap;
}

/* ---- The table ---- */

/* The table scrolls inside its own container so the page never scrolls
   sideways on a narrow screen. */
.queue__scroll {
  overflow-x: auto;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
}

.queue__table {
  width: 100%;
  border-collapse: collapse;
  font-size: 0.9375rem;
}

/* Tighter horizontally than the firm-facing table's 18px. This one runs to
   eight columns inside 68rem, and the wider gutter pushed "Harbor & Vance LLP"
   onto four lines — the row height is what a reviewer scans past, so the
   columns get the space instead of the gutters. */
.queue__table th,
.queue__table td {
  text-align: left;
  padding: 14px 12px;
  vertical-align: top;
  border-bottom: 1px solid var(--rule-soft);
}

/* Headers wrap. They used to be `nowrap`, which made the *header* the width
   floor for its whole column rather than the data: "FIRM INSTRUCTION" held 153px
   open against 85px of content, and the eight together needed 1107px inside a
   1086px container — so the table scrolled sideways while every cell in it had
   room to spare. Two words on two lines costs one row of header height and
   saves 21px across the table. */
.queue__table thead th {
  padding: 13px 12px;
  font-size: 0.625rem; /* 10px */
  letter-spacing: 0.13em;
  text-transform: uppercase;
  color: var(--ink-muted);
  font-weight: 600;
  background: var(--surface-raised);
  border-bottom: 1px solid var(--rule);
}

.queue__table tbody tr:last-child th,
.queue__table tbody tr:last-child td {
  border-bottom: 0;
}

.queue__table .numeric {
  text-align: right;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* The case question's answer on a queue row, when it went against the match. */
.queue__sub--flag {
  color: var(--caution);
  font-weight: 600;
}

/* The second line under firm, client and received: the detail that answers
   "which of these do I pick up next" without opening anything. */
.queue__sub,
.facts__sub {
  display: block;
  color: var(--ink-faint);
  font-size: 0.8125rem;
  margin-top: 0.125rem;
  /* Reset for a sub-line under a figure, which would otherwise inherit the
     display face and its tracking: it is a note about the number, not part
     of it. */
  font-family: var(--f-body);
  font-weight: 400;
  letter-spacing: normal;
}

.received__date {
  display: block;
  white-space: nowrap;
}

.received__time {
  display: block;
  color: var(--ink-faint);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

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

/* Both admin lists render one page of rows. The control sits below the table
   rather than above it: these screens are scanned top to bottom, so somebody
   reaching the end of a page is where the next link is wanted. */
.pagination {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  margin-top: 16px;
  flex-wrap: wrap;
}

.pagination__link {
  font-size: 0.875rem;
  white-space: nowrap;
}

/* Held in the layout rather than removed, so Previous and Next do not swap
   places as a reviewer pages through — a moving target is harder to click than
   a dead one. */
.pagination__link--disabled {
  color: var(--ink-faint);
  cursor: default;
}

/* The position reads as a caption, not a control. Centred so it stays put
   between the two links as their widths change. */
.pagination__status {
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.875rem;
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.pagination__page {
  color: var(--ink-faint);
}

/* ---- Badges ---- */

/* The status pills. Uppercase at 10.5px with 0.07em tracking, square, and a
   fill rather than an outline — the design gives each one a background/text
   pair and no border, so the `border` below stays transparent and exists only
   to keep the badges the same height as the bordered `.tier` chips beside them
   in the admin table. */
.tier,
.status {
  display: inline-block;
  font-size: 0.65625rem; /* 10.5px */
  font-weight: 600;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  padding: 4px 10px;
  border-radius: var(--radius);
  white-space: nowrap;
  border: 1px solid transparent;
}

/* Fast track carries no underwriting answers to read, which is a fact about
   the row worth seeing before opening it — so the two tiers are told apart by
   weight, not colour: neither is a warning. */
.tier--fast_track {
  background: var(--surface-sunken);
  color: var(--ink-muted);
  border-color: var(--rule);
}

.tier--full_underwriting {
  background: var(--surface-sunken);
  color: var(--ink);
  border-color: var(--ink-faint);
}

/* Submitted and in review are both "with SettleUp, nothing needed from you",
   so they share a treatment rather than implying a difference the firm can act
   on. Amber, from the design's Submitted pill. */
.status--submitted,
.status--in_review {
  background: var(--pill-submitted-bg);
  color: var(--pill-submitted-ink);
}

.status--approved {
  background: var(--pill-approved-bg);
  color: var(--pill-approved-ink);
}

/* Funded is the one pill the design fills with forest green rather than a
   tint. It is the end state a firm is looking for down a column of rows, and
   the solid dark chip is what makes it findable at a glance. */
.status--funded {
  background: var(--pill-funded-bg);
  color: var(--pill-funded-ink);
}

/* Declined at the firm's own request is not a refusal: the firm chose this,
   and A6 asks for it to read neutral rather than alarming. Grey rather than
   the blue it carried before -- see the token for why blue was wrong too. */
.status--declined_at_firm_request {
  background: var(--pill-declined-bg);
  color: var(--pill-declined-ink);
  border-color: var(--rule);
}

/* **Needs your answer** -- a request SettleUp opened from the client's call
   that the firm has not answered (A8). The one status on the page that is a
   question addressed to the reader rather than a state of the record, and the
   only one drawn to be seen first. */
.status--awaiting_firm {
  background: var(--pill-answer-bg);
  color: var(--pill-answer-ink);
}

/* Paid off: an outline rather than a fill, so it recedes. The border is the
   whole chip here, which is why it names a colour rather than inheriting the
   transparent one every other pill starts with. */
.status--paid_off {
  background: var(--pill-paid-off-bg);
  color: var(--pill-paid-off-ink);
  border-color: var(--pill-paid-off-ink);
}

/* Denied and withdrawn are the closed-with-nothing-funded states. Grey, which
   is the absence of the other four rather than a colour of its own. */
.status--denied,
.status--withdrawn {
  background: var(--surface-raised);
  color: var(--ink-muted);
  border-color: var(--rule);
}

/* An account that can sign in. Green like `approved` and `funded` elsewhere:
   the column is read by an admin asking "who has access", and the ordinary
   answer should be the one that needs no thought.

   It carries a border where --settled-bg alone would not, because this badge
   sits directly above and below the grey `off` and amber `pending` ones and a
   borderless chip in a column of bordered ones reads as a different kind of
   thing rather than a different value.

   5.8:1 on its own tint in light mode and 8.2:1 in dark, both measured; the
   tokens are the ones `approved`/`funded` already use rather than new values. */
.status--on {
  background: var(--settled-bg);
  color: var(--settled);
  border-color: var(--settled-rule);
}

/* Revoked access. Reads as "stopped" rather than "failed" — a deactivated
   account is an ordinary decision, not an error state. */
.status--off {
  background: var(--surface-sunken);
  color: var(--ink-muted);
  border-color: var(--rule);
}

/* An invitation nobody has accepted. The same caution treatment as "with
   SettleUp, waiting" elsewhere: something is outstanding, nothing is wrong. */
.status--pending {
  background: var(--caution-bg);
  color: var(--caution);
  border-color: var(--caution-rule);
}

/* ---- Firm and user management ------------------------------------------ */

/* A deactivated row stays legible rather than being greyed into unreadability:
   the admin is often here *because* someone lost access, so the row they came
   to find must not be the hardest one to read. */
.queue__table tr.is-inactive td {
  color: var(--ink-muted);
}

.queue__aside {
  margin: 0 0 1.5rem;
  font-size: 0.875rem;
}

.queue__note {
  margin: 0 0 1.5rem;
  max-width: 60ch;
  color: var(--ink-muted);
  font-size: 0.875rem;
  line-height: 1.5;
}

.queue__section {
  margin: 2.5rem 0 1rem;
  font-size: 1.125rem;
  font-weight: 620;
}

/* Row actions get no vertical offset at all, which took three attempts to
   arrive at.

   Cells are `vertical-align: top`, so every cell's content starts at the same
   12.5px inset from the row's top edge: the date, the badges, the buttons. That
   shared top edge is what the eye reads as "these belong to one row". Pulling
   the button up to centre its *label* on the first line of text broke it — the
   button's box then started 11px above every badge beside it, which is visible
   at a glance and was reported as the buttons not lining up.

   Two earlier attempts made it worse before this: `display: flex` on these
   classes stopped the `<td>` being a table cell, so it no longer stretched to
   the row height and its bottom border sat 55px high; a negative `margin-top`
   on the cell was the same mistake one step smaller. The cell is a table cell
   and its content is left where the row puts it. */
.queue__row-actions {
  text-align: right;
  white-space: nowrap;
}

/* A row's action is the small button, in every table.
   
   The full-size button is 44.5px against a 22.5px line of text, and something
   has to give: align its box with the badges beside it and its *label* sits
   11px below every other column's text; align the label and the box rides above
   the badges. Neither is fixable by nudging, because the mismatch is the
   button's own height.
   
   The small button is 33px, which halves the discrepancy in both directions and
   takes 20px off every row (90px to 70px on the staff table, which was already
   using it). Scoped to `td` so the page-header actions on a firm's page — which
   reuse `.queue__actions` outside any table — keep their full size, where they
   are the primary control rather than one row's affordance. */
td.queue__actions .button,
td.queue__row-actions .button {
  padding: 7px 11px;
  font-size: 0.6875rem; /* 11px */
  /* The uppercase tracking that gives a full-size button its shape costs real
     width on a one-word label in a table that is already eight columns wide —
     "Open" at 0.09em is 4px wider than the column can spare. */
  letter-spacing: 0.04em;
}

.button--small {
  padding: 7px 11px;
  font-size: 0.6875rem; /* 11px */
  letter-spacing: 0.04em;
}

/* Deactivation is separated from the header's ordinary actions, so "Invite
   user" and "Deactivate firm" are never adjacent. Both are one click; only one
   of them signs out an entire firm. */
.danger-zone {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  align-items: center;
  justify-content: space-between;
  margin: 1.5rem 0 0;
  padding: 1rem 1.25rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-sunken);
}

.danger-zone__note {
  margin: 0;
  max-width: 60ch;
  color: var(--ink-muted);
  font-size: 0.875rem;
  line-height: 1.5;
}

.stacked-form {
  display: grid;
  gap: 1.5rem;
  max-width: 32rem;
}

.stacked-form__actions {
  display: flex;
  gap: 0.75rem;
  align-items: center;
}

/* ---- Request detail ---- */

.detail__back {
  margin-bottom: 1rem;
  font-size: 0.875rem;
}

.detail__badges {
  display: flex;
  gap: 0.5rem;
  align-items: center;
}

.detail__pending {
  background: var(--surface-sunken);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--caution-rule);
  border-radius: var(--radius);
  padding: 0.75rem 1rem;
  margin: 0 0 1.5rem;
  color: var(--ink-muted);
  font-size: 0.875rem;
}

/* What to do about the box above, where there is something to do (round four
   F2). Joined to it rather than boxed again: the explanation says the case
   update is wanted and this is how to give it, so two bordered cards would
   split one thought in half. The margin above is collapsed to zero and the
   pending box loses its own, so the pair reads as one block. */
.detail__pending:has(+ .detail__answer) {
  margin-bottom: 0;
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.detail__answer {
  background: var(--surface-sunken);
  border: 1px solid var(--rule);
  border-top: 0;
  border-left: 3px solid var(--caution-rule);
  border-radius: 0 0 var(--radius) var(--radius);
  padding: 0 1rem 1rem;
  margin: 0 0 1.5rem;
}

.detail__answer p {
  margin: 0 0 0.875rem;
  color: var(--ink-muted);
  font-size: 0.875rem;
}

.panel {
  background: var(--surface);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  padding: 24px 26px;
  margin-bottom: 1rem;
}

/* The card's section heading, carrying the same accent rule the form's
   fieldset legends do — the design puts one before every section heading, and
   these are the detail page's sections. */
.panel h2 {
  display: flex;
  align-items: center;
  gap: 12px;
  margin: 0 0 20px;
  font-size: 0.65625rem; /* 10.5px */
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-muted);
  font-weight: 600;
}

.panel h2::before {
  content: "";
  flex: none;
  width: 22px;
  height: 2px;
  background: var(--accent);
}

/* The funding box, and the design's one exception to the plain white card: an
   accent top border and a fill barely off white, so the card carrying the money
   reads as the live one rather than as the third of five identical boxes. The
   border replaces the top rule rather than sitting on top of it, so the card
   keeps the same outer dimensions as its neighbours. */
.panel--funding {
  border-color: #C6DDBB;
  border-top: 3px solid var(--accent);
  background: #FCFDFB;
}

/* An action on a panel, currently "Correct contact details" on a request.
   A sibling of the h2 rather than a child of it: the heading is the outline a
   screen reader navigates by, and folding the action into it would make this
   section's name "Client Correct contact details". `.health__action` sits
   outside its heading for the same reason.

   The panel heading carries a 20px bottom margin, so this pulls up into that
   gap to sit with the heading rather than opening a third band of space above
   the facts. */
.panel__action {
  display: inline-block;
  margin: -14px 0 16px;
  font-size: 0.8125rem; /* 13px */
  font-weight: 500;
}

.panel__prose {
  margin: 0;
  color: var(--ink);
}

.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 1rem 1.5rem;
  margin: 0;
}

.facts > div {
  min-width: 0;
}

/* Uppercase micro-label over its value, the same treatment the form's field
   labels get — these are the read-only version of the same thing. */
.facts dt {
  font-size: 0.65625rem; /* 10.5px */
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-muted);
  margin-bottom: 5px;
}

.facts dd {
  margin: 0;
  font-size: 0.9375rem; /* 15px */
  overflow-wrap: break-word;
}

/* A dollar figure, which the design sets in the display face — one of the three
   places Playfair is allowed. */
.facts__figure {
  font-family: var(--f-display);
  font-size: 1.375rem; /* 22px */
  font-weight: 500;
  letter-spacing: -0.015em;
  font-variant-numeric: tabular-nums;
}

.answer--missing {
  color: var(--ink-faint);
}

/* Attaching an emailed agreement under "Signed agreement" on the advance
   page: label, file picker and button stacked in the one facts cell. */
.inline-upload {
  display: grid;
  gap: 0.5rem;
  justify-items: start;
  margin-top: 0.5rem;
}

.inline-upload input[type="file"] {
  max-width: 100%;
  font-size: 0.875rem;
}

@media (max-width: 40rem) {
  .queue__table th,
  .queue__table td {
    padding: 0.625rem 0.75rem;
  }
}


/* ---- Admin actions, activity, and notes --------------------------------- */

.actions__row {
  display: flex;
  gap: 0.5rem;
  flex-wrap: wrap;
  margin-bottom: 1rem;
}

.actions__note {
  margin: 0.5rem 0 1rem;
  font-size: 0.875rem;
  color: var(--ink-muted);
}

.actions__label {
  display: block;
  font-size: 0.875rem;
  margin-bottom: 0.25rem;
}

/* The amount on the approve step. Sized like the reason field below it rather
   than the firm form's inputs, which take their padding from `.field`. */
.approve-amount input {
  width: 100%;
  padding: 0.5rem 0.5rem 0.5rem 0;
  font: inherit;
}

.actions__reason,
.notes__field {
  width: 100%;
  padding: 0.5rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font: inherit;
  resize: vertical;
}

/* Recording a payment received outside the portal. */
.manual-payment {
  margin-top: 1.25rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}

.manual-payment__fields {
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  margin-bottom: 0.75rem;
}

.manual-payment__fields input {
  padding: 0.5rem;
  font: inherit;
}

/* Raising a failed payoff's invoice again (W4): the address and the button
   on one line, since the address is the only thing to check before sending. */
.reraise {
  margin-top: 1rem;
  padding-top: 0.75rem;
  border-top: 1px solid var(--caution-rule);
}

.reraise__row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  align-items: center;
}

.reraise__field {
  flex: 1 1 16rem;
  padding: 0.5rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  font: inherit;
}

.actions__deny,
.actions__withdraw {
  padding-top: 0.75rem;
  border-top: 1px solid var(--rule);
}

.actions__withdraw {
  margin: 1rem 0;
}

/* A subhead *inside* a panel, so it has to read as smaller than the panel's own
   h2 rather than larger. It was 13px/700 against `.panel h2`'s 12px/600, which
   put "Activity" ahead of the "Decision" heading it sits under and level with
   "Internal notes" next door — the nesting said one thing and the type said the
   opposite. Same size and weight as the panel title, one step lighter in colour:
   subordinate without introducing a fourth eyebrow size. */
.actions__subhead {
  margin: 1.5rem 0 0.5rem;
  font-size: 0.75rem;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  color: var(--ink-faint);
}

/* Each row is one thing that happened, with what is known about it beside. A
   status nobody has confirmed is muted rather than absent, so "sent" and
   "arrived" do not read alike. */
.activity,
.notes {
  list-style: none;
  margin: 0;
  padding: 0;
}

.activity li {
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  gap: 0.5rem;
  padding: 0.5rem 0;
  border-bottom: 1px solid var(--rule);
  font-size: 0.875rem;
}

.activity li:last-child {
  border-bottom: 0;
}

.activity__when {
  color: var(--ink-muted);
}

.notes__item {
  padding: 0.75rem;
  margin-bottom: 0.5rem;
  background: var(--surface-sunken);
  border: 1px solid var(--rule);
  border-radius: var(--radius);
}

.notes__body {
  margin: 0;
  white-space: pre-wrap;
}

.notes__meta {
  margin: 0.25rem 0 0;
  font-size: 0.8125rem;
  color: var(--ink-muted);
}

.notes__form {
  display: grid;
  gap: 0.5rem;
  justify-items: start;
}

/* "Earlier contact — 3" above an advance's contact history: a label for the
   list, set like a facts label so it reads as one rather than as a heading
   competing with the panel's own. */
.notes__heading {
  margin: 1.25rem 0 0.5rem;
  font-size: 0.65625rem; /* 10.5px */
  font-weight: 600;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--ink-muted);
}

/* The latest contact on a Follow-ups row carries a quoted note, which would
   otherwise take the table's whole spare width as one long line. */
.follow-ups__contact,
.follow-ups__text {
  max-width: 22rem;
}

/* What to do after approving. The portal does not send the agreement — that
   stays in DocuSign — so this is instructions rather than controls, and is
   styled as a quiet aside rather than something clickable. */
.next-step {
  margin-top: 1rem;
  padding: 0.75rem 1rem;
  background: var(--surface-sunken);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--caution-rule);
  border-radius: var(--radius);
}

/* Wraps a `.stage` badge and nothing else, so the badge sets the size, colour
   and casing. The font-size and letter-spacing that used to sit here never
   rendered — removed rather than left looking like a fourth eyebrow size.
   `font-size: inherit` keeps it off the browser's own h3 scale, so if this ever
   gains text of its own it starts from the panel's size rather than at 18.7px. */
.next-step__head {
  margin: 0 0 0.5rem;
  font-size: inherit;
  font-weight: inherit;
}

/* An agreement already back for this request (W2): the work has moved to
   Signed agreements, so the block says so in the caution wash and carries the
   way there. */
.next-step--back {
  background: var(--caution-bg);
  border-left-color: var(--caution-rule-strong);
}

.next-step--back .next-step__head {
  font-weight: 700;
}

.next-step--back .button {
  margin-top: 0.75rem;
}


/* ---- Request stage ------------------------------------------------------ */

/* Where a request stands, on the queue and on the detail page. One vocabulary
   rendered at two sizes -- RequestStage computes it, so the two screens cannot
   disagree.

   Colour carries only two distinctions, not nine: whether the next move is
   ours, and whether anything is expected at all. Nine different badge colours
   would be a legend to memorise rather than a queue to skim. */
/* Same geometry as the firm-facing `.status` pills, but **not** uppercased:
   these labels carry SettleUp's own capitalisation ("Needs SettleUp"), and
   text-transform would flatten it to NEEDS SETTLEUP. The pill shape is what
   makes them read as the same kind of thing; the casing is the label's own. */
.stage {
  display: inline-block;
  font-size: 0.71875rem; /* 11.5px */
  font-weight: 600;
  letter-spacing: 0.02em;
  padding: 4px 10px;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-raised);
  color: var(--ink-muted);
  white-space: nowrap;
}

/* Ours to move. The only rows a reviewer should feel prompted to pick up. */
.stage--needs_settleup,
.stage--in_review,
.stage--awaiting_agreement,
.stage--ready_to_fund,
/* `stuck` is a signed agreement that did not read (W1): the same weight,
   because both are a row that cannot move until somebody fixes something. */
.stage--payment_failed,
.stage--stuck {
  border-color: var(--caution-rule);
  background: var(--caution-bg);
  color: var(--caution);
}

/* Money is out and the advance is live. */
.stage--funded,
.stage--paid_off {
  border-color: var(--settled-rule);
  background: var(--settled-bg);
  color: var(--settled);
}


/* The next action, stated plainly above whatever detail follows it. */
.next-step__action {
  margin: 0.5rem 0 0;
  font-size: 0.9375rem;
  font-weight: 550;
}


/* ---- The firm's "do not fund" instruction ------------------------------- */

/* Stated above the buttons rather than left in the Request panel below: this
   is the one request where approving overrides what the firm asked for, and a
   reviewer should meet the instruction before the button, not after. Amber
   rather than red -- a "do not fund" request is ordinary and expected, and
   denying it is the normal outcome. Red is for what has gone wrong. */
.override {
  margin-bottom: 1rem;
  padding: 0.75rem 1rem;
  border: 1px solid var(--caution-rule);
  border-left-width: 3px;
  border-radius: var(--radius);
  background: var(--caution-bg);
  color: var(--caution);
}

.override__head {
  margin: 0 0 0.375rem;
  font-size: 0.875rem;
  font-weight: 600;
}

.override__body {
  margin: 0;
  font-size: 0.8125rem;
  line-height: 1.5;
}

/* The phrase that unlocks the override. Stacked rather than inline: the label
   carries the words to type, so it is a sentence to read rather than a caption
   beside a control. */
.override__confirm {
  margin-bottom: 0.75rem;
}

.override__label {
  display: block;
  margin-bottom: 0.375rem;
  font-size: 0.8125rem;
  color: var(--caution);
}

.override__field {
  width: 100%;
  max-width: 24rem;
  padding: 0.4rem 0.6rem;
  border: 1px solid var(--caution-rule);
  border-radius: var(--radius);
  background: var(--surface);
  color: var(--ink);
  font: inherit;
  font-size: 0.9375rem;
}

.override__field:focus-visible {
  outline: 2px solid var(--caution);
  outline-offset: 1px;
}

/* The button while the phrase is unmet. Muted rather than hidden -- a reviewer
   has to see what typing the sentence is going to enable, or the field reads as
   an unexplained obstacle. */
.actions__approve .button[disabled] {
  opacity: 0.5;
  cursor: not-allowed;
}

/* The queue cell. Same amber, sized like the tier badge beside it so the row
   reads as one scannable line. */
.instruction--deny {
  display: inline-block;
  font-size: 0.75rem;
  font-weight: 600;
  padding: 0.15rem 0.5rem;
  border: 1px solid var(--caution-rule);
  border-radius: var(--radius);
  background: var(--caution-bg);
  color: var(--caution);
  white-space: nowrap;
}

/* ---- Envelope match confirmation ---------------------------------------- */

/* The candidate list on the confirmation screen. Each row is a request the
   agreement might belong to, with the reasons it was suggested, and the button
   that files it. Deliberately a list rather than a table: the reasons are prose
   and a person reads them before choosing. */
.candidates {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
}

.candidates__item {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-sunken);
}

.candidates__detail {
  min-width: 14rem;
  flex: 1 1 20rem;
}

.candidates__item h3 {
  margin: 0 0 0.25rem;
  font-size: 1rem;
}

.candidates__meta,
.candidates__reasons {
  margin: 0;
  font-size: 0.875rem;
  color: var(--ink-muted);
}

.candidates__reasons {
  margin-top: 0.25rem;
}

/* The attorney the firm named on the request. Carries the ink the rest of the
   row's facts use rather than the muted tone of the metadata line, because
   where two candidates tie this is the field that separates them. */
.candidates__attorney {
  margin: 0.25rem 0 0;
  font-size: 0.875rem;
  color: var(--ink);
}

/* Said on the row as well as above the list: somebody scanning for the solid
   button reaches a row without having read the paragraph. */
.candidates__tie,
.candidates__amount {
  margin: 0.375rem 0 0;
  font-size: 0.8125rem;
  color: var(--caution);
}

/* A statement that the page cannot answer something and the reader must. Same
   caution rule as .panel--warning, one level down — it qualifies a list rather
   than replacing it. */
.detail__warning {
  margin: 0.75rem 0 0;
  padding: 0.625rem 0.75rem;
  border-left: 3px solid var(--caution-rule);
  background: var(--caution-bg);
  color: var(--caution);
  font-size: 0.875rem;
}

/* An agreement whose figures did not read. Carries the caution rule the pending
   notice uses, because it is the same kind of statement: something is blocked
   and here is what would unblock it. */
.panel--warning {
  border-left: 3px solid var(--caution-rule);
}

.problems {
  margin: 0.5rem 0 0;
  padding-left: 1.25rem;
  color: var(--ink-muted);
  font-size: 0.875rem;
}

.problems li + li {
  margin-top: 0.25rem;
}

/* ---- System health panel ------------------------------------------------ */

/* 0.9375rem, not the 0.9rem this carried: every other size in the file lands on
   a whole pixel at the 16px root, and 14.4px was the only one that did not. */
.health__intro,
.health__footer {
  color: var(--ink-muted);
  font-size: 0.9375rem;
  max-width: var(--measure);
}

.health__footer {
  margin-top: 2rem;
  padding-top: 1rem;
  border-top: 1px solid var(--rule);
}

.health {
  list-style: none;
  margin: 1.5rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.75rem;
  grid-template-columns: repeat(auto-fill, minmax(20rem, 1fr));
}

.health__signal {
  background: var(--surface);
  border: 1px solid var(--rule);
  /* The state colour rides the left edge rather than filling the card: a grid
     of saturated panels is unreadable, and the point is that the one thing
     wrong stands out from the ones that are fine. */
  border-left: 4px solid var(--rule);
  border-radius: var(--radius);
  padding: 1rem 1.125rem;
  /* A grid item's min-width is its content's, so a long value widened the
     card past its column instead of wrapping. */
  min-width: 0;
}

.health__heading {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
}

.health__heading h2 {
  font-size: 0.9375rem;
  margin: 0;
  flex: 1;
}

/* The verdict in words, beside the colour. Never the only signal and never
   omitted: colour alone fails anyone who cannot see the difference. */
.health__verdict {
  font-size: 0.75rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  white-space: nowrap;
  color: var(--ink-muted);
}

.health__state {
  width: 0.625rem;
  height: 0.625rem;
  border-radius: var(--radius);
  background: var(--ink-faint);
  flex: none;
}

.health__value {
  font-size: 1.5rem;
  font-variant-numeric: tabular-nums;
  margin: 0.5rem 0 0;
  /* Some values are addresses ("office@settleupfunding.com"), which have no
     space to break at and ran past the card's edge. */
  overflow-wrap: anywhere;
}

.health__description,
.health__measured {
  color: var(--ink-muted);
  font-size: 0.8125rem;
  margin: 0.375rem 0 0;
}

.health__detail {
  margin: 0.75rem 0 0;
  padding: 0.625rem 0 0;
  border-top: 1px solid var(--rule);
  list-style: none;
  font-size: 0.8125rem;
  color: var(--ink-muted);
  display: grid;
  gap: 0.25rem;
  /* Envelope ids and client names are long and must not stretch the grid
     column; they wrap rather than forcing a horizontal scroll. */
  overflow-wrap: anywhere;
}

/* The one action on this panel. Sits below whatever the row already shows,
   so the card still reads top-down as state first and action second. */
.health__action {
  margin-top: 0.75rem;
}

.health__signal--alarm {
  border-left-color: var(--error);
}

.health__signal--alarm .health__state {
  background: var(--error);
}

.health__signal--alarm .health__verdict {
  color: var(--error);
}

.health__signal--caution {
  border-left-color: var(--caution);
}

.health__signal--caution .health__state {
  background: var(--caution);
}

.health__signal--caution .health__verdict {
  color: var(--caution);
}

.health__signal--ok {
  border-left-color: var(--settled);
}

.health__signal--ok .health__state {
  background: var(--settled);
}

/* Unknown keeps the neutral rule it inherits: "we could not look" must not
   read as either healthy or broken. */

/* ---- Print ------------------------------------------------------------- */

/* Firms print payoff figures and request detail pages to file with a case. Law
   firms print constantly, so this is not a nicety.

   One rule governs most of what follows: **backgrounds are commonly dropped**,
   so anything whose only signal is a fill needs a border instead — that is why
   the status badge and the pending notice get one. The forest-green header bar
   and the accent-green buttons are the same problem at full strength, and they
   are hidden rather than bordered: neither is usable on paper. */
@media print {
  body {
    padding: 0;
    background: #fff;
    /* Forced rather than inherited. --ink is a dark green, which prints as a
       muddy grey on a monochrome printer; black is what a filed copy wants. */
    color: #000;
  }

  /* Navigation and actions. None of it can be used on paper, and the filled
     buttons print as black boxes or, once backgrounds go, empty outlines.
     `.site-header` covers the wordmark, the section tabs and Sign out together. */
  .skip-link,
  .site-header,
  .site-footer,
  .flash,
  .detail__back,
  .filters,
  .search,
  .form-actions,
  .client__actions,
  .queue__actions,
  .requests__actions {
    display: none;
  }

  /* A card or a panel split across a page break is the one layout failure that
     makes a printed figure hard to trust. `.panel` matters as much as `.client`:
     the request detail page renders its funding card inside a panel, so the
     original rule missed the page this was most needed on. */
  .client,
  .panel {
    break-inside: avoid;
  }

  .client__payoff-note {
    color: #000;
  }

  /* Panel headings are 0.75rem uppercase --ink-faint on screen, which is right
     there and almost invisible on a laser printer. */
  .panel h2 {
    color: #000;
  }

  /* All three say what state something is in, and all three say it with a
     fill. A border is what survives.

     `.stage` joined them for the same reason: a filled badge prints as a grey
     smudge on a laser printer, and the border keeps it reading as a badge
     rather than as loose text in a cell. */
  .detail__pending,
  .stage,
  .status {
    border: 1px solid #666;
    color: #000;
  }

  /* The download link stays as text and loses only its icon and its link
     styling. "Signed ownership purchase agreement, executed March 3" is a fact
     worth having in a client file; hiding the link and keeping the bare date
     beside it would print "executed March 3" with nothing to say what was.
     Deliberately no `attr(href)` trick: these are session-scoped download
     endpoints, so printing the URL puts an unusable string on the page. */
  a.download {
    color: #000;
    text-decoration: none;
  }

  a.download svg {
    display: none;
  }

  .download__date {
    color: #000;
  }
}

/* ---- Possible-match prompt --------------------------------------------- */

/* "Is this the same client?", asked of the firm once after submission.

   Deliberately not styled as an alert. The request is already saved and being
   reviewed, and a red-edged warning box would read as "something went wrong
   with your submission" — which is how a firm ends up answering under pressure
   and merging two people. So: the same card furniture as the payoff screen,
   and the reassurance above it in plain body type. */
.match__assurance {
  max-width: 32rem;
  margin: 1.5rem 0 0;
  color: var(--ink);
  font-size: 0.9375rem;
  line-height: 1.5;
}

.match {
  max-width: 32rem;
  margin: 0.75rem 0 0;
  padding: 30px 32px;
  border: 1px solid var(--rule);
  border-top: 3px solid var(--accent);
  border-radius: var(--radius);
  background: var(--surface);
}

.match__lede {
  margin: 0;
  color: var(--ink-muted);
  font-size: 0.875rem;
  line-height: 1.5;
}

/* One candidate. Ruled off from the next rather than boxed, because two boxes
   inside a box is where this screen starts looking like a form to fill in
   rather than a question to answer. */
.match__candidate {
  margin: 1.5rem 0 0;
  padding: 1.25rem 0 0;
  border-top: 1px solid var(--rule);
}

.match__candidate h2 {
  margin: 0;
  font-family: var(--f-display);
  font-size: 1.375rem;
  font-weight: 500;
  letter-spacing: -0.01em;
}

.match__reasons {
  margin: 4px 0 0;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.45;
}

.match__prompt {
  margin: 1rem 0 0;
  font-size: 0.875rem;
  font-weight: 600;
}

.match__cases {
  margin: 0.625rem 0 0;
  padding: 0;
  list-style: none;
}

/* Each case is a row: what it is on the left, the button that picks it on the
   right. Wraps to two lines on a phone rather than squeezing the button, since
   the button is the part being aimed at. */
.match__case {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 1rem;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem 0;
  border-bottom: 1px solid var(--rule);
}

.match__case-type {
  display: block;
  font-size: 0.875rem;
  font-weight: 600;
}

.match__case-detail {
  display: block;
  margin: 2px 0 0;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  font-variant-numeric: tabular-nums;
}

.match__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  margin: 1rem 0 0;
}

/* "None of these" is the filled button and sits below every candidate, so the
   most conservative answer is the one a firm reaches without choosing anybody.
   Failing toward a spare case is recoverable; a wrong merge is not. */
.match__actions--final {
  margin: 1.5rem 0 0;
  padding: 1.25rem 0 0;
  border-top: 1px solid var(--rule);
}

.match__note {
  margin: 0.75rem 0 0;
  color: var(--ink-muted);
  font-size: 0.8125rem;
  line-height: 1.45;
}

/* ---- A client with more than one advance -------------------------------- */

/* The admin client table's name cell. The table spans every firm and is sorted
   by funded date, so grouping would break the one property that makes it
   scannable and the relationship is marked on the row instead.

   The firm's own list reached the same answer in round four F1, where the
   nesting this comment used to contrast with was replaced by rows: see
   `.requests__related`, which is this marker one table along. */
.client-cell__name {
  display: block;
}

/* "+2 other advances", under the client's name. Quiet and small: it points at
   something rather than being a value, and the row's money columns are what a
   reviewer is scanning. */
.client-cell__related {
  display: block;
  margin-top: 2px;
  color: var(--ink-muted);
  font-size: 0.75rem;
  font-variant-numeric: tabular-nums;
}

/* ---- A funded client's own page (Trent, 2026-10-10) --------------------
   Read on a phone, by somebody who is not a lawyer, so one column, large
   figures and plain dates. The three steps are the point of the page: today's
   is marked, the past are greyed, and the next date is spelled out. */
.client-page {
  max-width: 34rem;
  margin: 0 auto;
  padding: 1.5rem 1rem 3rem;
}

.client-page__header {
  margin: 1.5rem 0 1.25rem;
}

.client-page__header h1 {
  margin: 0.25rem 0 0;
}

.client-page__total-label,
.client-advance__figure-label {
  margin: 1rem 0 0;
  color: var(--ink-muted);
  font-size: 0.9375rem;
}

.client-page__total,
.client-advance__figure {
  margin: 0.125rem 0 0;
  font-family: var(--f-display);
  font-size: 2.25rem;
  font-weight: 500;
  font-variant-numeric: tabular-nums;
  line-height: 1.1;
}

.client-page__total-note,
.client-advance__note,
.client-page__empty {
  margin: 0.375rem 0 0;
  color: var(--ink-muted);
}

.client-advance {
  margin-top: 1rem;
  padding: 1.25rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface);
}

.client-advance--paid .client-advance__figure {
  color: var(--ink-muted);
}

.client-advance__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
}

.client-steps {
  display: grid;
  gap: 0.375rem;
  margin: 1.25rem 0 0;
  padding: 0;
  list-style: none;
}

/* One row per step: when on the left, what on the right. */
.client-step {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.75rem;
  padding: 0.75rem 0.875rem;
  border: 1px solid var(--rule);
  border-radius: var(--radius);
  background: var(--surface-raised);
}

.client-step--current {
  border: 2px solid var(--accent);
  background: var(--surface);
}

.client-step--past {
  color: var(--ink-faint);
  background: var(--surface-sunken);
}

.client-step__when {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.375rem;
  font-size: 0.875rem;
}

.client-step__amount {
  font-size: 1.125rem;
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

.client-step__now {
  padding: 0.0625rem 0.4375rem;
  border-radius: 999px;
  background: var(--accent);
  color: var(--on-accent);
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.client-advance__ceiling {
  margin: 1rem 0 0;
}

.client-advance__people {
  margin: 1rem 0 0;
}

.client-advance__people dt {
  font-size: 0.8125rem;
  color: var(--ink-muted);
}

.client-advance__people dd {
  margin: 0;
}

.client-page__footer {
  display: grid;
  gap: 0.75rem;
  justify-items: start;
  margin-top: 1.5rem;
  color: var(--ink-muted);
}

/* Asking about more funding, below the advances. */
.client-ask {
  margin-top: 1.5rem;
  padding: 1rem;
  border: 1px solid var(--rule);
  border-radius: 0.5rem;
}

.client-ask__title {
  margin: 0 0 0.75rem;
  font-size: 1.0625rem;
}

.client-ask p {
  margin: 0;
}

.client-ask__amount {
  display: flex;
  gap: 0.375rem;
  align-items: center;
}

.client-ask__amount input {
  flex: 1;
}

/* Closing a client's ask, set apart from Enter it as a request above it. */
.ask-close {
  margin-top: 1.5rem;
}

.client-access__send {
  width: 100%;
  margin-top: 0.75rem;
}

/* The client page panel's buttons on the advance page: a row that wraps. */
.panel__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  align-items: center;
  margin: 1rem 0 0;
}
