/* Capture Request — an instrument, not a web form.
 *
 * THREE RULES DRIVE EVERYTHING HERE.
 *
 * 1. COLOUR IS RATIONED, AND IT REPORTS OUTCOMES. Red and green are never decoration
 *    and never chrome. Green appears only on a result that came back confirmed; red
 *    only on one that failed — a declined order, an error notice, a sign-in that broke.
 *    If you can see red on this page, something did not work.
 *
 *    Pisces navy carries identity and structure — masthead, the document's top edge,
 *    the ledger spine, the selected mode, the commit button, focus. Navy never reports
 *    an outcome, so it cannot be confused with one.
 *
 *    Both modes wear the same navy. An earlier revision made the card-change mode red
 *    throughout, on the reasoning that a new card is the more dangerous act; that was
 *    dropped deliberately — it left red doing two jobs at once, so a red panel meant
 *    "you are in card mode" in one place and "this failed" in another. Red now means
 *    exactly one thing.
 *
 * 2. DATA IS MONOSPACE. Every identifier a person could mistype — order numbers, POs,
 *    card digits, transaction ids — is set in a monospaced face with tabular figures.
 *    A transposed digit charges the wrong customer, and proportional type hides
 *    exactly that. This is a safety property that happens to be the type direction.
 *
 * 3. THE IDENTIFIER IS THE LARGEST THING ON THE PAGE. Rule 2 taken to its conclusion.
 *    The ledger sets each recognised order bigger than the heading above it, because
 *    the number is the one thing a person must actually read before committing.
 *
 * THE WHOLE FORM FITS ON ONE SCREEN, in both modes, with nothing entered. That is a
 * requirement, not a nicety: this form commits an irreversible act, and a commit button
 * below the fold is a commit button pressed without the orders in view.
 *
 * The mode picker lives CENTRED IN THE MASTHEAD (a strip above the heading until
 * 2026-08-17, then briefly a left rail, which read as too heavy). The bar is a three-track
 * grid so the tabs are centred on the page rather than in the space the logo and the account
 * block leave over, and the selected tab is marked by an underline sitting on the bar's own
 * bottom rule. The masthead is sticky, so the picker stays visible for nothing. Under 64rem
 * the bar stacks into two rows.
 *
 * Card mode earns the room two ways, both in the two-column rule at the bottom of this
 * file. The card panel sits BESIDE the orders and the steps rather than under them, and
 * the message strip fills the space left under the card panel instead of taking a full
 * row of its own — so the strip finishes level with the steps box and no gap is wasted.
 * Together that is worth a bit over 250px of height.
 *
 * No webfonts, by constraint and by preference: the CSP is font-src 'self', so the
 * personality here comes from treatment — tracking, scale, tabular figures, rules —
 * rather than from a downloaded face.
 */

:root {
  /* ground + surfaces — cool, clinical, and a ground the navy belongs on */
  --paper:      #F1F4F8;
  --surface:    #FFFFFF;

  /* identity — structure only, never a state */
  --navy:       #003680;
  --navy-deep:  #002356;
  --navy-tint:  #E7EEF8;
  --brand-green:#00DE73;

  --ink:        #0E1319;
  --ink-soft:   #4E5A69;
  --ink-faint:  #8592A2;
  --rule:       #DEE4EC;
  --rule-firm:  #BDC7D4;

  /* state — outcomes only */
  --charge:     #B4231F;   /* a result that failed */
  --charge-dim: #FBEAE9;
  --ok:         #07784A;   /* confirmed, past tense */
  --ok-dim:     #E3F2EA;

  --mono: ui-monospace, "Cascadia Mono", "Cascadia Code", Consolas,
          "SF Mono", "Roboto Mono", monospace;
  --sans: "Segoe UI Variable Text", "Segoe UI", system-ui, -apple-system, sans-serif;

  --step: 4px;
  --lift: 0 1px 2px rgba(14, 19, 25, .04), 0 10px 28px -14px rgba(14, 19, 25, .14);
}

*, *::before, *::after { box-sizing: border-box; }

/* The `hidden` attribute must win over every layout rule below.
   The UA stylesheet's [hidden] { display: none } has the lowest possible priority, so
   ANY author `display` rule silently beats it — `.gate { display: grid }` kept the
   sign-in panel painted over the whole app after a successful sign-in, which read as
   "sign-in did nothing". Anything toggled with .hidden in JS depends on this line. */
[hidden] { display: none !important; }

html { -webkit-text-size-adjust: 100%; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 15px;
  line-height: 1.5;
}

