/* vendor and customer contracts, Ask Claude, the contract modal. Part of the stylesheet split by page; public/css/README.md says why. */
/* ── Modals ───────────────────────────────────────────────── */
.modal-note {
  margin: 14px 0 0;
  font-size: 12px;
  line-height: 1.5;
  color: var(--text-muted);
}

.modal-note code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 11px;
}

/* ── Vendor Contracts (admin-only) ────────────────────────── */
#contracts-view,
#customer-contracts-view { padding: 1rem 1rem 2rem; }


#contracts-table-container,
#customer-contracts-table-container {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 8px;
  overflow-x: auto;
}
/* table-layout: fixed so the caps below are obeyed. Without it the browser
   sizes columns to content and one long contract title or vendor name pushes
   the money columns off the right-hand side. */
/* min-width, not just width: with table-layout: fixed the column widths below
   only hold if the table is allowed to be as wide as their sum. Squeezed
   narrower, the browser satisfies the pixel columns first and gives the
   others nothing — see the comment on the width list. */
.contracts-table {
  width: 100%;
  /* The sum of the column widths below. Lower than that and the browser
     starts starving columns again; keep the two in step. */
  min-width: 1392px;
  table-layout: fixed;
  border-collapse: collapse;
  font-size: 12px;
}
.contracts-table th,
.contracts-table td {
  padding: 8px 10px; text-align: left;
  border-bottom: 1px solid var(--border); vertical-align: top;
  /* Long values are truncated rather than allowed to widen the column; the
     full text stays available through the cell's title attribute. */
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}

/* Widths are capped per column, keyed to the column's name rather than its
   position — nth-child rules shifted the moment a column was added. The
   identity column gets the room because it carries the vendor and the title;
   everything else is sized to its content, so a long signatory name can no
   longer squeeze the renewal date out of view.

   Every width is in pixels on purpose. Mixing percentages with pixels under
   table-layout: fixed collapsed the percentage columns to ZERO on a phone —
   the pixel columns already exceeded the container, so Vendor / Contract,
   Signed By and Business Owner disappeared entirely, and min-width does not
   rescue a table cell the way it would a block. Pixels everywhere plus a
   min-width on the table keeps the sum deterministic at any viewport. */
.contracts-table .col-vendor_name          { width: 220px; }
.contracts-table .col-document_kind        { width: 74px; }
.contracts-table .col-mediakind_signee     { width: 120px; }
.contracts-table .col-business_owner       { width: 110px; }
.contracts-table .col-status               { width: 74px; }
.contracts-table .col-term_months          { width: 62px; }
.contracts-table .col-renewal_date         { width: 92px; }
.contracts-table .col-notice_period_days   { width: 62px; }
.contracts-table .col-notice_deadline      { width: 150px; }
.contracts-table .col-auto_renews          { width: 76px; }
.contracts-table .col-one_time_cost        { width: 96px; }
.contracts-table .col-recurring_cost       { width: 112px; }
.contracts-table .col-total_contract_value { width: 96px; }
.contracts-table .col-file_count           { width: 48px; }

/* The identity cell is the one place wrapping is wanted: vendor on one line,
   title beneath it. */
.contracts-table td:first-child { white-space: normal; }

/* ...and it stays put while the rest scrolls sideways. On a phone the table is
   far wider than the screen, so scrolling to reach Renewal or Recurring used
   to carry the vendor and title off the left edge, leaving rows you could read
   but not identify. */
.contracts-table th:first-child,
.contracts-table td:first-child {
  position: sticky;
  left: 0;
  z-index: 2;
  background: var(--surface);
}
.contracts-table th:first-child { z-index: 4; }
.contracts-group > td:first-child { background: var(--total-bg); }
/* The sticky column needs its own right edge, since the cell it used to sit
   against may have scrolled away underneath it. */
.contracts-table th:first-child::after,
.contracts-table td:first-child::after {
  content: '';
  position: absolute;
  top: 0; bottom: -1px; right: 0;
  border-right: 1px solid var(--border);
}
.contracts-table tbody tr:hover td:first-child { background: var(--hover-bg); }
/* Two lines at most. The cell wraps so vendor and title stack, but a
   90-character title would otherwise make one row four lines tall and push
   everything else down the page. */
