/* ---------- Google Fonts ----------
   The whole system runs on two families: Montserrat for headings and
   Inter for everything else. Every layout (home, client, admin) loads
   this file, so this is the one place either family is fetched or
   named — page stylesheets read the variables below instead. */
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=Montserrat:wght@600;700;800;900&display=swap');

/* =============================================
   GLOBAL VARIABLES
   ============================================= */
:root {
  --border-light: rgba(255, 255, 255, 0.15);
  --awg-primary: #16A249;
  --awg-primary-dark: #128a3d;
  --awg-primary-light: rgba(255, 255, 255, 0.12);
  --awg-light: #f4f2ef;
  --awg-green-hover: #f0faf4;
  /* ← light green hover bg on buttons */
  --awg-primary-deep: #0f6b30;
  /* ← readable brand green for text on --awg-green-hover */
  --sb-width: 240px;
  --text-muted: rgba(255, 255, 255, 0.75);
  --white-text: #ffffff;
  --body-text: #212529;
  /* ← dark text used everywhere */
  --muted-text: #6c757d;
  /* ← secondary/muted text */
  --border-color: #dee2e6;
  /* ← standard border */
  --input-text: #495057;
  /* ← form/filter text */
  --light-bg: #e9ecef;
  /* ← light gray backgrounds */
  --labor-bg: #f8f9fa;
  --dark-blue: #374151;
  --label-color: #FFFFFFD9;
  --projects-text: #FFFFFFB3;

  /* ---------- Fonts, system-wide ---------- */
  --font-display: 'Montserrat', sans-serif;
  --font-body: 'Inter', sans-serif;
  /* Bootstrap reads this for the body, buttons and form controls. */
  --bs-body-font-family: var(--font-body);
}

/* =============================================
   TYPOGRAPHY
   ============================================= */
body {
  font-family: var(--font-body);
}

h1,
h2,
h3,
h4,
h5,
h6,
.font-display {
  font-family: var(--font-display);
}


.fs-46 {
  font-size: 46px !important;
}

.fs-42 {
  font-size: 42px !important;
}

.fs-40 {
  font-size: 40px !important;
}

.fs-22 {
  font-size: 22px !important;
}

.fs-20 {
  font-size: 20px !important;
}

.fs-18 {
  font-size: 18px !important;
}

.fs-17 {
  font-size: 17px !important;
}

.fs-16 {
  font-size: 16px !important;
}

.fs-15 {
  font-size: 15px !important;
}

.fs-14 {
  font-size: 14px !important;
}

.fs-12 {
  font-size: 12px !important;
}

.fs-11 {
  font-size: 11px !important;
}

/* =============================================
   line height
   ============================================= */

.lh-17 {
  line-height: 1.7 !important;
}

.lh-105 {
  line-height: 1.05 !important;
}

.lh-12 {
  line-height: 1.2 !important;
}

/* =============================================
   TEXT COLOR
   ============================================= */
.green-text {
  color: var(--awg-primary) !important;
}

.dblue-text {
  color: var(--dark-blue) !important;
}

.muted-text {
  color: var(--muted-text) !important;
}

.label-text {
  color: var(--label-color) !important;
}

.projects-text {
  color: var(--label-color) !important;
}

/* =============================================
   WIDTHS
   ============================================= */


.w-40 {
  width: 40px !important;
}

.w-70 {
  width: 70px !important;
}

.w-90 {
  width: 90px !important;
}

.w-140 {
  width: 140px !important;
}

.w-145 {
  width: 145px !important;
}

/* Only forces the fixed 180px width at sm+, where the hero stat
   cards switch to a 4-across row (col-sm-3). Below that they're
   col-6 (2 across) and need their natural 50% width — a fixed
   180px there doesn't fit two per row next to the row gap, so
   the second card wraps to its own line. */
@media (min-width: 576px) {
  .w-180 {
    width: 180px !important;
  }
}

.mw-720 {
  max-width: 720px !important;
}

.mw-640 {
  max-width: 640px !important;
}

.mw-600 {
  max-width: 600px !important;
}

.mw-500 {
  max-width: 500px !important;
}

/* width percentage */
.wp-10 {
  width: 10% !important;
}

.wp-15 {
  width: 15% !important;
}