.mono {
  font-family: var(--mono);
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1;
  letter-spacing: -.01em;
}

button, input, select, textarea { font: inherit; color: inherit; }

:focus-visible {
  outline: 2px solid var(--navy);
  outline-offset: 2px;
}

/* ── the mark ───────────────────────────────────────────────────────────── */
.logo {
  display: block;
  width: 152px;
  height: auto;
}

.eyebrow {
  margin: 0;
  font-family: var(--mono);
  font-size: 11px;
  font-weight: 600;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--navy);
}

/* ── sign-in gate ───────────────────────────────────────────────────────── */
.gate {
  min-height: 100dvh;
  display: grid;
  place-items: center;
  padding: calc(var(--step) * 6);
}
.gate-inner {
  max-width: 30rem;
  width: 100%;
  background: var(--surface);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--navy);
  box-shadow: var(--lift);
  padding: calc(var(--step) * 9) calc(var(--step) * 8) calc(var(--step) * 8);
}
.gate-inner .logo { margin-bottom: calc(var(--step) * 7); }
.gate-inner .eyebrow { margin-bottom: calc(var(--step) * 3); }
.gate-inner h1 {
  margin: 0 0 calc(var(--step) * 3);
  font-size: clamp(1.45rem, 4vw, 1.8rem);
  font-weight: 650;
  letter-spacing: -.025em;
  line-height: 1.15;
}
.gate-inner .btn { margin-top: calc(var(--step) * 6); }
.gate-error {
  margin-top: calc(var(--step) * 5);
  padding: calc(var(--step) * 3) calc(var(--step) * 4);
  border-left: 3px solid var(--charge);
  background: var(--charge-dim);
  color: var(--ink);
  font-size: 14px;
}

/* ── masthead ───────────────────────────────────────────────────────────── */
.bar {
  position: sticky;
  top: 0;
  z-index: 20;
  /* Three tracks, so the picker is centred on the PAGE rather than in whatever space the
     logo and the account block happen to leave. `1fr auto 1fr` is what makes that true
     even though the right-hand side changes width with the length of an email address. */
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  column-gap: calc(var(--step) * 8);
  padding: 0 calc(var(--step) * 7);
  background: var(--surface);
  border-bottom: 1px solid var(--rule);
}
.bar > .logo { justify-self: start; }
.bar-nav { justify-self: center; min-width: 0; }
.who { justify-self: end; }
/* The bar's height comes from the TABS, which have to reach its bottom edge for the
   selected underline to land on the same line as the border. So the vertical padding lives
   on them, and the two side items get their own margin instead. */
.bar > .logo, .bar > .who { margin-block: calc(var(--step) * 3); }
.who {
  display: flex;
  align-items: center;
  gap: calc(var(--step) * 4);
  font-size: 13px;
  color: var(--ink-soft);
  /* Shrinkable, so a long address cannot push the masthead past the viewport. Without
     this pair, bradley.diehl@pisceshealth.com set in nowrap mono was wider than a phone
     and scrolled the whole page sideways — the address is the flex item that has to give,
     never Sign out. */
  min-width: 0;
}
#whoami {
  font-size: 12.5px;
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* ── sheet ───────────────────────────────────────── */
/* One constant width in every mode. It is wide enough for the two-column card layout, so
   switching modes never reflows the page around you — and with the picker up in the
   masthead the document owns the sheet's full width again. */
.sheet {
  max-width: 58rem;
  margin: 0 auto;
  padding: calc(var(--step) * 6) calc(var(--step) * 7) calc(var(--step) * 4);
}
/* min-width:0 so a wide child (the admin table) cannot widen the column and take the whole
   page sideways with it. */
.content { min-width: 0; }

.page-head { margin-bottom: calc(var(--step) * 5); }
.page-head .eyebrow { margin-bottom: calc(var(--step) * 2); }
.page-head h1 {
  margin: 0 0 calc(var(--step) * 1.5);
  font-size: clamp(1.5rem, 3vw, 1.85rem);
  font-weight: 650;
  letter-spacing: -.03em;
  line-height: 1.1;
}
.lede {
  margin: 0;
  max-width: 38rem;
  font-size: 14.5px;
  color: var(--ink-soft);
}

