/* Omni Title service-layer web app. Vanilla CSS, no build step, no
   framework.

   Restyled 2026-09-09 to match contract-form.css, which the client and his
   architect both preferred: "design should be united and have client logo."
   The app had drifted into a neomorphic look while the rule-pack-generated
   form kept a crisper one, so the two surfaces of the same product did not
   look like the same product.

   This is a change of design language, not of markup -- every selector the
   render files use is still here, so nothing needed rewriting to adopt it.

   The tokens below are contract-form.css's own, copied rather than
   approximated -- same paper, same ink, same lines, same radius, same type
   scale. The client asked for "exactly this", so a token that differs
   between the two files is a bug in this one, and editing either should
   mean editing both.

   Surfaces are white cards on warm paper, separated by a thin line rather
   than a shadow. Nothing is embossed and nothing sits in a "hole". */

:root {
  /* Tells the browser this design is light, so it renders form controls,
     scrollbars and the canvas that way -- and, more to the point, stops
     Chrome and Edge applying their own automatic dark treatment when the
     operating system is set to dark. Removing our dark theme was not
     enough on its own: without this the browser inverted the page
     underneath us, which is why the logo came back nearly invisible on a
     dark laptop. */
  color-scheme: light;
  /* These are contract-form.css's values, copied rather than approximated.
     The two files are the same product and the client asked for one design,
     so a token that differs between them is a bug in this file. */
  --paper: #f4f3f0;
  /* Ivory rather than pure white. A large sheet of #ffffff next to warm
     paper glares under office lighting and is the first thing that makes a
     long form tiring to read -- this keeps the card clearly lighter than the
     page while sharing its warmth, so the two surfaces belong to each other.
     Still well above 4.5:1 against --ink and --dim. */
  --card: #fbf9f5;
  --ink: #191b1f;
  --dim: #565c66;
  /* Darkened from #8b909a on 2026-09-09. That measured 3.05:1 on a card and
     2.89:1 on the page, under the 4.5:1 normal-text minimum -- and this is
     not decorative grey: it is every table header, every .meta line and
     every empty-state sentence. #6b7079 clears 4.5 on both surfaces while
     still reading as clearly secondary to --dim. */
  --faint: #6b7079;
  --line: #dcd9d3;
  --line-2: #c3bfb7;

  /* The form's own accent, not the logo's blue.
     The logo is a bright #2858f8, which is right for a mark and too loud for
     every button and link on a document-handling screen -- and the client's
     reference for "exactly this" is the form, where the accent is this
     restrained slate-teal. A brand mark being brighter than the interface
     around it is normal; the mark still carries the identity. */
  --accent: #1d4f6c;
  --accent-strong: #163d54;
  --accent-soft: #e8eff3;

  --good: #1a6b52;
  --good-soft: #e2f1ea;
  --warn: #9a6a15;
  --warn-soft: #fbf3e2;
  --warn-line: #e0cb96;
  --danger: #b3352f;
  --danger-soft: #fae7e5;
  --accent-glow: rgba(29, 79, 108, .18);
  --danger-glow: rgba(179, 53, 47, .18);

  /* The three attention statuses, one scheme on every screen (walkthrough
     2026-09-21, #6): Missing blue, Needs review amber, Problem red. The
     Dashboard and Files list had Missing amber and Needs review blue while
     the file page and the form had it the other way round, so the same item
     changed colour as you clicked into it. contract-form.css is a standalone
     page and declares the same literal values; test/status_colours.js fails
     if the two sets ever drift apart. */
  --status-missing-ink: #163d54;
  --status-missing-bg: #e8eff3;
  --status-review-ink: #9a6a15;
  --status-review-bg: #fbf3e2;
  --status-review-line: #e0cb96;
  --status-problem-ink: #b3352f;
  --status-problem-bg: #fae7e5;

  --radius: 7px;
  --radius-sm: 6px;
  --radius-xs: 5px;

  --sans: system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
  --display: var(--sans);
  --mono: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;

  /* Kept as aliases so any rule still reaching for the old neomorphic
     variables resolves to a sensible line colour rather than to nothing.
     New rules should use --line and --card directly. */
  --shadow-dark: var(--line);
  --shadow-light: var(--card);
}

/* No dark theme, deliberately.
   Both this file and contract-form.css carried one, and it is why the same
   product looked like two: the client's machine prefers dark, so the app
   rendered dark while every screenshot of the form he approved was light.
   The two were never different designs, only different themes.

   Committing to the light one settles it, and there is a second reason
   worth more than the first: this is document software, shown to a room. A
   demo that renders differently depending on each viewer's operating system
   setting is a demo nobody can rehearse. If a dark theme is wanted later it
   belongs in both files at once, as a deliberate piece of work. */

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.55;
  -webkit-text-size-adjust: 100%;
}

main { max-width: 1080px; margin: 0 auto; padding: 28px clamp(14px, 4vw, 28px) 64px; }
main.wide { max-width: 1440px; }

/* ---- surfaces ---- */

.card, .stat-tile, .split .pane, .vault-card {
  background: var(--card);
  border: 1px solid var(--line);
}

/* ---- header / nav ---- */

header.app-header {
  display: flex; align-items: center; justify-content: space-between;
  padding: 18px 0; margin-bottom: 26px;
  gap: 16px; flex-wrap: wrap;
  border-bottom: 1px solid var(--line);
}
header.app-header .brand { display: flex; align-items: center; gap: 12px; }
/* The logo carries the product name, so the text beside it is a wayfinding
   crumb rather than a second wordmark. Height is set and width left auto so
   the mark keeps its own proportions whatever file is dropped in. */