.wp-20 {
  width: 20% !important;
}

.wp-30 {
  width: 30% !important;
}

.wp-40 {
  width: 40% !important;
}

.wp-55 {
  width: 55% !important;
}

.wp-65 {
  width: 65% !important;
}

.wp-100 {
  width: 100% !important;
}

/* =============================================
   HEIGHTS
   ============================================= */
.mh-400 {
  max-height: 400px !important;
}

.hpx-6 {
  height: 6px !important;
}

.hpx-40 {
  height: 40px !important;
}

.hpx-50 {
  height: 50px !important;
}

.hpx-200 {
  height: 200px !important;
}

.hpx-260 {
  height: 260px !important;
}

/* =============================================
   BORDERS
   ============================================= */
.light-border {
  border-color: var(--border-light) !important;
}

.borr-10 {
  border-radius: 10px !important;
}

/* =============================================
   BACKGROUNDS
   ============================================= */
.bg-awg-primary {
  background-color: var(--awg-primary) !important;
}

.bg-awg-light {
  background-color: var(--awg-light) !important;
}

.bg-labor {
  background-color: var(--labor-bg) !important;
}

/* =============================================
   POINTER EVENTS
   ============================================= */
.pe-none {
  pointer-events: none !important;
}

/* =============================================
   BADGES
   ============================================= */
.badge {
  font-size: 10px;
  padding: 3px 8px;
  font-weight: 500;
}

/* =============================================
   BRAND ALERT
   Green info panel in the brand palette. Bootstrap's .alert-success is a
   different green (#198754), so it reads as a second green next to
   anything using --awg-primary. Pair with .border-0 like the other
   alerts in the app; the icon inside takes .green-text.
   ============================================= */
.alert-brand {
  /* Bootstrap 5.3 drives .alert through these custom properties; the
     literal declarations keep it correct on older 5.x too. */
  --bs-alert-color: var(--awg-primary-deep);
  --bs-alert-bg: var(--awg-green-hover);
  --bs-alert-border-color: transparent;
  --bs-alert-link-color: var(--awg-primary-deep);
  background-color: var(--awg-green-hover);
  color: var(--awg-primary-deep);
}

.alert-brand strong {
  color: var(--awg-primary-deep);
}

/* =============================================
   BUTTONS
   ============================================= */
/* Bootstrap 5.3 resolves every button state through these custom
   properties, and .btn:disabled (0,2,0) outranks a plain .btn-primary
   (0,1,0) override — so a disabled button fell back to Bootstrap blue
   however the base colour was set. Setting the properties themselves
   recolours all states, disabled included. */
.btn-primary {
  --bs-btn-bg: var(--awg-primary);
  --bs-btn-border-color: var(--awg-primary);
  --bs-btn-hover-bg: var(--awg-primary-dark);
  --bs-btn-hover-border-color: var(--awg-primary-dark);
  --bs-btn-active-bg: var(--awg-primary-dark);
  --bs-btn-active-border-color: var(--awg-primary-dark);
  --bs-btn-disabled-bg: var(--awg-primary);
  --bs-btn-disabled-border-color: var(--awg-primary);
  background-color: var(--awg-primary);
  border-color: var(--awg-primary);
}

.btn-primary:hover {
  background-color: var(--awg-primary-dark);
  border-color: var(--awg-primary-dark);
}

.btn-outline-primary {
  --bs-btn-color: var(--awg-primary);
  --bs-btn-border-color: var(--awg-primary);
  --bs-btn-hover-bg: var(--awg-primary);
  --bs-btn-hover-border-color: var(--awg-primary);
  --bs-btn-active-bg: var(--awg-primary);
  --bs-btn-active-border-color: var(--awg-primary);
  --bs-btn-disabled-color: var(--awg-primary);
  --bs-btn-disabled-border-color: var(--awg-primary);
  color: var(--awg-primary);
  border-color: var(--awg-primary);
}

.btn-outline-primary:hover {
  background-color: var(--awg-primary);
  border-color: var(--awg-primary);
  color: #fff;
}

/* One green. Bootstrap's "success" (#198754) sat beside the brand green
   (#16A249) on half the buttons in the system; both are the primary action
   here, so success buttons take the brand colours too. Buttons are primary
   (green) or secondary (grey), with red kept for destructive actions; other
   colours are for badges and statuses. */