.contracts-vendor, .contracts-title {
  display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2;
  overflow: hidden; text-overflow: ellipsis;
}
.contracts-vendor { -webkit-line-clamp: 1; }

/* The converted figure sits under the contract's own amount. */
.contracts-usd { font-size: 10px; color: var(--text-muted); font-weight: 300; }
.contracts-table th {
  position: sticky; top: 0;
  background: var(--total-bg);
  font-size: 11px; font-weight: 700;
  text-transform: uppercase; letter-spacing: .04em;
  color: var(--text-muted);
  user-select: none;
  /* Headers wrap to two lines rather than clip ("Business Owner…"). */
  white-space: normal; line-height: 1.25; vertical-align: bottom;
}
.contracts-table th.sorted { color: var(--accent-text); }
/* The sort control is a real button so it is keyboard-reachable; it inherits
   the header's type so it still reads as a column heading. */
.contracts-sort {
  padding: 0; border: none; background: none; cursor: pointer;
  font: inherit; color: inherit; text-transform: inherit; letter-spacing: inherit;
}
.contracts-sort:focus-visible { outline: 2px solid var(--accent); outline-offset: 3px; border-radius: 2px; }
.contracts-table .num { text-align: right; font-variant-numeric: tabular-nums; }
.contracts-table tbody tr { cursor: pointer; }
.contracts-table tbody tr:hover,
.contracts-table tbody tr:focus-visible { background: var(--hover-bg); outline: none; }
.contracts-table tbody tr:last-child td { border-bottom: none; }
.contracts-vendor { font-weight: 600; }
.contracts-cycle {
  margin-left: 6px; padding: 0 6px; border-radius: 999px; vertical-align: 1px;
  background: var(--bg); border: 1px solid var(--border);
  font-size: 10px; font-weight: 600; color: var(--text-muted); text-transform: uppercase; letter-spacing: .03em;
}
.contracts-sub { font-size: 11px; color: var(--text-muted); font-weight: 300; }
.contracts-muted { color: var(--text-muted); }
.contracts-empty { padding: 3rem 1rem; text-align: center; display: flex; flex-direction: column; gap: 6px; }

.contracts-status { font-size: 11px; font-weight: 600; }
.contracts-status--active     { color: var(--text); font-weight: 400; }
.contracts-status--pending    { color: var(--growth-warn); }
.contracts-status--expired,
.contracts-status--terminated { color: var(--text-muted); }

/* Vendor grouping: a vendor row with its contracts nested beneath it. */
.contracts-group > td {
  background: var(--total-bg);
  border-top: 1px solid var(--border-strong);
  border-bottom: 1px solid var(--border-strong);
  vertical-align: middle;
  font-weight: 600;
}
.contracts-group > td.contracts-sub { font-weight: 400; color: var(--text); }
.contracts-group > td:first-child { position: sticky; }
/* One line, always: the name gives way (ellipsis) before the count or the
   badge wrap underneath it and double the row's height. The Merge button is
   positioned over the cell's right edge so it never competes for that line. */
.contracts-group-toggle {
  display: flex; align-items: center; gap: 8px; flex-wrap: nowrap;
  padding: 0 56px 0 0; border: none; background: none; cursor: pointer;
  font: inherit; text-align: left; width: 100%; color: inherit;
}
.contracts-group-toggle .contracts-vendor { min-width: 0; flex: 0 1 auto; }
.contracts-group-toggle .contracts-count,
.contracts-group-toggle .pill { flex: none; }
.contracts-group > td:first-child .contracts-merge-btn {
  position: absolute; right: 8px; top: 50%; transform: translateY(-50%); margin: 0;
}
/* Empty values recede so the figures that are there stand out. */
.contracts-table td.contracts-dash,
.contracts-table td .contracts-muted { color: color-mix(in srgb, var(--text-muted) 40%, transparent); }
/* A deadline: the date, then how far off it is as a small pill, or as plain
   text when it is not close. */
.contracts-deadline { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }
.contracts-days { color: var(--text-muted); font-size: 11px; }
.contracts-group-toggle:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; border-radius: 4px; }
.contracts-caret { color: var(--text-muted); font-size: 10px; width: 10px; flex: none; }
.contracts-count {
  min-width: 18px; padding: 0 6px; border-radius: 999px; text-align: center;
  background: var(--bg); border: 1px solid var(--border);
  font-size: 11px; line-height: 16px; color: var(--text-muted); font-weight: 500;
}
.contracts-review-btn:hover { background: var(--amber-bg-strong); }