/* ── the mode picker ──────────────────────────────────────────────────────
   Restored 2026-08-17. Navy FILLS the selected tab: rule 1 at the top of this file
   gives navy structure and identity and never an outcome, so the selected mode can be
   the strongest thing in the head without being mistaken for a result. Both tabs wear
   the same navy for the same reason — see the note about the red card mode up there.

   IN THE BAR they are underline tabs, not filled blocks. A row of navy-filled blocks in a
   white masthead reads as five buttons competing with the commit button; an underline puts
   the selection on the rule the eye is already following — the one between the bar and the
   document — and leaves navy meaning exactly what it meant before.

   One line per label, and short. The heading and lede below already say what the mode does,
   and rule "the whole form fits on one screen" is being spent on the ledger.

   `body.mode-card` is toggled by setMode() and drives the two-column layout at the
   bottom of this file, NOT the picker: the selected tab is styled off aria-selected, so
   the accessible state and the visible state are the same fact. */
.modes {
  display: flex;
  min-width: 0;
  /* Scrolls within itself if the labels ever outgrow the middle track, so the masthead can
     never push the page sideways. No visible scrollbar: it would sit on the bar's rule. */
  overflow-x: auto;
  scrollbar-width: none;
}
.modes::-webkit-scrollbar { display: none; }
.tab {
  appearance: none;
  border: 0;
  border-radius: 0;
  background: transparent;
  color: var(--ink-soft);
  /* This padding IS the bar's height. The -1px bottom margin pulls the underline down onto
     the bar's own rule, so the two read as one line rather than two. */
  padding: calc(var(--step) * 4.5) calc(var(--step) * 4);
  border-bottom: 3px solid transparent;
  margin-bottom: -1px;
  font-size: 12.5px;
  font-weight: 650;
  letter-spacing: .07em;
  text-transform: uppercase;
  white-space: nowrap;
  cursor: pointer;
  transition: color .14s ease, border-color .14s ease;
}

/* Admin is not a request type — it configures the others. A rule to its left says so
   without introducing a second visual language into the row. */
.tab.tab-admin {
  margin-left: calc(var(--step) * 3);
  border-left: 1px solid var(--rule);
}
.tab:hover:not([aria-selected="true"]):not(:disabled) {
  color: var(--navy);
  border-bottom-color: var(--rule-firm);
}
.tab[aria-selected="true"] {
  color: var(--navy);
  border-bottom-color: var(--navy);
}

/* Inset: at the default 2px offset the ring would be drawn over the bar's bottom rule and
   across its neighbours. */
.tab:focus-visible { outline-offset: -3px; }

/* Locked from submit until "New request" (syncTabs() in app.js). The SELECTED tab keeps
   its full navy while locked — the mode a request was sent in is part of reading the
   result, so it must not fade out with the control. */
.tab:disabled { cursor: not-allowed; }
.tab:disabled:not([aria-selected="true"]) { opacity: .38; }

/* ── the document ───────────────────────────────────────────────────────── */
/* A grid with no named areas on purpose: items flow in DOM order, so a hidden card
   panel or notice is removed outright instead of leaving an empty row and its gaps. */
.doc {
  position: relative;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  gap: calc(var(--step) * 6);
  background: var(--surface);
  border: 1px solid var(--rule);
  border-top: 3px solid var(--navy);
  box-shadow: var(--lift);
  padding: calc(var(--step) * 5) calc(var(--step) * 7);
}
/* The one place brand green appears outside the mark: a short segment on the top edge,
   the width of the cross in the logo.

   It sweeps: green runs left-to-right until the edge is fully green, then navy runs
   left-to-right over it, and the cycle repeats from the 52px stub. Both layers sit
   ON TOP of .doc's navy border-top, so the resting state and the end of the cycle are
   the same navy — the loop closes without a flash.

   READ THIS BEFORE COPYING THE PATTERN ELSEWHERE. Green means "confirmed" everywhere
   else on this page (see rule 1 at the top of this file), and a moving bar reads as
   progress. Both are deliberately suspended here because this edge is the brand mark,
   not a status: it is outside the form's reporting surface, it animates identically
   whether the bot is idle, working or finished, and it is never keyed to a step. The
   thing that reports real progress is .progress-track, which is driven by steps the
   bot actually logged and does not move on its own. Do not wire this to state, and do
   not put green on anything that is not an outcome. */
.doc::before,
.doc::after {
  content: "";
  position: absolute;
  top: -3px;
  left: -1px;                 /* clear .doc's 1px side border so the sweep reaches the corner */
  height: 3px;
  pointer-events: none;
}
.doc::before {
  width: 52px;
  background: var(--brand-green);
  animation: doc-edge-green 4.2s linear infinite;
}
.doc::after {
  width: 0;
  background: var(--navy);
  animation: doc-edge-navy 4.2s linear infinite;
}