.btn-success {
  --bs-btn-bg: var(--awg-primary);
  --bs-btn-border-color: var(--awg-primary);
  --bs-btn-hover-bg: var(--awg-primary-dark);
  --bs-btn-hover-border-color: var(--awg-primary-dark);
  --bs-btn-active-bg: var(--awg-primary-dark);
  --bs-btn-active-border-color: var(--awg-primary-dark);
  --bs-btn-disabled-bg: var(--awg-primary);
  --bs-btn-disabled-border-color: var(--awg-primary);
}

.btn-outline-success {
  --bs-btn-color: var(--awg-primary);
  --bs-btn-border-color: var(--awg-primary);
  --bs-btn-hover-bg: var(--awg-primary);
  --bs-btn-hover-border-color: var(--awg-primary);
  --bs-btn-active-bg: var(--awg-primary);
  --bs-btn-active-border-color: var(--awg-primary);
  --bs-btn-disabled-color: var(--awg-primary);
  --bs-btn-disabled-border-color: var(--awg-primary);
}

/* =============================================
   CARDS
   ============================================= */
.card {
  transition: transform 0.2s, box-shadow 0.2s;
}

.card:hover {
  transform: translateY(-2px);
  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15) !important;
}

/* =============================================
   TABLES  
   ============================================= */
.table thead th {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.5px;
  color: var(--awg-primary);
  background-color: #f8f9fa;
}

.table tbody td {
  vertical-align: middle;
  font-size: 13px;
}

.green-th {
  color: var(--awg-primary) !important;
  font-size: 0.7rem;
  text-transform: uppercase;
  letter-spacing: 0.04em;
}

/* The Actions column of a data table.

   width:1% is the shrink-to-fit trick, not a real width: a table laid out at
   100% gives this column the smallest width its content allows and hands the
   slack to the other columns. That is what puts the buttons against the right
   edge of the table.

   It has to be the column that shrinks rather than the cell that is
   text-end, because the number of buttons varies per row — a row awaiting
   approval offers reschedule and cancel, one whose form is in does not.
   Right-aligning each cell independently lines the rows up by their *last*
   button, so the first one, View, lands in a different place on every row and
   the eye icons stagger down the column. Left-aligned content in a
   shrink-wrapped column lines them up by their first button instead, which is
   the one that is always there. */
.col-actions {
  width: 1%;
  white-space: nowrap;
}

/* =============================================
   SUMMARY / STAT CARDS
   ============================================= */
.summary-card {
  background-color: #fff;
  border-radius: 10px;
  box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
  padding: 16px;
  display: flex;
  align-items: center;
  gap: 14px;
  /* Fills its grid column's stretched height (Bootstrap rows default to
     align-items: stretch) so cards with a two-line label ("Done
     Assessment") don't end up taller than one-line siblings ("Total"). */
  height: 100%;
}

.summary-icon {
  font-size: 32px;
  flex-shrink: 0;
}

.summary-label {
  font-size: 11px;
  font-weight: 600;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-text);
  margin-bottom: 2px;
}

.summary-value {
  font-size: 1.6rem;
  font-weight: 600;
  color: var(--body-text);
  margin-bottom: 0;
  line-height: 1;
}

/* A breakdown of the value above rather than a count of its own — for a
   figure that is a subset of the card's total and would double-count if it
   were given a card beside it (e.g. "1 needs contract" under Approved). */
.summary-sub {
  font-size: 11px;
  font-weight: 600;
  margin-top: 4px;
  margin-bottom: 0;
  line-height: 1.2;
}

@media (max-width: 575.98px) {
  .summary-icon  { font-size: 26px; }
  .summary-value { font-size: 1.3rem; }
  .summary-label { font-size: 10px; }
}

/* =============================================
   FILTER BUTTON GROUP
   ============================================= */
/* A .btn-group never wraps its buttons (they rely on shared borders and
   corners between siblings), so on any width too narrow for the whole row,
   a tablet or a narrowed window as much as a phone, the labels wrapped
   inside their buttons and the row ran past the card. The row stays one
   line and scrolls sideways within the card instead. */