header.app-header .brand img.logo { height: 52px; width: auto; display: block; }
header.app-header a.home {
  color: var(--ink); text-decoration: none; font-weight: 700; font-size: 16px;
  font-family: var(--display); letter-spacing: -0.01em;
  display: flex; align-items: center;
}
/* The crumb is the way back, so it has to look like something you can
   click. As inert text it was indistinguishable from a page subtitle, which
   is why nobody found it. */
header.app-header .crumb { color: var(--faint); font-size: 13px; text-decoration: none; }
header.app-header a.crumb { color: var(--dim); cursor: pointer; padding: 4px 8px; border-radius: var(--radius-xs); }
header.app-header a.crumb:hover { color: var(--accent); background: var(--accent-soft); }
header.app-header a.crumb:focus-visible { outline: 2px solid var(--accent); outline-offset: 1px; }
/* Wraps rather than running off the side: five links, the bell and Sign out
   do not fit a 375px screen in one row, and everything else on the page
   already does. Without this the whole app scrolled sideways on a phone --
   the header around it has wrapped since it was built. Seen at phone width,
   2026-09-18. */
nav.app-nav {
  display: flex; align-items: center; gap: 2px;
  flex-wrap: wrap; justify-content: flex-end;
}
nav.app-nav a {
  color: var(--dim); text-decoration: none; font-size: 13.5px; font-weight: 600;
  padding: 8px 14px; border-radius: var(--radius-xs);
}
nav.app-nav a:hover { color: var(--accent); background: var(--accent-soft); }
nav.app-nav a.active { color: var(--accent); background: var(--accent-soft); }
nav.app-nav form { margin: 0; }
nav.app-nav button.link {
  font-family: inherit; font-size: 13.5px; font-weight: 600; color: var(--dim);
  background: none; border: none; padding: 8px 14px; border-radius: var(--radius-xs); cursor: pointer;
}
nav.app-nav button.link:hover { color: var(--danger); background: var(--danger-soft); }

/* Narrow screens: the same links, a little tighter, so they take two rows
   rather than three. */
@media (max-width: 480px) {
  nav.app-nav a, nav.app-nav button.link { padding: 6px 10px; }
}

/* ---- type / layout basics ---- */

/* h1 and h2 are contract-form.css's, to the pixel. A section heading there
   is a readable 19px sentence, not a small uppercase label -- the label
   treatment is what made these screens read as a dashboard rather than as
   the document tool the rest of the product looks like. */
h1 {
  margin: 0 0 4px; font-size: clamp(26px, 4vw, 34px); line-height: 1.15;
  letter-spacing: -.01em; font-weight: 700; text-wrap: balance;
}
h2 {
  margin: 30px 0 10px; font-size: 19px; letter-spacing: -.005em;
  font-weight: 700; color: var(--ink);
}
h2:first-of-type { margin-top: 22px; }
p.lede { color: var(--dim); margin: 12px 0 22px; max-width: 62ch; }

.card { border-radius: var(--radius); padding: 20px 22px; margin-bottom: 16px; }
/* overflow-x rather than overflow: hidden -- a table wider than the card
   (the dashboard's Next deadline and Updated columns are the case that
   found this) used to be clipped outright at phone width instead of made
   reachable. auto still clips the table's square corners to the card's
   rounded ones; it just also scrolls sideways when the content is wider
   than the card, which hidden never did. */
.card.flush { padding: 0; overflow-x: auto; }
.card.login-card { max-width: 380px; padding: 26px 28px; }

/* ---- stat tiles (dashboard summary) ---- */

.stat-row { display: grid; grid-template-columns: repeat(auto-fit, minmax(150px, 1fr)); gap: 12px; margin-bottom: 22px; }
.stat-tile { border-radius: var(--radius); padding: 14px 16px; }
.stat-tile .n { font-variant-numeric: tabular-nums; font-size: 26px; font-weight: 700; letter-spacing: -0.02em; line-height: 1.15; }
.stat-tile .label { color: var(--faint); font-size: 11px; text-transform: uppercase; letter-spacing: .05em; margin-top: 3px; font-weight: 600; }
.stat-tile.warn .n { color: var(--warn); }
.stat-tile.danger .n { color: var(--danger); }
.stat-tile.good .n { color: var(--good); }

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

table { width: 100%; border-collapse: collapse; }
th, td { text-align: left; padding: 12px 16px; border-bottom: 1px solid var(--line); font-size: 14px; vertical-align: middle; }
th { color: var(--faint); font-weight: 700; font-size: 11px; text-transform: uppercase; letter-spacing: .05em; background: var(--paper); }
tr:last-child td { border-bottom: none; }
tbody tr:hover { background: var(--accent-soft); }
a.row-link { color: var(--ink); text-decoration: none; display: block; font-weight: 700; }
a.row-link:hover { color: var(--accent); }
/* A file number is read as one unit (walkthrough 2026-09-21, #20): at a
   narrow width it split into "OMNI-" and "0001" on two lines. It stays whole
   and the address column takes the width instead. */
td.file-token { white-space: nowrap; }
/* "Read these first" (walkthrough #22): a part of a line that is still empty
   says so, in the missing colour, rather than the line reading as complete. */