/* The indent is what carries the parent/child relationship visually. */
.contracts-child { padding-left: 30px !important; }
.contracts-row > td:first-child { box-shadow: inset 2px 0 0 var(--border-strong); }
/* Smaller than the vendor row above it: the vendor is the heading, the
   contract title is detail under it, and matching sizes made the hierarchy
   hard to read at a glance. */
.contracts-title { font-weight: 600; font-size: 11px; }
.contracts-row.is-unreviewed > td:first-child { box-shadow: inset 3px 0 0 var(--amber-border); }

/* Batch upload progress */
.contracts-batch {
  margin-bottom: .75rem; padding: 10px 12px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 8px;
  display: flex; flex-direction: column; gap: 8px; font-size: 12px;
}
.contracts-batch-head { display: flex; align-items: center; gap: 8px; }
.contracts-batch-close { margin-left: auto; padding: 3px 8px; font-size: 11px; }
.contracts-batch-counts { display: flex; gap: 12px; flex-wrap: wrap; align-items: center; }
.contracts-progress { height: 4px; background: var(--bg); border-radius: 999px; overflow: hidden; }
.contracts-progress-bar { height: 100%; background: var(--accent); transition: width .3s ease; }
.contracts-batch-list summary { cursor: pointer; color: var(--text-muted); }
.contracts-batch-list ul { margin: 6px 0 0 18px; display: flex; flex-direction: column; gap: 3px; }
.contracts-batch-list li { overflow-wrap: anywhere; }

/* Contract modal */
.contract-section { border: none; padding: 0; margin: 0; }
.contract-section legend {
  font-size: 12px; font-weight: 700; color: var(--accent-text);
  padding: 0; margin-bottom: 6px;
}
.contract-grid { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 10px 14px; }
.contract-grid--wide { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.contract-field { display: flex; flex-direction: column; gap: 4px; min-width: 0; }

/* .modal-box only styles text/password inputs and selects; the contract form
   also uses date, number and textarea. */
#contract-modal input,
#contract-modal select,
#contract-modal textarea,
#customer-contract-modal input,
#customer-contract-modal select,
#customer-contract-modal textarea {
  padding: 8px 10px;
  border: 1px solid var(--border);
  border-radius: 6px;
  font: inherit;
  font-size: 13px;
  outline: none;
  width: 100%;
  background: var(--surface);
}
#contract-modal textarea,
#customer-contract-modal textarea { resize: vertical; }
#contract-modal textarea:focus,
#customer-contract-modal textarea:focus { border-color: var(--accent); box-shadow: 0 0 0 3px rgba(0,79,137,.15); }
#contract-modal .contract-input--code,
#customer-contract-modal .contract-input--code { text-transform: uppercase; }
#contract-modal .ai-filled,
#customer-contract-modal .ai-filled { background: var(--actual-bg); border-color: var(--brand-cyan); }

.contract-hint { display: flex; flex-wrap: wrap; align-items: baseline; gap: 4px 6px; font-size: 11px; color: var(--text-muted); }
.contract-hint-tag { font-weight: 700; text-transform: uppercase; letter-spacing: .04em; font-size: 10px; color: var(--brand-cyan); }
.contract-hint--medium .contract-hint-tag { color: var(--growth-warn); }
.contract-hint--low .contract-hint-tag { color: var(--danger); }
.contract-hint-value { font-weight: 600; color: var(--text); }
.contract-hint-apply {
  padding: 1px 8px; font-size: 11px; font-weight: 600;
  color: var(--accent-text); background: var(--surface);
  border: 1px solid var(--accent); border-radius: 999px; cursor: pointer;
}
.contract-hint-apply:hover { background: var(--hover-bg); }
.contract-hint-quote { flex-basis: 100%; font-style: italic; overflow-wrap: anywhere; }