.filter-btn-group {
  display: inline-flex;
  flex-wrap: nowrap;
  max-width: 100%;
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
}

.filter-btn-group .btn {
  font-size: 12.5px;
  padding: 5px 14px;
  border-color: var(--border-color);
  color: var(--input-text);
  flex-shrink: 0;
  white-space: nowrap;
}

.filter-btn-group .btn.active {
  background-color: var(--awg-primary);
  border-color: var(--awg-primary);
  color: #fff;
  font-weight: 600;
}

.filter-btn-group .btn:hover:not(.active) {
  background-color: var(--awg-green-hover);
  color: var(--awg-primary);
}

/* Wrapped onto two lines as separate buttons was tried on tablets and
   phones (2026-09-30) and set aside: the joined row that scrolls sideways
   is kept at every width, on every page's table. */

@media (max-width: 575.98px) {
  /* Smaller on a phone, so more of the row fits before it scrolls. */
  .filter-btn-group .btn {
    font-size: 12px;
    padding: 6px 12px;
  }
}

/* =============================================
   TABLE ACTION BUTTONS
   ============================================= */
.icon-action {
  font-size: 16px;
  vertical-align: middle;
}

/* =============================================
   TOP HEADER
   ============================================= */
.top-header {
  height: 80px;
  z-index: 10;
  background-color: var(--awg-primary);
  border-bottom: 1px solid var(--border-light) !important;
}

/* =============================================
   DATATABLES — PAGINATION & SPACING
   ============================================= */
.dataTables_wrapper .pagination .page-item.active .page-link {
  background-color: var(--awg-primary) !important;
  border-color: var(--awg-primary) !important;
  color: #fff !important;
}

.dataTables_wrapper .dataTables_filter,
.dataTables_wrapper .dataTables_length {
  margin-bottom: 12px;
}

.dataTables_wrapper table.dataTable {
  margin-top: 10px !important;
}

/* "Search:" and its box stay on one line, but the box shrinks to fit a phone
   instead of running past the card. */
.dataTables_wrapper .dataTables_filter label {
  display: inline-flex;
  align-items: center;
  gap: .5em;
  max-width: 100%;
}
.dataTables_wrapper .dataTables_filter input {
  flex: 1 1 auto;
  min-width: 0;
  margin-left: 0 !important;
}

.dataTables_wrapper .dataTables_paginate .paginate_button.current,
.dataTables_wrapper .dataTables_paginate .paginate_button.current:hover {
  background: var(--awg-primary) !important;
  border-color: var(--awg-primary) !important;
  color: #fff !important;
  border-radius: 6px;
}

.dataTables_wrapper .dataTables_paginate .paginate_button:not(.disabled):hover {
  background: rgba(22, 162, 73, 0.1) !important;
  border-color: var(--awg-primary) !important;
  color: var(--awg-primary) !important;
  border-radius: 6px;
}

.dataTables_wrapper .dataTables_paginate .paginate_button {
  border-radius: 6px;
}

/* Only the rows scroll sideways; Show entries, Search and paging stay put
   (js/includes/datatable-scroll.js). */
.table-responsive.dt-holder {
  overflow-x: visible;
}

.dt-scroll-x {
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
}

/* With the outer box no longer scrolling, a long row of page numbers has
   to wrap instead of pushing the page wide. */
.dataTables_wrapper .dataTables_paginate .pagination {
  flex-wrap: wrap;
  row-gap: 4px;
}

@media (max-width: 575.98px) {
  .dataTables_wrapper .dataTables_paginate .pagination {
    justify-content: center !important;
  }
}

/* ---------- Disable Bootstrap's built-in validation icons ---------- */
.was-validated .auth-input:invalid,
.was-validated .auth-input:valid,
.auth-input.is-invalid,
.auth-input.is-valid {
  background-image: none !important;
}

/* Bootstrap marks an invalid field by colouring its border — and .auth-input
   sets that border with !important, so the red never landed and a rejected
   field looked exactly like an accepted one.

   That went unnoticed while every field also printed a message underneath it.
   The password pair now reports through a toast instead, which says what is
   wrong but not which box, so the border is the only thing left to say where.

   Higher specificity than .auth-input, so it wins whichever stylesheet the
   page loads last. */