.key-fields .value a.part { color: inherit; text-decoration: none; }
.key-fields .value a.part:hover { text-decoration: underline; }
.key-fields .value a.missing-part { color: var(--status-missing-ink); font-style: italic; }
.key-fields .value .sep { color: var(--faint); margin: 0 2px; }
.read-rows { display: grid; gap: 8px; }
.read-row { display: grid; grid-template-columns: 200px 1fr; gap: 12px; align-items: baseline; }
.read-label { color: var(--faint); font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; }
.read-value { font-size: 14px; color: var(--ink); }
.read-value.empty { color: var(--faint); font-style: italic; }
@media (max-width: 620px) { .read-row { grid-template-columns: 1fr; gap: 2px; } }

.help.not-needed { color: var(--good); font-weight: 600; margin: 2px 0 6px; }
/* Rule pages (walkthrough #39): the words first, the pack's own code beside
   them in small grey type for whoever maintains the pack. */
.code { font-family: var(--mono, ui-monospace, monospace); font-size: 11px; font-weight: 400; color: var(--faint); margin-left: 6px; }
ul.reads { margin: 0; padding-left: 18px; display: grid; gap: 4px; }
/* Who confirmed a statement figure, on the line itself (walkthrough #32). */
.meta.confirmed-by { display: block; margin-top: 2px; font-size: 12px; }
/* The one "this must be filled" marker (#26), set apart from the label so
   "Commitment number Required" cannot read as one phrase. */
.req-tag {
  display: inline-block; margin-left: 6px; padding: 1px 6px;
  font-size: 10.5px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
  color: var(--status-review-ink); background: var(--status-review-bg);
  border: 1px solid var(--status-review-line); border-radius: 999px;
  vertical-align: 1px;
}
/* A short value ("2026-07-09") wrapping onto two lines reads as two values.
   Scoped to a class rather than every td, since most cells (an address, a
   detail sentence) are supposed to wrap. */
td.date { white-space: nowrap; }

/* Plain links -- an unclassed <a> (the .eml download, a document's file
   link, the transaction-facts jump and "Read by:" links) otherwise renders
   in the browser's default bright blue, which belongs to no other control on
   this screen. :not([class]) so this can never restyle a button, a row
   link, a nav link or the crumb -- all of which already carry their own
   class and their own look. */
main a:not([class]):not(.app-header a) {
  color: var(--accent);
  text-decoration: underline;
  text-underline-offset: 2px;
}
main a:not([class]):not(.app-header a):hover {
  color: var(--accent-strong);
}

/* ---- search ---- */

.search-box { margin-bottom: 14px; }
.search-box input[type="search"] {
  width: 100%; font-family: inherit; font-size: 14px; padding: 11px 14px;
  border: 1px solid var(--line-2); border-radius: var(--radius); background: var(--card); color: var(--ink);
}
.search-box input[type="search"]:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
/* Generic rather than tr-only (2026-09-14) -- the rules page's own
   search hides a whole rule section (a heading plus its card), not just a
   table row, on the same data-hidden attribute the files list already
   uses. */
[data-hidden="true"] { display: none; }

/* ---- badges / chips ---- */

.badge {
  display: inline-block; padding: 3px 10px; border-radius: 999px; font-size: 12px; font-weight: 600;
  background: var(--accent-soft); color: var(--accent-strong); white-space: nowrap;
}
.badge.pending { background: var(--warn-soft); color: var(--warn); }
.badge.confirmed { background: var(--good-soft); color: var(--good); }
.badge.rejected, .badge.stopped { background: var(--danger-soft); color: var(--danger); }
.badge.completed { background: var(--good-soft); color: var(--good); }

.chip {
  display: inline-flex; align-items: center; gap: 6px; padding: 4px 11px; border-radius: 999px;
  font-size: 12px; font-weight: 600; background: var(--paper); color: var(--dim);
  border: 1px solid var(--line); white-space: nowrap;
}
.chip.overdue { background: var(--danger-soft); color: var(--danger); border-color: transparent; }
.chip.approaching { background: var(--warn-soft); color: var(--warn); border-color: transparent; }
.chip.upcoming { background: var(--good-soft); color: var(--good); border-color: transparent; }
.chip.none { color: var(--faint); background: transparent; border-color: var(--line); }

/* A deadline under 24 hours away (due today) -- deliberately louder than
   .chip.overdue's soft fill: this is the one row on the page that still
   needs action before it becomes a passed deadline, so it reads as more
   urgent than "already late," not less. */