.contract-analysis { padding: 10px 12px; border-radius: 8px; font-size: 12px; display: flex; flex-direction: column; gap: 6px; border: 1px solid var(--border); }
.contract-analysis--working { flex-direction: row; align-items: center; gap: 10px; background: var(--actual-bg); border-color: var(--blue-border); }
.contract-analysis--done    { background: var(--actual-bg); border-color: var(--blue-border); }
.contract-analysis--failed  { background: var(--red-bg); border-color: var(--red-border); align-items: flex-start; }
.contract-analysis--skipped { background: var(--forecast-bg); border-color: var(--amber-border); }
.contract-analysis-summary { color: var(--text-muted); }
.contract-analysis-warnings ul { margin: 2px 0 0 18px; }
.contract-spinner {
  flex: none; width: 14px; height: 14px; border-radius: 50%;
  border: 2px solid var(--blue-border); border-top-color: var(--accent);
  animation: contract-spin .8s linear infinite;
}
@keyframes contract-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) { .contract-spinner { animation: none; } }

.contract-error { padding: 8px 12px; border-radius: 6px; background: var(--red-bg); color: var(--danger); font-size: 12px; font-weight: 600; }

.contract-documents { border-top: 1px solid var(--border); padding-top: 12px; display: flex; flex-direction: column; gap: 8px; }
.contract-doc {
  display: grid; grid-template-columns: minmax(0, 1fr) 160px auto auto; align-items: center; gap: 10px;
  padding: 8px 0; border-bottom: 1px solid var(--border); font-size: 12px;
}
.contract-doc-main { min-width: 0; }
.contract-doc-name {
  padding: 0; border: none; background: none; cursor: pointer;
  font: inherit; font-weight: 600; color: var(--accent-text); text-align: left;
  max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
}
.contract-doc-name:hover { text-decoration: underline; }
.contract-doc-error { color: var(--danger); font-size: 11px; white-space: normal; }
.contract-doc-status { font-size: 11px; font-weight: 600; color: var(--text-muted); }
.contract-doc-status--done { color: var(--growth-pos); }
.contract-doc-status--failed { color: var(--danger); }
.contract-doc-actions { display: flex; gap: 4px; }
.contract-doc-actions .btn-ghost { padding: 4px 8px; font-size: 11px; }
.contract-doc-remove:hover { color: var(--danger); border-color: var(--red-border); }
.contract-attach { display: flex; gap: 8px; align-items: center; }
#contract-modal .contract-attach select,
#customer-contract-modal .contract-attach select { width: 200px; }

.contract-history-list { max-height: 60vh; overflow-y: auto; display: flex; flex-direction: column; font-size: 12px; }
.contract-history-row { padding: 8px 0; border-bottom: 1px solid var(--border); overflow-wrap: anywhere; }
.contract-history-old { color: var(--text-muted); text-decoration: line-through; }

@media (max-width: 760px) {
  .contract-grid, .contract-grid--wide { grid-template-columns: 1fr; }
  .contract-doc { grid-template-columns: 1fr; }
}
#contract-form-body,
#customer-contract-form-body { display: flex; flex-direction: column; gap: 14px; }
.contract-doc.is-hidden { opacity: .6; }
.contract-doc-hidden > summary { cursor: pointer; color: var(--text-muted); font-size: 11px; padding: 6px 0; }
.contracts-kind { font-size: 11px; font-weight: 600; }
.contracts-kind--signed_order { color: var(--growth-pos); }
.contracts-kind--quote {
  color: var(--amber-fg); background: var(--forecast-bg);
  border: 1px solid var(--amber-border); border-radius: 999px; padding: 1px 7px;
}
.contracts-kind--unsigned { color: var(--danger); }
.contracts-kind--unknown { color: var(--text-muted); font-weight: 300; }

/* Committed-cost projection under the contracts table */
.contracts-forecast {
  margin-top: 1rem; padding: 12px 14px 14px;
  background: var(--surface); border: 1px solid var(--border); border-radius: 8px;
}
.contracts-forecast-head { display: flex; align-items: baseline; gap: .75rem; flex-wrap: wrap; margin-bottom: 8px; }
.contracts-forecast-head h3 { font-size: 13px; font-weight: 700; color: var(--accent-text); }
.contracts-forecast-canvas { height: 220px; }
@media (max-width: 600px) { .contracts-forecast-canvas { height: 260px; } }

/* ── Customer Contracts (admin-only) ──────────────────────
   The table reuses .contracts-table wholesale; only the column widths differ,
   because the columns do. Same rule as the vendor table: every width is in
   pixels, keyed to the column's NAME, and .customer-contracts-table's
   min-width is their sum — keep the two in step or the browser starts
   starving columns on a narrow screen. */