.was-validated .auth-input:invalid,
.auth-input.is-invalid {
  border-color: #dc3545 !important;
}

.was-validated .auth-input:invalid:focus,
.auth-input.is-invalid:focus {
  border-color: #dc3545 !important;
  box-shadow: 0 0 0 3px rgba(220, 53, 69, .12) !important;
}

#password.auth-input,
#password_confirmation.auth-input {
  padding-right: 2.75rem !important;
}

/* ---------- Fix invalid-feedback visibility when input sits inside a wrapper div ---------- */
.was-validated .input-icon-wrap:has(:invalid)~.invalid-feedback,
.input-icon-wrap:has(.is-invalid)~.invalid-feedback,
.was-validated .auth-input:invalid~.invalid-feedback,
.auth-input.is-invalid~.invalid-feedback,
.was-validated .form-check:has(:invalid)~.invalid-feedback,
.form-check:has(.is-invalid)~.invalid-feedback {
  display: block;
}

/* =============================================
   LIVE PASSWORD CHECKLIST
   Shared by Register and Reset Password. It lives here rather than in either
   page's own stylesheet because both show the identical checklist, and
   utilities.css is loaded by the home layout for every page — so the two
   cannot drift into looking different for the same rule.
   The fp- prefix is kept from when this was reset-password-only, so the
   existing markup and password-rules.js keep matching.
   ============================================= */
.fp-strength-hint {
  display: flex;
  flex-direction: column;
  gap: .375rem;
}

.fp-strength-item {
  display: flex;
  align-items: center;
  gap: .5rem;
  font-size: .8125rem;
  color: var(--muted-text);
  margin: 0;
  transition: color .2s ease;
}

.fp-strength-item .material-symbols-outlined {
  font-size: 16px;
  color: #d1d5db;
  transition: color .2s ease;
}

.fp-strength-item.fp-rule-met {
  color: #065f46;
}

.fp-strength-item.fp-rule-met .material-symbols-outlined {
  color: var(--awg-primary);
}
/* The row a link opened (?open=, js/linked-record.js): marked so the eye lands
   on it when the page turns to it. Brand green, with a stripe on the first
   cell so it still reads as marked beside a row that is merely hovered. */
.linked-record-row > td {
  background: #dcf5e3 !important;
}

.linked-record-row > td:first-child {
  box-shadow: inset 4px 0 0 var(--awg-primary);
}

/* A control whose action is under way (js/includes/busy-buttons.js): the
   spinner stands where its icon was, and the pointer says it is working. */
.is-busy {
  cursor: progress !important;
  pointer-events: none;
}
.is-busy > .material-symbols-outlined,
.is-busy > .bi {
  display: none !important;
}
.is-busy > .busy-spinner {
  width: 0.9rem;
  height: 0.9rem;
  border-width: 0.15em;
  vertical-align: -0.125em;
}

/* ── Compact modals ─────────────────────────────────────────────────────
   Every details window reads like Staff Details: small label, value right
   under it, rows close together. Several pages never styled detail-label or
   detail-value, so each one kept the browser's 16px paragraph gap and the
   windows grew tall enough to scroll. Scoped to .modal, and more specific
   than a page's own .detail-label, so one rule set covers them all. */
.modal .modal-header {
  padding-top: 0.75rem;
  padding-bottom: 0.75rem;
}

.modal .modal-body {
  padding-top: 0.85rem;
  padding-bottom: 0.85rem;
}

.modal .modal-footer {
  padding-top: 0.5rem;
  padding-bottom: 0.5rem;
}

.modal .row.g-3 {
  --bs-gutter-y: 0.6rem;
}

.modal .section-label {
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  color: var(--muted-text);
  margin-bottom: 0;
}

.modal .detail-label {
  font-size: 12px;
  color: var(--muted-text);
  margin-bottom: 1px;
}

.modal .detail-value {
  margin-bottom: 0;
}

.modal hr {
  margin-top: 0.25rem;
  margin-bottom: 0.25rem;
}

.modal .form-label {
  margin-bottom: 0.25rem;
}

/* Assessor pick lists (Confirm on Requests, Reschedule on the Schedule): two
   per row, so a longer crew does not push the fields below out of view. One
   per row on a phone. */