.chip.urgent { background: var(--danger); color: #fff; border-color: transparent; }
/* A broken deadline chain (a missing signature, a missing day count) --
   distinct from the ordinary "hasn't started/doesn't apply" neutral chip,
   because unlike those this one needs a person's attention. */
.chip.unresolvable { background: var(--warn-soft); color: var(--warn); border-color: var(--warn-line); }

/* The files list's per-file attention counts (2026-09-16), in the same three
   words the file page uses: Missing, Needs review, Problem. Same palette as
   the tags there -- missing is warn, a problem is danger, review is the
   neutral chip -- so a person reads the dashboard and the file page as one
   thing rather than two vocabularies. */
.chip.missing { background: var(--status-missing-bg); color: var(--status-missing-ink); border-color: transparent; }
.chip.review { background: var(--status-review-bg); color: var(--status-review-ink); border-color: var(--status-review-line); }
.chip.problem { background: var(--status-problem-bg); color: var(--status-problem-ink); border-color: transparent; }
/* Wraps the chips, and sits INSIDE the cell rather than being the cell.
   A <td> with display:flex stops being a table cell: it no longer
   stretches to the height of its row, so its bottom border was drawn 15px
   above every other border on the line and the whole column looked a row
   out of step. Measured on the files list, 2026-09-19. */
.attention-cell { display: flex; flex-wrap: wrap; gap: 4px; }
.stage-cell { display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.stage-cell .attention-cell { margin: 0; }

/* The notification bell (2026-09-16). Sits in the nav, fills itself in from
   /notifications, and says nothing at all when it cannot load -- an error
   nobody can act on is worse than silence here. */
.bell-wrap { position: relative; display: inline-flex; }
button.link.bell { font-size: 15px; line-height: 1; padding: 6px 8px; position: relative; }
.bell-count {
  position: absolute;
  top: 0;
  right: 0;
  min-width: 16px;
  padding: 0 4px;
  border-radius: 999px;
  background: var(--danger);
  color: #fff;
  font-size: 10px;
  font-weight: 700;
  line-height: 16px;
  text-align: center;
}
.bell-count[hidden] { display: none; }
.bell-panel {
  position: absolute;
  top: calc(100% + 8px);
  right: 0;
  z-index: 20;
  width: min(380px, calc(100vw - 32px));
  max-height: 60vh;
  overflow-y: auto;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 10px 30px rgba(0, 0, 0, .12);
  padding: 6px;
  text-align: left;
}
.bell-panel[hidden] { display: none; }
.bell-panel ul { list-style: none; margin: 0; padding: 0; }
.bell-panel li { border-bottom: 1px solid var(--line); }
.bell-panel li:last-child { border-bottom: 0; }
.bell-panel a, .bell-panel .bell-line { display: block; padding: 9px 10px; color: var(--ink); text-decoration: none; font-size: 13px; }
.bell-panel a:hover { background: var(--paper); }
.bell-file { font-weight: 600; color: var(--accent-strong); }
.bell-when { color: var(--faint); font-size: 11.5px; margin-left: 4px; }
.bell-detail { display: block; color: var(--dim); font-size: 12px; margin-top: 2px; }
.bell-empty { padding: 12px; margin: 0; color: var(--faint); font-size: 13px; }

.empty { color: var(--faint); font-style: italic; padding: 14px 0; }

/* A file that has left this person's hands (walkthrough 2026-09-21, #13). */
p.notice.locked-notice {
  background: var(--status-review-bg); border: 1px solid var(--status-review-line);
  color: var(--status-review-ink); padding: 10px 14px; border-radius: var(--radius);
  font-size: 13px; margin: 0 0 14px;
}

/* ---- the in-page confirmation (web/waiting.js) ----
   An action that sends something, or cannot be quietly undone, asks first.
   Drawn in the page rather than through the browser's own pop-up, which
   some browsers never show at all (walkthrough 2026-09-21, #12, #14). */
.confirm-backdrop {
  position: fixed; inset: 0; z-index: 60; display: flex; align-items: center; justify-content: center;
  background: rgba(25, 27, 31, .38); padding: 16px;
}
.confirm-dialog {
  background: var(--card); border: 1px solid var(--line); border-radius: var(--radius);
  box-shadow: 0 12px 32px rgba(25, 27, 31, .22); padding: 20px; max-width: 460px; width: 100%;
}
.confirm-dialog-question { margin: 0; font-size: 15px; font-weight: 700; color: var(--ink); line-height: 1.45; }
.confirm-dialog-body { margin-top: 10px; font-size: 13px; color: var(--dim); line-height: 1.5; }
.confirm-dialog-body ul { margin: 6px 0 0; padding-left: 18px; }
.confirm-dialog-body p { margin: 6px 0 0; }
.confirm-dialog-actions { margin-top: 18px; display: flex; gap: 8px; justify-content: flex-end; }

/* ---- buttons ---- */

button.confirm, a.button {
  font-family: inherit; font-size: 13.5px; font-weight: 600; padding: 10px 18px;
  border-radius: var(--radius); border: 1px solid transparent; background: var(--accent);
  color: #fff; cursor: pointer; text-decoration: none; display: inline-block; line-height: 1.3;
  transition: background .12s ease;
}
button.confirm:hover, a.button:hover { background: var(--accent-strong); color: #fff; }
button.confirm:active, a.button:active { transform: translateY(1px); }
button.confirm.secondary, a.button.secondary {
  background: var(--card); color: var(--ink); border-color: var(--line-2);
}
/* Soft accent wash on hover -- keep readable contrast (not accent text on
   a filled primary background). */
button.confirm.secondary:hover, a.button.secondary:hover {
  color: var(--accent-strong); border-color: var(--accent); background: var(--accent-soft);
}
button.confirm.danger { background: var(--danger); }
button.confirm.danger:hover { filter: brightness(1.08); background: var(--danger); }
button.confirm:disabled { opacity: .5; cursor: not-allowed; }
button.confirm.big { padding: 12px 22px; font-size: 14.5px; }

/* A text button, for an action that is real but minor -- removing one row
   of a list -- where a filled button beside the one that matters would
   compete with it. The nav has had its own button.link since it was built;
   everywhere else the class fell through to the browser's own grey default,
   Arial and an outset border, which is the same defect a class="button" on
   a <button> caused. Seen on the rendered page, 2026-09-18. */
button.link {
  font-family: inherit; font-size: 13px; font-weight: 600; color: var(--dim);
  background: none; border: none; padding: 6px 8px;
  border-radius: var(--radius-xs); cursor: pointer;
}
button.link:hover { color: var(--danger); background: var(--danger-soft); }

/* A folded-away form. The browser gives <summary> the page's own text and
   a default cursor, which reads as a subtitle rather than as something to
   click -- the same way the file breadcrumb went unnoticed while it was
   inert text. It gets the weight and colour of the other things here that
   open something. */
details.fold > summary {
  cursor: pointer; font-size: 13.5px; font-weight: 600; color: var(--accent);
  padding: 8px 2px; width: fit-content;
}
details.fold > summary:hover { color: var(--accent-strong); }
details.fold[open] > summary { margin-bottom: 6px; }

/* One requirement, one row: the status, what happens next, and the two
   actions on a line together. The controls inherit the form styling used
   everywhere else, which lays every field out as a block -- right for a
   form somebody fills in once, wrong for a list where each line has its
   own controls, and a commitment with ten requirements became a page of
   stacked boxes. */
.requirement-actions {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-top: 6px;
}
.requirement-actions form { margin: 0; }
form.requirement-edit {
  display: flex; flex-wrap: wrap; align-items: center; gap: 8px; flex: 1 1 340px;
}
form.requirement-edit select { width: auto; }
form.requirement-edit input[type="text"] { flex: 1 1 200px; width: auto; }

/* Waiting / in-flight (AI calls, slow GETs). Opt in via data-waiting on a
   form, or set .is-waiting / aria-busy on the button from script. */
button.confirm.is-waiting,
a.button.is-waiting {
  position: relative;
  cursor: wait;
  opacity: 1;
  padding-right: 2.4em;
  pointer-events: none;
}
button.confirm.is-waiting:disabled { opacity: 1; cursor: wait; }
button.confirm.secondary.is-waiting:disabled {
  background: var(--accent-soft);
}
button.confirm.is-waiting::after,
a.button.is-waiting::after {
  content: '';
  position: absolute;
  right: 12px; top: 50%;
  width: 14px; height: 14px;
  margin-top: -7px;
  border-radius: 50%;
  border: 2px solid rgba(255, 255, 255, .35);
  border-top-color: #fff;
  animation: button-wait-spin .7s linear infinite;
}
button.confirm.secondary.is-waiting::after {
  border-color: color-mix(in srgb, var(--accent) 35%, transparent);
  border-top-color: var(--accent);
}
@keyframes button-wait-spin {
  to { transform: rotate(360deg); }
}

/* ---- forms ---- */

.field { margin-bottom: 16px; }
.field label { display: block; font-size: 13px; font-weight: 600; color: var(--dim); margin-bottom: 6px; }
/* Every type we render, not five of them. The list used to stop at "tel",
   so the file picker on New file and Upload a document, and the date boxes
   on the contract and commitment forms, were painted by the browser and sat
   next to our own controls looking nothing like them. */
input[type="text"], input[type="password"], input[type="number"], input[type="search"], input[type="tel"],
input[type="email"], input[type="url"], input[type="date"], input[type="file"],
textarea, select {
  font-family: inherit; font-size: 14px; padding: 10px 13px;
  border: 1px solid var(--line-2); border-radius: var(--radius);
  background: var(--card); color: var(--ink); width: 100%;
}
select { width: auto; }
textarea { resize: vertical; }

/* Add a charge or credit (the settlement statement): one row per label,
   each with its own box under it. Left to the ordinary flow, a label
   holding a <select> does not fill its row -- select is width:auto above --
   so the next label came up beside it and read as one row with a stray box
   under it. Seen on the running page, 2026-09-21 (walkthrough #31). */
form.entry-form { display: grid; gap: 12px; }
form.entry-form > label { display: grid; gap: 5px; font-size: 13px; font-weight: 600; color: var(--dim); }
form.entry-form > label select { width: 100%; }
form.entry-form > label.checkbox-row { display: flex; align-items: center; gap: 8px; font-weight: 500; }
form.entry-form > button { justify-self: start; }

/* A file picker is two controls in one: the box, and the browser's own
   button inside it. The button needs dressing separately or it stays the
   platform default -- grey, square, in the system font -- which is the
   same mismatch an unstyled <button> gives, and more obvious because it
   sits an inch from our own Upload button. */
input[type="file"] { padding: 7px 10px; cursor: pointer; }
input[type="file"]::file-selector-button {
  font-family: inherit; font-size: 13.5px; font-weight: 600;
  margin-right: 10px; padding: 8px 14px;
  border: 1px solid var(--line-2); border-radius: var(--radius-xs);
  background: var(--card); color: var(--ink); cursor: pointer;
}
input[type="file"]::file-selector-button:hover {
  color: var(--accent-strong); border-color: var(--accent); background: var(--accent-soft);
}

input:focus, textarea:focus, select:focus { outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent); }
label.checkbox-row { display: flex; align-items: flex-start; gap: 10px; font-size: 13.5px; color: var(--ink); cursor: pointer; }
/* Radios are ticked in the same colour as checkboxes; without this they
   are the operating system's blue, which belongs to no palette here. */
label.checkbox-row input[type="checkbox"], label.checkbox-row input[type="radio"],
input[type="checkbox"], input[type="radio"] { accent-color: var(--accent); }
label.checkbox-row input[type="checkbox"], label.checkbox-row input[type="radio"] {
  margin-top: 3px; width: 16px; height: 16px;
}

form.stack fieldset {
  border: 1px solid var(--line);
  border-radius: var(--radius);
  padding: 14px 16px 4px;
  margin: 0 0 18px;
}
form.stack fieldset legend {
  font-size: 12px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .04em;
  color: var(--faint);
  padding: 0 6px;
}

dl.facts {
  display: grid;
  grid-template-columns: 140px 1fr;
  gap: 8px 16px;
  margin: 0 0 24px;
  font-size: 14.5px;
}
dl.facts dt { color: var(--faint); font-size: 12px; font-weight: 600; text-transform: uppercase; letter-spacing: .04em; }
dl.facts dd { margin: 0; }

.contact-picker { position: relative; }
ul.contact-results {
  list-style: none;
  margin: 4px 0 0;
  padding: 4px;
  position: absolute;
  z-index: 10;
  left: 0;
  right: 0;
  max-height: 220px;
  overflow-y: auto;
  background: var(--card);
  border: 1px solid var(--line);
  border-radius: var(--radius);
  box-shadow: 0 8px 20px rgba(0, 0, 0, .1);
}
ul.contact-results[hidden] { display: none; }
ul.contact-results li { margin: 0; }
ul.contact-results button.link {
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px 10px;
  font-size: 13.5px;
}
ul.contact-results button.link:hover { background: var(--accent-soft); }

.notice {
  padding: 12px 15px; background: var(--warn-soft); border: 1px solid var(--warn-line);
  border-radius: var(--radius); font-size: 13.5px; margin-bottom: 20px; color: var(--warn); font-weight: 500;
}
.notice.danger { background: var(--danger-soft); color: var(--danger); border-color: transparent; }

.meta { color: var(--faint); font-size: 13px; }
.mono { font-family: var(--mono); font-size: 13px; font-variant-numeric: tabular-nums; }

/* ---- checklist (HITL) ---- */

ul.checklist { list-style: none; margin: 0; padding: 0; }
ul.checklist li {
  display: flex; align-items: flex-start; gap: 12px;
  padding: 13px 4px; border-bottom: 1px solid var(--line);
}
ul.checklist li:last-child { border-bottom: none; }
/* Severity dots used to sit in the left gutter; attention/status tags now
   carry that meaning, so the absolute .dot is unused on those lists. Kept
   for checklist rows that still render one (document findings, rules). */
ul.checklist .dot {
  flex: 0 0 auto;
  display: block; width: 9px; height: 9px; border-radius: 50%;
  margin-top: 6px;
  background: var(--faint);
}
ul.checklist li.stop .dot { background: var(--danger); }
ul.checklist li.review .dot { background: var(--warn); }
ul.checklist li.done .dot { background: var(--good); }
ul.checklist .item-body { flex: 1; min-width: 0; }
ul.checklist .item-title { font-weight: 600; font-size: 14px; }
ul.checklist .item-detail { color: var(--dim); font-size: 13px; margin-top: 2px; }
/* A resolved item is greyed rather than struck through. Line-through on a
   wrapped two-line detail reads as damage rather than as completion, and on
   this screen the resolved rows still have to be readable -- they say who
   confirmed a gate and when. */
ul.checklist li.done .item-title, ul.checklist li.done .item-detail { color: var(--faint); }

.checklist-footer {
  margin-top: 16px; padding-top: 16px; border-top: 1px solid var(--line);
  display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap;
}

/* ---- split screen (document review) ---- */

.split {
  display: grid; grid-template-columns: 1fr 1fr; gap: 16px; align-items: start;
  min-height: 70vh;
}
@media (max-width: 860px) { .split { grid-template-columns: 1fr; } }
.split .pane {
  border-radius: var(--radius); overflow: hidden; position: sticky; top: 16px;
}
.split .pane-header {
  padding: 11px 15px; font-size: 11px; font-weight: 700;
  color: var(--faint); text-transform: uppercase; letter-spacing: .06em;
  display: flex; align-items: center; justify-content: space-between; gap: 12px;
  border-bottom: 1px solid var(--line); background: var(--paper);
}
.split .pane-body { padding: 0; }
.split .field-list { max-height: 78vh; overflow-y: auto; padding: 0; }

/* ---- PDF viewer + field-location highlighting ---- */

.pdf-nav { display: flex; align-items: center; gap: 6px; text-transform: none; letter-spacing: 0; }
.pdf-nav button {
  font-family: inherit; font-size: 13px; font-weight: 700; width: 26px; height: 26px; line-height: 1;
  border-radius: var(--radius-xs); border: 1px solid var(--line-2); background: var(--card);
  color: var(--dim); cursor: pointer;
}
.pdf-nav button:hover:not(:disabled) { border-color: var(--accent); color: var(--accent); }
.pdf-nav button:disabled { opacity: .4; cursor: not-allowed; }
.pdf-nav-sep {
  width: 1px; height: 16px; background: var(--line-2); margin: 0 2px;
}
.pdf-nav button.pdf-zoom-label {
  width: auto; min-width: 48px; padding: 0 8px; font-size: 11.5px; font-weight: 600;
  letter-spacing: 0; font-variant-numeric: tabular-nums;
}
.pdf-nav button.pdf-tool {
  width: auto; padding: 0 8px; font-size: 11.5px; font-weight: 600; letter-spacing: 0;
}
.pdf-nav button.pdf-tool.on {
  background: var(--accent-soft); border-color: var(--accent); color: var(--accent);
}
.pdf-viewer-wrap { position: relative; height: 78vh; overflow: auto; background: #525659; }
.pdf-viewer-wrap.is-panning { cursor: grab; }
.pdf-viewer-wrap.is-panning.is-dragging {
  cursor: grabbing;
  user-select: none;
}
/* Intrinsic canvas size (set in JS from the zoomed viewport). Do not force
   width:100% -- that would shrink a zoomed-in page back to the pane and
   make +/- appear to do nothing. */
.pdf-viewer-wrap canvas { display: block; width: auto; height: auto; max-width: none; }
.pdf-viewer-wrap.is-panning canvas { cursor: inherit; }
.pdf-highlight-layer { position: absolute; top: 0; left: 0; pointer-events: none; }
.pdf-highlight-box {
  position: absolute; border: 2px solid var(--warn); background: rgba(232, 163, 61, .22);
  border-radius: 2px;
}

.meta.stale { color: var(--warn); font-weight: 600; }

.field-row { padding: 11px 16px; border-bottom: 1px solid var(--line); display: flex; align-items: flex-start; gap: 12px; }
.field-row:last-child { border-bottom: none; }
.field-row .field-main { flex: 1; min-width: 0; }
.field-row .field-name { font-size: 11px; color: var(--faint); text-transform: uppercase; letter-spacing: .05em; font-weight: 600; }
.field-row .field-value { font-size: 14.5px; margin-top: 2px; word-break: break-word; }
.field-row .field-value.not-observed { color: var(--faint); font-style: italic; }
.field-row .field-value.confirmed-absent { color: var(--dim); }
.field-row.verified { background: var(--good-soft); }
button.verify-toggle {
  font-family: inherit; font-size: 12px; font-weight: 600; padding: 5px 11px; border-radius: var(--radius-xs);
  border: 1px solid var(--line-2); background: var(--card); color: var(--dim); cursor: pointer; flex: 0 0 auto;
}
button.verify-toggle:hover { border-color: var(--accent); color: var(--accent); }
button.verify-toggle.on { background: var(--good); color: #fff; border-color: transparent; }

/* ---- wire vault ---- */

.vault-card { border-radius: var(--radius); padding: 22px 24px; }
.vault-card h2 { color: var(--danger); margin-top: 0; }
.vault-steps { list-style: none; margin: 16px 0; padding: 0; }
.vault-steps li { padding: 12px 0; border-top: 1px solid var(--line); font-size: 14px; }
.vault-steps li:first-child { border-top: none; }

/* ---- dashboard activity feed ---- */

.activity-panel { padding: 16px 18px 8px; }
.activity-filters { margin-bottom: 12px; }
.activity-filter-row {
  display: flex; flex-wrap: wrap; gap: 12px 18px; margin-top: 10px;
}
.files-filters { margin-bottom: 18px; }
.activity-filter {
  display: flex; flex-direction: column; gap: 4px; min-width: 140px;
}
.activity-filter select {
  font: inherit; font-size: 13.5px; padding: 7px 10px;
  border: 1px solid var(--line-2); border-radius: var(--radius);
  background: var(--card); color: var(--ink);
}
.activity-table { width: 100%; border-collapse: collapse; }
.activity-table th {
  text-align: left; font-size: 12px; font-weight: 600; letter-spacing: .04em;
  text-transform: uppercase; color: var(--dim); padding: 8px 10px;
  border-bottom: 1px solid var(--line);
}
.activity-table td {
  padding: 10px; border-bottom: 1px solid var(--line); vertical-align: top;
  font-size: 14px;
}
.activity-table tr[hidden] { display: none; }
.activity-table .item-title { font-weight: 600; }
.activity-pager {
  display: flex; flex-wrap: wrap; align-items: center; gap: 12px;
  justify-content: flex-end; margin: 12px 0 4px; padding-top: 4px;
}
.activity-pager[hidden] { display: none; }
.activity-pager .meta { margin: 0; }

.pill-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 0 0 14px; }
.pill {
  font-family: inherit; font-size: 12.5px; font-weight: 600; padding: 7px 13px;
  border-radius: 999px; border: 1px solid var(--line-2); background: var(--card);
  color: var(--dim); cursor: pointer;
}
.pill:hover { border-color: var(--accent); color: var(--accent); }
.pill.active {
  color: #fff; background: var(--accent); border-color: transparent;
}

th.sortable { cursor: pointer; user-select: none; }
th.sortable:hover { color: var(--accent); }
/* The arrow is generated rather than written into the markup so the sort
   indicator can never be left behind on a column that is no longer sorted. */
th.sortable[data-sorted="asc"]::after { content: " \2191"; color: var(--accent); }
th.sortable[data-sorted="desc"]::after { content: " \2193"; color: var(--accent); }

/* One consistent keyboard-focus ring, added everywhere it was missing.
   Before this, only the crumb and form inputs showed any focus state at
   all, so tabbing through a page like the dashboard gave no visible sign of
   where you were -- including th.sortable, which is only reachable at all
   once it carries tabindex (see filesListPage.js). */
a:focus-visible,
button:focus-visible,
summary:focus-visible,
.pill:focus-visible,
th.sortable:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* A replies card with something awaiting review should be seen before it's
   read -- an accent rule on one edge, not a full recolour, so it doesn't
   compete with an actual STOP/REVIEW card. */
.card.replies-awaiting { border-left: 3px solid var(--accent); }

/* ---- outstanding information, and the draft request ---- */

details.outstanding > summary {
  cursor: pointer; font-weight: 600; color: var(--accent);
  padding: 2px 0; user-select: none;
}
details.outstanding[open] > summary { margin-bottom: 10px; }
.outstanding-table { width: 100%; border-collapse: collapse; }
.outstanding-table td { padding: 8px 0; border-top: 1px solid var(--line); font-size: 14px; }
.outstanding-table tr:first-child td { border-top: none; }

/* The draft is shown as written, wrapped rather than scrolled sideways: a
   person approving an email should not have to scroll to read a line of it. */
.draft-body {
  white-space: pre-wrap; word-break: break-word; font-family: inherit;
  font-size: 14px; line-height: 1.6; color: var(--ink); margin: 0;
  padding: 14px 16px; border-radius: var(--radius); background: var(--paper);
  border: 1px solid var(--line);
}

/* ---- sign-in ---- */

/* Two panels: the brand on the left, the form on the right.
   The layout is taken from the client's own printed collateral rather than
   invented -- on the business card the lockup sits large at the top, a short
   blue rule sits under it, and the type below is confident and widely
   spaced. A sign-in screen is the one place a product introduces itself, so
   it borrows that rather than centring a lone box on an empty page.

   Splits at 900px and stacks below it. The brand panel comes FIRST in the
   markup so the stacked order on a phone is logo, then form -- reading
   order and visual order stay the same, which is what keyboard and screen
   reader users get either way. */
body.signin { margin: 0; }
body.signin main { max-width: none; padding: 0; }

.signin-split {
  display: grid;
  grid-template-columns: 1fr;
  min-height: 100vh;
}
@media (min-width: 900px) {
  .signin-split { grid-template-columns: 1.05fr 1fr; }
}

/* Left: the brand. A slightly deeper ground than the page so the two panels
   read as separate surfaces without a hard divider. */
.signin-brand-panel {
  display: flex; flex-direction: column; justify-content: center;
  gap: 22px;
  padding: clamp(32px, 6vw, 72px);
  background: #eeece7;
  border-bottom: 1px solid var(--line);
}
@media (min-width: 900px) {
  .signin-brand-panel { border-bottom: none; border-right: 1px solid var(--line); }
}
.signin-brand-panel img.lockup {
  height: clamp(38px, 5vw, 62px); width: auto; align-self: flex-start;
}

/* The card's own motif: a short blue rule. This is the one place the
   brand's bright blue is used rather than the interface's slate -- it is an
   accent on a brand panel, which is exactly what it is on the card. */
.signin-rule {
  width: 56px; height: 5px; border-radius: 3px; background: #2858f8;
}

.signin-tagline {
  margin: 0; max-width: 26ch;
  font-size: clamp(20px, 2.4vw, 27px); line-height: 1.25;
  letter-spacing: -.02em; font-weight: 700; color: var(--ink);
}
.signin-sub {
  margin: 0; max-width: 42ch; color: var(--dim); font-size: 15px;
}

/* Right: the form. Centred in its own panel, capped so the fields stay a
   comfortable width on a wide screen rather than stretching. */
.signin-form-panel {
  display: flex; align-items: center; justify-content: center;
  padding: clamp(32px, 6vw, 72px);
  background: var(--paper);
}
.signin-form-inner { width: 100%; max-width: 360px; }

.signin-form-inner h1 { font-size: 24px; margin: 0 0 22px; letter-spacing: -.015em; }
.signin-form-inner .lede { margin: 0 0 22px; font-size: 14.5px; color: var(--dim); }

.signin-form-inner .field { margin-bottom: 16px; }
.signin-form-inner label {
  display: block; font-size: 13px; font-weight: 600; color: var(--dim); margin-bottom: 6px;
}
.signin-form-inner input {
  width: 100%; font-family: inherit; font-size: 15px; padding: 11px 13px;
  border: 1px solid var(--line-2); border-radius: var(--radius);
  background: var(--card); color: var(--ink);
}
.signin-form-inner input:focus {
  outline: 2px solid var(--accent); outline-offset: -1px; border-color: var(--accent);
}
/* Full width and 44px tall: the only action on the screen, and the minimum
   comfortable touch target on a phone. */
.signin-form-inner button.confirm {
  width: 100%; padding: 12px 18px; font-size: 15px; min-height: 44px; margin-top: 4px;
}
.signin-foot { margin: 20px 0 0; font-size: 12.5px; color: var(--faint); }

@media (prefers-reduced-motion: reduce) {
  * { animation-duration: .01ms !important; transition-duration: .01ms !important; }
}

/* ---- landing on something from a link elsewhere ----
   Generic counterpart to contract-form.css's .field.is-target /
   .rider.is-target, for form-focus.js landings on a page that is not the
   contract form -- the transaction facts page's own "Record <fact>" links
   land on a <tr>, which carries neither class. The motion this could add
   (a fade-in, say) is already neutralised by the reduced-motion rule
   above; there is none here to begin with. */
tr.is-target,
.field-row.is-target {
  outline: 3px solid var(--accent);
  outline-offset: -2px;
  box-shadow: 0 0 0 4px var(--accent-soft) inset;
}