/* The two halves of one cycle. The navy layer is held at 0 until the green has
   finished, and held at full width until the cycle restarts — at which point green
   snaps back to the stub underneath a bar that is already navy, so the reset is not
   visible. */
@keyframes doc-edge-green {
  0%   { width: 52px; }
  42%  { width: calc(100% + 2px); }
  100% { width: calc(100% + 2px); }
}
@keyframes doc-edge-navy {
  0%,
  50%  { width: 0; }
  92%  { width: calc(100% + 2px); }
  100% { width: calc(100% + 2px); }
}

/* ── fields ─────────────────────────────────────────────────────────────── */
.field { display: grid; gap: calc(var(--step) * 1.5); align-content: start; }
.field + .field { margin-top: calc(var(--step) * 4); }

label {
  font-size: 11.5px;
  font-weight: 650;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.hint { margin: 0; font-size: 13px; color: var(--ink-faint); }

textarea, input[type="text"], input:not([type]), select {
  width: 100%;
  padding: calc(var(--step) * 2.5) calc(var(--step) * 3);
  border: 1px solid var(--rule-firm);
  border-radius: 0;
  background: var(--surface);
  font-family: var(--mono);
  font-size: 15px;
  font-variant-numeric: tabular-nums;
  transition: border-color .14s ease, box-shadow .14s ease;
}
textarea { resize: vertical; min-height: 3.4rem; line-height: 1.55; }
textarea:focus, input:focus, select:focus {
  border-color: var(--navy);
  box-shadow: 0 0 0 3px var(--navy-tint);
  outline: none;
}
::placeholder { color: var(--ink-faint); opacity: 1; }

/* Shut means LOOKS shut. Email & CC closes these boxes until Begin has read the order, and
   again while a saved card is chosen — and Chromium's own disabled styling is nearly
   invisible against this panel, which would leave a box that looks typeable and is not.
   Flat grey and no white ground: the difference is meant to be obvious at a glance. */
textarea:disabled, input:disabled, select:disabled {
  background: var(--paper);
  border-color: var(--rule);
  color: var(--ink-faint);
  cursor: not-allowed;
}
/* The label dims with its control, so a shut field reads as one shut thing rather than a
   live heading over a dead box. */
.field:has(:disabled) > label { color: var(--ink-faint); }

/* ── THE LOCKED ORDERS BOX — the signature ──────────────────────────────
   The same box you typed into, after you commit. It REPLACES the textarea rather than
   overlaying it, because a textarea cannot colour individual lines of its own value —
   so the box stays the same size, in the same place, in the same face, and the numbers
   you approved are the numbers that turn green. Nothing migrates to another part of the
   page to report back.

   The numbered spine is the ledger that used to have its own section. The numbers are a
   CSS counter: the identifiers are the content, their position is a fact about the
   rendering. */
.orders-view {
  position: relative;
  counter-reset: oline;
  list-style: none;
  margin: 0;
  padding: calc(var(--step) * 1.5) calc(var(--step) * 3);
  border: 1px solid var(--rule-firm);
  background: var(--surface);
}
.orders-view::before {
  content: "";
  position: absolute;
  left: 1.6rem; top: calc(var(--step) * 3); bottom: calc(var(--step) * 3);
  width: 1px;
  background: var(--rule);
}

.oline {
  position: relative;
  display: grid;
  grid-template-columns: 1.625rem auto 1fr auto;
  align-items: baseline;
  gap: calc(var(--step) * 3);
  padding: calc(var(--step) * 2) 0;
}
.oline + .oline { border-top: 1px solid var(--rule); }
.oline::before {
  counter-increment: oline;
  content: counter(oline, decimal-leading-zero);
  align-self: center;
  width: 1.625rem;
  padding: 3px 0;
  background: var(--navy-tint);
  color: var(--navy);
  font-family: var(--mono);
  font-size: 10.5px;
  font-weight: 600;
  text-align: center;
}
.oline-kind {
  font-family: var(--mono);
  font-size: 10.5px;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
/* Rule 3: the number a person must actually read. */
.oline-id {
  font-family: var(--mono);
  font-size: 18px;
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1, "zero" 1;
  font-weight: 600;
  letter-spacing: -.02em;
  color: var(--ink-faint);
  transition: color .2s ease;
}
.oline-state {
  font-family: var(--mono);
  font-size: 11px;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-faint);
  white-space: nowrap;
}

/* Queued: legible but plainly not started. */
.oline.is-wait .oline-id { color: var(--ink-faint); }

.oline.is-working .oline-id { color: var(--ink); }
.oline.is-working .oline-state { color: var(--navy); }
.oline.is-working::before { background: var(--navy); color: #fff; }

/* A wash as well as coloured type: this is the moment the person is waiting for, and it
   should be readable from across a desk. */
.oline.is-ok { background: linear-gradient(90deg, var(--ok-dim), transparent 55%); }
.oline.is-ok .oline-id { color: var(--ok); }
.oline.is-ok .oline-state { color: var(--ok); }
.oline.is-ok::before { background: var(--ok); color: #fff; }

.oline.is-bad { background: linear-gradient(90deg, var(--charge-dim), transparent 55%); }
.oline.is-bad .oline-id { color: var(--charge); }
.oline.is-bad .oline-state { color: var(--charge); }
.oline.is-bad::before { background: var(--charge); color: #fff; }

/* A dry run is neither — saying it failed trains people to ignore the word. */
.oline.is-dry .oline-id { color: var(--ink); }
.oline.is-dry .oline-state { color: var(--ink-soft); }

.orders-note {
  margin: calc(var(--step) * 1.5) 0 0;
  min-height: 1.1rem;
  font-size: 12.5px;
  color: var(--ink-faint);
}
.orders-note.is-bad { color: var(--charge); }

/* ── the side column ────────────────────────────────────────────────────
   In card mode this is the right-hand column: the card panel at its natural height with
   the message strip filling whatever is left, so the strip finishes exactly level with
   the steps box beside it and the blank space under the card panel is put to work.

   In capture mode there is no second column and no card panel, so this collapses to
   nothing more than a wrapper around the strip. */
.side {
  display: flex;
  flex-direction: column;
  gap: calc(var(--step) * 4);
  min-height: 0;
}

/* ── card panel ─────────────────────────────────────────────────────────── */
.card-panel {
  margin: 0;
  padding: calc(var(--step) * 4);
  background: var(--navy-tint);
  border: 1px solid var(--rule-firm);
  border-left: 3px solid var(--navy);
  align-content: start;
}
.card-head h2 {
  margin: 0 0 calc(var(--step) * 1.5);
  font-size: 14.5px;
  font-weight: 650;
  letter-spacing: -.01em;
}
.card-panel .hint { color: var(--ink-soft); }
/* The card number takes the full width; expiry and code share the row under it. This
   holds up in the narrow side column AND full width, so it is the only card layout. */
.card-grid {
  margin-top: calc(var(--step) * 4);
  display: grid;
  grid-template-columns: 1fr 6.25rem;
  gap: calc(var(--step) * 3) calc(var(--step) * 4);
  align-items: end;
}
/* Placed by name, not by position: the card number spans the row, expiry and code share
   the one under it. */
/* Named, not positional: the email row is only present in Email & CC mode, so
   :first-child would move to the card number in the other one and these two are the rows
   that must span regardless. The :first-child rule stays for the same reason it always
   worked — harmless when both of these already match. */
/* The saved-cards dropdown spans too. Left to auto-placement it landed in column 1 — the
   1fr one, about 175px in the side panel — and clipped "ending in 4287 — expires 12/2026"
   to "ending in 42…", which is the one thing a person has to read to pick the right card. */
.card-grid .field-email,
.card-grid .field-saved,
.card-grid .field-pan { grid-column: 1 / -1; }
.card-grid .field:first-child { grid-column: 1 / -1; }
.card-grid .field-exp { grid-column: 1; }
.card-grid .field-cvv { grid-column: 2; }
.card-grid .field + .field { margin-top: 0; }
.exp { display: flex; align-items: center; gap: calc(var(--step) * 1.5); }
.exp select { padding-inline: calc(var(--step) * 2); }
.exp-sep { color: var(--ink-faint); }

/* ── live step progress ─────────────────────────────────────────────────
   The bot publishes each step as it takes it and this renders what arrived. A step is
   ticked ONLY because the pipeline said so — there is no timer here and no estimate.

   The bar at the top is a real fraction (steps closed / steps planned). The bar under
   the active step is indeterminate on purpose: the bot cannot know how long the gateway
   will take, and a bar that pretends to would be a lie told in animation. */
.progress {
  margin: 0;
  padding: calc(var(--step) * 3.5);
  background: var(--paper);
  border: 1px solid var(--rule);
  border-left: 3px solid var(--navy);
}
.progress-head {
  display: flex;
  align-items: baseline;
  gap: calc(var(--step) * 2);
}
.progress-label {
  font-size: 11.5px; font-weight: 650; letter-spacing: .11em;
  text-transform: uppercase; color: var(--navy);
}
#progress-what { font-size: 13.5px; font-weight: 600; }
.progress-count {
  margin-left: auto;
  font-size: 11.5px;
  color: var(--ink-soft);
}

.progress-track {
  margin-top: calc(var(--step) * 2.5);
  height: 3px;
  background: var(--rule);
  overflow: hidden;
}
.progress-fill {
  display: block;
  height: 100%;
  width: 0;
  background: var(--navy);
  transition: width .3s ease;
}

.psteps {
  list-style: none;
  margin: calc(var(--step) * 2.5) 0 0;
  padding: 0;
  display: grid;
}
.pstep {
  display: grid;
  grid-template-columns: 1.25rem minmax(0, 1fr);
  align-items: baseline;
  gap: calc(var(--step) * 2);
  padding: calc(var(--step) * 0.75) 0;
  font-size: 13.5px;
  line-height: 1.4;
  color: var(--ink-faint);
}
/* The status glyph. Drawn from content so it needs no icon font and no inline SVG. */
.pstep-mark {
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1.35;
  text-align: center;
  color: var(--rule-firm);
}
.pstep-body { min-width: 0; }
.pstep-note {
  display: block;
  margin-top: 1px;
  font-family: var(--mono);
  font-size: 11.5px;
  color: var(--ink-faint);
  overflow-wrap: anywhere;
}

.pstep.is-done { color: var(--ink); }
.pstep.is-done .pstep-mark { color: var(--ok); font-weight: 700; }

.pstep.is-now { color: var(--ink); font-weight: 600; }
.pstep.is-now .pstep-mark { color: var(--navy); }

.pstep.is-fail { color: var(--ink); }
.pstep.is-fail .pstep-mark { color: var(--charge); font-weight: 700; }
.pstep.is-fail .pstep-note { color: var(--charge); }

/* Was running when the answer arrived, so its outcome was never polled. Deliberately NOT
   a tick and NOT a failure: the form does not know, and says so. Its note carries the
   weight, so it is set in the readable ink rather than the faint one. */
.pstep.is-unconfirmed .pstep-note { color: var(--ink-soft); }

/* The indeterminate bar on the step actually running right now. */
.pstep-bar {
  display: none;
  margin-top: calc(var(--step) * 1.5);
  height: 2px;
  background: var(--navy-tint);
  overflow: hidden;
}
.pstep.is-now .pstep-bar { display: block; }
.pstep-bar::after {
  content: "";
  display: block;
  height: 100%;
  width: 40%;
  background: var(--navy);
  animation: pstep-sweep 1.1s ease-in-out infinite;
}
@keyframes pstep-sweep {
  from { transform: translateX(-100%); }
  to   { transform: translateX(250%); }
}

/* ── notice ─────────────────────────────────────────────────────────────── */
.notice {
  margin: 0;
  padding: calc(var(--step) * 3) calc(var(--step) * 4);
  border-left: 3px solid var(--charge);
  background: var(--charge-dim);
  font-size: 13.5px;
  /* One line per order, so the reasons stay separate and readable. */
  white-space: pre-line;
}
.notice.is-quiet {
  border-left-color: var(--rule-firm);
  background: var(--paper);
  color: var(--ink-soft);
}

/* ── commit ─────────────────────────────────────────────────────────────── */
.commit {
  margin: 0;
  padding-top: calc(var(--step) * 4);
  border-top: 1px solid var(--rule);
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: calc(var(--step) * 2) calc(var(--step) * 5);
}
.commit-note { margin: 0; font-size: 13px; color: var(--ink-faint); }
/* The note doubles as the reason a disabled commit button will not light up. Red only for
   that: a faint grey sentence beside a dead button is not read as the explanation. */
.commit-note.is-bad { color: var(--charge); }

.btn {
  appearance: none;
  border: 1px solid var(--navy);
  background: transparent;
  color: var(--navy);
  padding: calc(var(--step) * 2.5) calc(var(--step) * 6);
  border-radius: 0;
  font-size: 14px;
  font-weight: 650;
  letter-spacing: -.005em;
  cursor: pointer;
  transition: background-color .14s ease, color .14s ease, border-color .14s ease;
}
.btn:hover:not(:disabled) { background: var(--navy); color: #fff; }
.btn-primary { background: var(--navy); color: #fff; }
.btn-primary:hover:not(:disabled) {
  background: var(--navy-deep);
  border-color: var(--navy-deep);
}
.btn:disabled { opacity: .34; cursor: not-allowed; }

.btn-link {
  border: 0;
  padding: 0;
  font-weight: 500;
  font-size: 13px;
  color: var(--ink-soft);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.btn-link:hover { background: transparent; color: var(--navy); }
/* Never the thing that wraps. Now that .who can shrink, the address ellipsizes and this
   would otherwise break to "Sign / out" on a phone. */
#signout { white-space: nowrap; flex: 0 0 auto; }

.foot {
  margin-top: calc(var(--step) * 3);
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-faint);
  max-width: 40rem;
}

.muted { color: var(--ink-soft); }

/* ── side-panel modes, wide: the panel goes BESIDE the ledger ────────────
   This is what buys the height that keeps the commit button above the fold. Below this
   width the single-column flow above applies and the page may scroll — a phone has no
   second column to give.

   `mode-card`, `mode-po` and `mode-ec` all select it: the rules are about a form WITH a
   side panel, not about which panel — Change PO's New PO box and Email & CC's two boxes
   have the same job in the layout as the card box. Capture mode has no panel and stays
   single-column. */
@media (min-width: 60rem) {
  body.mode-card #form,
  body.mode-po #form,
  body.mode-ec #form {
    grid-template-columns: minmax(0, 1fr) minmax(0, 20rem);
    column-gap: calc(var(--step) * 7);
    align-items: start;
  }
  body.mode-card #form > .field-orders,
  body.mode-po   #form > .field-orders,
  body.mode-ec   #form > .field-orders { grid-column: 1; grid-row: 1; }
  body.mode-card #form > #progress,
  body.mode-po   #form > #progress,
  body.mode-ec   #form > #progress     { grid-column: 1; grid-row: 2; }
  /* Spanning both rows is what makes the column as tall as the orders box plus the
     steps box, and the flex child below is what makes the strip reach the bottom of it. */
  body.mode-card #form > .side,
  body.mode-po   #form > .side,
  body.mode-ec   #form > .side         { grid-column: 2; grid-row: 1 / span 2; }
  body.mode-card #form > .side > #notice,
  body.mode-po   #form > .side > #notice,
  body.mode-ec   #form > .side > #notice {
    flex: 1 1 auto;
    /* min-height:0 lets it shrink inside the flex column; without it a long result
       would push the column taller and put the page back into scroll. */
    min-height: 0;
    overflow-y: auto;
  }
  /* No explicit row: auto-placement puts this after the block above. */
  body.mode-card #form > .commit,
  body.mode-po   #form > .commit,
  body.mode-ec   #form > .commit       { grid-column: 1 / -1; }
}

/* ── the Admin tab ────────────────────────────────────────────────────────
   A table, because the question it answers is a grid: people down, tabs across. Ticks
   rather than a multi-select per person — with four columns the whole grant is legible at
   a glance, and "who can charge a card" is a question somebody should be able to answer by
   looking rather than by opening four dropdowns.

   No red anywhere: rule 1 at the top of this file reserves it for a result that failed,
   and a person's permission is not an outcome. The only red on this panel is a write that
   did not save. */
.admin-head { margin-bottom: calc(var(--step) * 5); }

.admin-add {
  display: flex;
  align-items: end;
  gap: calc(var(--step) * 3);
  padding-bottom: calc(var(--step) * 5);
  margin-bottom: calc(var(--step) * 2);
  border-bottom: 1px solid var(--rule);
}
.admin-add .field { flex: 1 1 22rem; max-width: 26rem; }

/* The grid scrolls inside itself rather than widening the sheet — six columns on a phone
   has to go somewhere, and the page body must never scroll sideways. */
/* min-width:0 is what makes overflow-x actually contain the table. #admin-panel is a
   `.doc`, so this is a GRID ITEM, and a grid item's default min-width:auto refuses to
   shrink below its content — the table then pushed the whole page sideways on a phone,
   which is the one thing the layout rules here forbid outright. */
.admin-table-wrap { overflow-x: auto; min-width: 0; }
.admin-table {
  width: 100%;
  border-collapse: collapse;
  font-size: 14px;
}
.admin-table th {
  text-align: left;
  padding: calc(var(--step) * 2) calc(var(--step) * 2);
  border-bottom: 1px solid var(--rule-firm);
  font-size: 11px;
  font-weight: 650;
  letter-spacing: .08em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
/* The tick headings WRAP, deliberately. They carry the tab's exact name — an admin has to
   know which column is which tab — and five unwrapped names plus an email column was wider
   than the content track, so the table scrolled and clipped the Remove button. Two short
   lines each fits, and keeps the names honest rather than abbreviating them. */
.admin-table th:not(:first-child) {
  text-align: center;
  white-space: normal;
  max-width: 5.5rem;
  line-height: 1.25;
}
.admin-table th:first-child { white-space: nowrap; }
.admin-table td {
  padding: calc(var(--step) * 2.5) calc(var(--step) * 2);
  border-bottom: 1px solid var(--rule);
  vertical-align: middle;
}
.admin-table tr:hover td { background: var(--paper); }
.admin-tick { text-align: center; }
.admin-tick input { width: 16px; height: 16px; accent-color: var(--navy); cursor: pointer; }
.admin-tick input:disabled { cursor: not-allowed; opacity: .45; }

/* The identifier is monospaced for the same reason every other identifier on this page is:
   these are addresses somebody could mistype, and a wrong one grants the wrong person. */
.admin-who {
  font-family: var(--mono);
  font-size: 13px;
  white-space: nowrap;
}
.admin-you {
  font-family: var(--sans);
  font-size: 10.5px;
  font-weight: 650;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--navy);
  background: var(--navy-tint);
  padding: 1px calc(var(--step) * 1.5);
}
/* width:1% with nowrap is the "shrink to fit the content" idiom for a table cell: the
   person column takes the slack instead, and "Remove" stops being clipped to "Rem". */
.admin-table th:last-child,
.admin-table td:last-child { width: 1%; white-space: nowrap; text-align: right; }
.admin-remove { font-size: 12.5px; white-space: nowrap; }

.admin-note {
  margin: calc(var(--step) * 4) 0 0;
  min-height: 1.2em;
  font-size: 13px;
  color: var(--ink-soft);
}
.admin-note.is-bad {
  padding: calc(var(--step) * 2) calc(var(--step) * 3);
  border-left: 3px solid var(--charge);
  background: var(--charge-dim);
  color: var(--ink);
}

/* The no-access panel. Deliberately plain: it is a statement, not a failure. */
#no-access { padding: calc(var(--step) * 7); }

/* ── the bar becomes two rows ────────────────────
   Five labels plus a logo plus a full email address need about 84rem before they share one
   line without the last tab crowding the address — measured, not guessed: at 1240px the
   Admin tab overlapped the address by 7px and was being clipped, and it was still tight at
   1400px. So below 84rem the bar stacks: identity on the first row, the picker centred on
   its own row beneath. That is the shape most laptops get, and the better-looking of the
   two; the single row is for genuinely wide screens. */
@media (max-width: 83.999rem) {
  .bar {
    grid-template-columns: 1fr auto;
    row-gap: 0;
  }
  .bar > .logo { grid-column: 1; grid-row: 1; }
  .bar > .who { grid-column: 2; grid-row: 1; }
  .bar-nav {
    grid-column: 1 / -1;
    grid-row: 2;
    justify-self: center;
    max-width: 100%;
  }
  .tab { padding-block: calc(var(--step) * 3); }
}

/* ── narrow ───────────────────────────────────────────────────── */
@media (max-width: 40rem) {
  .sheet { padding-inline: calc(var(--step) * 4); }
  .bar { padding-inline: calc(var(--step) * 4); }
  .doc { padding: calc(var(--step) * 5) calc(var(--step) * 4); }
  .gate-inner { padding: calc(var(--step) * 7) calc(var(--step) * 5); }
  .logo { width: 136px; }
  /* The identifier stays big — it is the thing being checked. The row number and the
     kind label are what give way. */
  .oline { grid-template-columns: 1.375rem auto 1fr; gap: calc(var(--step) * 2.5); }
  .oline::before { width: 1.375rem; }
  .oline-state { grid-column: 2 / -1; }
  .orders-view::before { left: 1.4rem; }
  /* The picker is a horizontal scrolling row on a phone (see the block above)
     rather than a stack: five stacked tabs pushed the orders box a screen down. */
}

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }
  /* A sweep frozen mid-travel reads as a hung page. Show a solid bar instead — the
     step is still marked as running, just without the movement. */
  .pstep-bar::after { width: 100%; animation: none !important; }

  /* Without this the blanket rule above lands the edge on its LAST frame — fully navy,
     brand stub gone. Put it back to the static mark it was before it animated. */
  .doc::before { width: 52px; animation: none !important; }
  .doc::after  { width: 0;    animation: none !important; }
}