.customer-contracts-table { min-width: 1412px; }
.customer-contracts-table .col-customer_name        { width: 220px; }
.customer-contracts-table .col-document_kind        { width: 74px; }
.customer-contracts-table .col-doc_types            { width: 140px; }
.customer-contracts-table .col-account_owner        { width: 110px; }
.customer-contracts-table .col-status               { width: 74px; }
.customer-contracts-table .col-term_months          { width: 62px; }
.customer-contracts-table .col-renewal_date         { width: 92px; }
.customer-contracts-table .col-notice_period_days   { width: 62px; }
.customer-contracts-table .col-notice_deadline      { width: 150px; }
.customer-contracts-table .col-auto_renews          { width: 76px; }
.customer-contracts-table .col-one_time_fee         { width: 96px; }
.customer-contracts-table .col-recurring_fee        { width: 112px; }
.customer-contracts-table .col-total_contract_value { width: 96px; }
.customer-contracts-table .col-file_count           { width: 48px; }
/* Chips wrap, so this one cell is allowed to be as tall as it needs. */
.customer-contracts-table .col-doc_types { white-space: normal; }

/* Which paperwork is on file. The three that matter — MSA, NDA, order form —
   are solid; anything else is outlined, so a glance down the column reads as
   "what is missing" rather than "what exists". */