.modal .assessor-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));
  gap: 0.5rem;
}

/* Columns by headcount, so rows come out even: 2 or 4 technicians two
   across (4 as 2 by 2), 3 or more three across, one on its own. Stacks on
   a phone, and three across drops to two on a narrow window. */
.modal .assessor-grid.assessor-cols-1 { grid-template-columns: minmax(0, 1fr); }
.modal .assessor-grid.assessor-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.modal .assessor-grid.assessor-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }

@media (max-width: 767.98px) {
  .modal .assessor-grid.assessor-cols-3 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 575.98px) {
  .modal .assessor-grid.assessor-cols-2,
  .modal .assessor-grid.assessor-cols-3 { grid-template-columns: minmax(0, 1fr); }
}

/* A technician who is out at that time, and why (js/admin/technician-slot.js). */
.assessor-check-row.assessor-busy {
  background: #fef3f2;
}

.assessor-busy-reason {
  display: block;
  font-size: 11px;
  line-height: 1.25;
  color: #b42318;
}

/* The row wraps, so a busy technician's reason takes a line of its own under
   the name and tags, the full width of the card, instead of a narrow column
   beside the name. */
.assessor-check-row { flex-wrap: wrap; font-size: 14px; row-gap: 4px; }
.assessor-check-row > .assessor-busy-reason { flex-basis: 100%; }

a.assessor-busy-reason { text-decoration: underline; }

.assessor-free-tag {
  flex-shrink: 0;
  font-size: 11px;
  font-weight: 600;
  line-height: 1;
  padding: 4px 8px;
  border-radius: 999px;
  background: #dcfce7;
  color: #166534;
}

/* =============================================
   PAGE TABS (Tasks: Task List / Technician Availability;
   Items: Items / Templates). Same look as the Schedule page's tabs.
   ============================================= */
.task-tabs { display: inline-flex; gap: 0; }
.task-tabs .nav-link {
  color: #6c757d; background-color: #e9ecef; border: none; border-radius: 0;
  font-weight: 500; font-size: 14px; padding: 10px 20px; min-width: 150px; text-align: center;
}
.task-tabs .nav-item:first-child .nav-link { border-radius: 6px 0 0 6px; }
.task-tabs .nav-item:last-child .nav-link { border-radius: 0 6px 6px 0; }
.task-tabs .nav-link.active {
  color: #212529; background-color: #fff; font-weight: 600; box-shadow: 0 1px 3px rgba(0, 0, 0, .1);
}
.task-tabs .nav-link:hover:not(.active) { background-color: #dee2e6; color: #495057; }

/* Below a laptop width the tabs and the page buttons no longer fit one row:
   the tabs take the full row in equal halves and the buttons go above them,
   to the right, at the top of the page as on pages without tabs. */
@media (max-width: 991.98px) {
  .task-tab-row { flex-wrap: wrap; }
  .task-tabs { display: flex; width: 100%; }
  .task-tabs .nav-item { flex: 1 1 0; min-width: 0; }
  .task-tabs .nav-link { width: 100%; min-width: 0; padding: 10px 8px; }
  .task-tab-actions { margin-left: auto; order: -1; }
}

/* ---------- Password fields ----------
   Every password box has our own eye button to show the password. Edge
   adds its own eye inside the box as well, which showed two; its one is
   hidden so ours is the only one, the same in every browser. */
/* Two rules, not one list: a browser drops a whole rule over one selector
   it does not know, and only Edge knows these. */
input[type="password"]::-ms-reveal {
  display: none;
}
input[type="password"]::-ms-clear {
  display: none;
}

/* A summary card's line that lists those rows ("3 need form", "1 needs
   contract"), under the "Showing the …" bar (partials.list-focus-banner). */
.summary-filter {
  display: block; background: transparent; border: 0; padding: 0; text-align: left;
  text-decoration: underline; text-underline-offset: 2px; cursor: pointer;
}
.summary-filter:hover { text-decoration-thickness: 2px; }

/* The schedule detail window's Tasks and Assessment Forms boxes
   (partials.visit-tasks / visit-forms): one height, so the two columns
   line up row for row whether a box holds a badge or a button. */
.visit-box { min-height: 48px; }