.doc-chips { display: flex; flex-wrap: wrap; gap: 3px; }
.doc-chip {
  font-size: 10px; font-weight: 600; letter-spacing: .02em;
  padding: 1px 6px; border-radius: 999px;
  border: 1px solid var(--border-strong); color: var(--text-muted);
}
.doc-chip--key { border-color: transparent; color: #fff; background: var(--brand-cyan); }
.doc-chip--msa { background: var(--accent); }
.doc-chip--nda { background: #0f766e; }
.doc-chip--order_form { background: var(--brand-cyan); }

.contract-ask-btn:disabled { opacity: .5; cursor: not-allowed; }

/* ── Ask Claude about a contract ──────────────────────────
   A chat panel, not a form: the log grows, the composer stays put, and the
   answer is plain text laid out by renderAnswer() — never HTML. */
/* .modal-box--wide scrolls its own content; here only the log should,
   or the composer slides off the bottom as the conversation grows. The log
   is the one flexible child, so it is what shrinks on a short screen. */
/* A fixed height rather than max-height: sized to the content, the panel sat
   half-empty in the middle of a tall window while the transcript — the part
   you actually read — was the smallest thing in it. Now it takes the room
   that is there and the log, as the one flexible child, gets all of it. */
.chat-box {
  display: flex;
  flex-direction: column;
  gap: 10px;
  height: min(88vh, 880px);
  max-height: 88vh;
  overflow: hidden;
}
.chat-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 1rem; }
.chat-head-text { min-width: 0; }
.chat-head h2 { margin: 0; }
.chat-head-actions { display: flex; gap: 6px; flex: none; }
.chat-head-actions .btn-ghost { padding: 5px 10px; font-size: 12px; }
.chat-sources {
  margin-top: 3px; font-size: 11px; color: var(--text-muted);
  overflow-wrap: anywhere;
  /* Contract filenames run long, and on a phone an eight-line one pushed the
     conversation itself off the screen. */
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.chat-source { font-weight: 600; color: var(--text); }
.chat-warn { color: var(--growth-warn); }

.chat-log {
  /* No max-height: the box above has a height, so this fills whatever is
     left once the header, the composer and the note have taken theirs. */
  flex: 1 1 auto; min-height: 160px; overflow-y: auto;
  display: flex; flex-direction: column; gap: 12px;
  padding: 12px; border: 1px solid var(--border); border-radius: 8px;
  background: var(--bg);
}
.chat-msg { display: flex; flex-direction: column; gap: 3px; max-width: 86%; }
.chat-msg--user { align-self: flex-end; align-items: flex-end; }
.chat-role {
  font-size: 10px; font-weight: 700; text-transform: uppercase;
  letter-spacing: .04em; color: var(--text-muted);
}
.chat-body {
  padding: 9px 12px; border-radius: 10px; font-size: 13px; line-height: 1.5;
  background: var(--surface); border: 1px solid var(--border);
  overflow-wrap: anywhere;
}
.chat-msg--user .chat-body { background: var(--accent); border-color: var(--accent); color: #fff; }
.chat-body p + p, .chat-body p + .chat-list, .chat-body .chat-list + p { margin-top: 7px; }
.chat-list { margin: 0 0 0 18px; display: flex; flex-direction: column; gap: 3px; }
.chat-error { color: var(--danger); font-weight: 600; }

.chat-suggestions { display: flex; flex-wrap: wrap; gap: 6px; }
.chat-suggestions.hidden { display: none; }
.chat-chip {
  font: inherit; font-size: 12px; cursor: pointer;
  padding: 5px 11px; border-radius: 999px;
  border: 1px solid var(--border-strong); background: var(--surface); color: var(--text);
}
.chat-chip:hover { background: var(--hover-bg); border-color: var(--accent); color: var(--accent-text); }

.chat-status { font-size: 11px; color: var(--text-muted); min-height: 14px; }
/* `.modal-box form` further up sets flex-direction: column, and at (0,2,0) it
   outranks a bare `.chat-form` — which left the composer stacked and, because
   of the align-items below, shrunk into the bottom-right corner. Matched
   specificity, and the direction stated rather than inherited. */
.modal-box .chat-form {
  display: flex;
  flex-direction: row;
  gap: 8px;
  align-items: flex-end;
}
.modal-box .chat-form textarea {
  flex: 1 1 auto;
  width: 100%;
  /* Three lines to start. A contract question is usually a sentence, and a
     two-line box made it feel like a search field. */
  min-height: 92px;
  max-height: 40vh;
  resize: vertical;
  font: inherit;
  font-size: 13px;
  line-height: 1.45;
  padding: 10px 12px;
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
}
.modal-box .chat-form textarea:focus { outline: none; border-color: var(--accent); box-shadow: 0 0 0 3px rgba(0,79,137,.15); }
.modal-box .chat-form button { flex: none; min-height: 40px; padding-left: 22px; padding-right: 22px; }
.chat-note { font-size: 11px; color: var(--text-muted); }

/* The empty transcript, before anyone has asked anything. Without it the
   panel opens on a large blank panel that reads as something failing to
   load. */
.chat-empty {
  margin: auto; text-align: center; max-width: 42ch;
  color: var(--text-muted); font-size: 12px; line-height: 1.6;
}
.chat-empty strong { display: block; color: var(--text); font-size: 13px; margin-bottom: 2px; }

@media (max-width: 760px) {
  .chat-msg { max-width: 100%; }
  .chat-box { height: min(92vh, 720px); }
}


/* A document that arrived as Word and was converted on the way in. */
.contract-doc-original { font-size: 11px; }
.contract-doc-original button {
  font: inherit; color: var(--accent-text); background: none; border: none;
  padding: 0; cursor: pointer; text-decoration: underline;
}


/* The Merge control on a group row, on both contract pages. Kept quiet until the row is
   hovered or it takes focus: it is an occasional cleanup action, and one that
   rewrites every contract under the group, so it should not read as an
   invitation on every row at rest. Always rendered, never display:none, so it
   stays reachable by keyboard. */
.contracts-merge-btn {
  margin-left: 8px;
  padding: 1px 8px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: transparent;
  color: var(--text-muted);
  font-size: 11px;
  cursor: pointer;
  opacity: 0;
  /* Invisible AND inert. At opacity 0 alone it still takes a click, so a
     stray tap near the group name opened a dialog that renames every
     contract in the group. Keyboard focus is unaffected by pointer-events,
     so :focus-visible below still reaches it. */
  pointer-events: none;
  transition: opacity .12s ease;
}
/* :focus as well as :focus-visible. focus-visible depends on the browser's
   heuristic for "was this keyboard?", and where it does not match, the button
   would be focused, invisible and inert at once -- the worst of the three. */
.contracts-group:hover .contracts-merge-btn,
.contracts-merge-btn:focus,
.contracts-merge-btn:focus-visible { opacity: 1; pointer-events: auto; }
.contracts-merge-btn:hover { color: var(--accent-text); border-color: var(--accent); }

/* A touch screen has no hover to reveal it with, so there it is simply
   always shown rather than permanently unreachable. */
@media (hover: none) {
  .contracts-merge-btn { opacity: 1; pointer-events: auto; }
}

