/* =====================================================
   36 Rule — application shell and components

   Structure mirrors the site4 wireframe:
     header.topnav        navy bar: logo, hamburger, global menu
     .scopebar            search + selected-business scope
     .contextbar          dark bar: current scope + secondary menu
     .workspace           the page itself
   The wireframe styled its tables with inline attributes; here
   the same visuals are classes, so the markup stays readable.
   ===================================================== */

* { box-sizing: border-box; }

/* Present to screen readers and to browser find, invisible to the eye. */
.sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

html, body {
  margin: 0;
  padding: 0;
  height: 100%;
}

body {
  background: var(--color-off-white);
  color: var(--color-body-text);
  font-family: var(--font-1);
  font-size: 14px;
  line-height: 1.5;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); text-decoration: none; }
a:hover { text-decoration: underline; }

/* =====================================================
   TOP NAV
   ===================================================== */

.topnav {
  display: flex;
  align-items: center;
  gap: var(--s-16);
  height: 50px;
  padding: 0 var(--s-16);
  background: var(--dark);
  color: var(--color-white);
  position: sticky;
  top: 0;
  z-index: 300;
}

.topnav__mark {
  font-family: var(--font-2);
  font-weight: 700;
  font-size: 18px;
  color: var(--color-white);
  flex-shrink: 0;
}

.topnav__brand {
  font-weight: 700;
  font-size: 17px;
  letter-spacing: -0.01em;
  color: var(--color-white);
  white-space: nowrap;
  flex-shrink: 0;
}
.topnav__brand:hover { text-decoration: none; }

.topnav__menu {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  overflow-x: auto;
  scrollbar-width: none;
}
.topnav__menu::-webkit-scrollbar { display: none; }

.topnav__link {
  color: rgba(255, 255, 255, .82);
  padding: 6px 10px;
  border-radius: var(--r-sm);
  font-size: 13px;
  font-weight: 500;
  white-space: nowrap;
  transition: background var(--t-fast), color var(--t-fast);
}
.topnav__link:hover {
  background: rgba(255, 255, 255, .10);
  color: var(--color-white);
  text-decoration: none;
}
.topnav__link.is-active {
  background: var(--accent);
  color: var(--dark-deep);
  font-weight: 600;
}

.topnav__spacer { margin-left: auto; }

.topnav__who {
  font-size: 12.5px;
  font-weight: 600;
  color: rgba(255, 255, 255, .82);
  white-space: nowrap;
  padding-left: var(--s-4);
}

/* Hamburger — opens the left pop-out */
.drawer-toggle {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--color-white);
  cursor: pointer;
  flex-shrink: 0;
}
.drawer-toggle:hover { background: rgba(255, 255, 255, .12); }
.drawer-toggle svg { width: 18px; height: 18px; }

/* =====================================================
   LEFT POP-OUT DRAWER
   ===================================================== */

.drawer {
  position: fixed;
  inset: 0 auto 0 0;
  width: 288px;
  max-width: 86vw;
  background: var(--dark-deep);
  color: var(--color-white);
  transform: translateX(-100%);
  transition: transform var(--t-med);
  z-index: 500;
  overflow-y: auto;
  padding-bottom: var(--s-32);
}
.drawer[data-open="true"] { transform: translateX(0); }

/* --- the edge tab ---
   The live site's .side-tab-btn: a slim tab pinned to the left edge that slides
   out to sit against the pop-out's edge when it opens. Sits above .drawer
   (z-index 500) so it rides that edge rather than vanishing under it. Unlike
   the hamburger it is not in the top bar, so it stays put.

   Shown at EVERY width, because it is now the only way to #recentDrawer — the
   hamburger opens the menu and nothing else. Hiding it on desktop, as it was
   when one pop-out did both jobs, would leave the business browser unreachable
   there. */
.side-tab {
  display: inline-flex;
  position: fixed;
  left: 0;
  top: 50vh;
  transform: translateY(-50%);
  align-items: center;
  justify-content: center;
  width: 22px;
  height: 52px;
  padding: 0;
  background: var(--accent);
  color: var(--dark-deep);
  border: 1px solid var(--accent-dark);
  border-left: none;
  border-radius: 0 var(--r-md) var(--r-md) 0;
  box-shadow: 2px 0 8px rgba(0, 0, 0, .18);
  cursor: pointer;
  z-index: 501;
  transition: left var(--t-med);
}
/* The tab is 22px wide to match the live site, which is well under any usable
   touch target. Widen the hit area rather than the tab. */
.side-tab::before {
  content: "";
  position: absolute;
  top: -8px;
  bottom: -8px;
  left: 0;
  right: -22px;
}
.side-tab svg {
  width: 13px;
  height: 13px;
  transition: transform var(--t-med);
}
/* Open: ride the pop-out's edge and turn the chevron back on itself.
   .drawer is 288px but caps at 86vw, so the tab has to track the same cap. */
.side-tab[aria-expanded="true"] { left: min(288px, 86vw); }
.side-tab[aria-expanded="true"] svg { transform: rotate(180deg); }

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  height: 50px;
  padding: 0 var(--s-16);
  background: var(--dark-deeper);
}
.drawer__title {
  font-weight: 700;
  font-size: 14px;
  letter-spacing: .04em;
  text-transform: uppercase;
  color: var(--accent);
}
.drawer__close {
  border: 0;
  background: transparent;
  color: var(--color-white);
  font-size: 20px;
  line-height: 1;
  cursor: pointer;
  padding: 4px 6px;
  border-radius: var(--r-sm);
}
.drawer__close:hover { background: rgba(255, 255, 255, .12); }

/* Who is signed in. The top bar drops this on narrow screens; this is where
   it goes instead, so the name and Sign out are never unreachable. */
.drawer__who {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-8);
  padding: var(--s-8) var(--s-16);
  font-size: 12.5px;
  font-weight: 600;
  color: rgba(255, 255, 255, .82);
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}
.drawer__who a {
  font-size: 11.5px;
  font-weight: 600;
  color: var(--accent);
  flex-shrink: 0;
}

.drawer__group {
  padding: var(--s-16) var(--s-16) var(--s-8);
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .45);
}

.drawer__link {
  display: flex;
  align-items: center;
  gap: var(--s-8);
  padding: 9px var(--s-16);
  color: rgba(255, 255, 255, .86);
  font-size: 13.5px;
}
.drawer__link:hover {
  background: rgba(255, 255, 255, .08);
  color: var(--color-white);
  text-decoration: none;
}
.drawer__link.is-active {
  background: var(--color-1-transparent-3);
  color: var(--color-white);
  box-shadow: inset 3px 0 0 var(--accent);
  font-weight: 600;
}
.drawer__link--child { padding-left: var(--s-32); font-size: 13px; }

.drawer-scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, .45);
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--t-med);
  z-index: 400;
}
.drawer-scrim[data-open="true"] { opacity: 1; pointer-events: auto; }

/* --- drawer: recent items --- */
.drawer__group { display: flex; align-items: baseline; justify-content: space-between; }
.drawer__clear {
  font-size: 10px;
  font-weight: 600;
  letter-spacing: .04em;
  text-transform: none;
  color: var(--accent);
}
.drawer__clear:hover { color: var(--accent-light); }

.drawer__recent { align-items: flex-start; gap: 9px; }
.drawer__ico {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 22px; height: 22px;
  border-radius: var(--r-sm);
  background: rgba(255, 255, 255, .10);
  color: var(--accent);
  flex-shrink: 0;
  margin-top: 1px;
}
.drawer__rtext { min-width: 0; }
.drawer__rlabel {
  display: block;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.drawer__rsub {
  display: block;
  font-size: 11px;
  color: rgba(255, 255, 255, .48);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

/* --- drawer: business browser --- */
.drawer__search { padding: 0 var(--s-16) var(--s-8); }
.drawer__search input {
  width: 100%;
  padding: 7px 10px;
  font: inherit;
  font-size: 13px;
  color: var(--color-white);
  background: rgba(255, 255, 255, .10);
  border: 1px solid rgba(255, 255, 255, .18);
  border-radius: var(--r-md);
}
.drawer__search input::placeholder { color: rgba(255, 255, 255, .45); }
.drawer__search input:focus {
  outline: none;
  border-color: var(--accent);
  background: rgba(255, 255, 255, .16);
}

.drawer__results { max-height: 40vh; overflow-y: auto; }
.drawer__hint {
  padding: 6px var(--s-16) var(--s-12);
  font-size: 11.5px;
  color: rgba(255, 255, 255, .45);
}
.drawer__add {
  color: var(--accent);
  font-weight: 600;
  border-top: 1px solid rgba(255, 255, 255, .10);
  margin-top: var(--s-4);
}

/* =====================================================
   RECENT STRIP — the wireframe's "Recent Tabs" row
   ===================================================== */

.recentbar {
  display: flex;
  align-items: center;
  gap: var(--s-8);
  padding: 7px var(--s-16);
  background: var(--color-cream);
  border-bottom: 1px solid var(--color-border);
  overflow: hidden;
}
.recentbar__label {
  font-size: 10px;
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--color-muted);
  flex-shrink: 0;
}
.recentbar__items {
  display: flex;
  gap: 6px;
  overflow-x: auto;
  scrollbar-width: none;
  flex: 1;
}
.recentbar__items::-webkit-scrollbar { display: none; }
/* Sits at the end of a strip that scrolls under it. Without a ground of its
   own a half-scrolled tab runs right up under the word and the two read as
   one broken control — which is exactly how it looked on a phone, where the
   tabs are always wider than the bar. The matching background plus a short
   shadow to its left cuts the strip off cleanly instead. */
.recentbar__clear {
  font-size: 11px;
  font-weight: 600;
  color: var(--color-muted);
  flex-shrink: 0;
  padding-left: var(--s-12);
  background: var(--color-cream);
  box-shadow: -12px 0 12px -6px var(--color-cream);
}

.rtab {
  display: inline-flex;
  align-items: center;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--r-full);
  font-size: 12px;
  white-space: nowrap;
  max-width: 230px;
}
.rtab.is-current { border-color: var(--accent); background: var(--color-1-transparent-1); }
.rtab > a:first-child {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 3px 4px 3px 10px;
  color: var(--color-body-text);
  overflow: hidden;
  text-overflow: ellipsis;
}
.rtab > a:first-child:hover { text-decoration: none; color: var(--accent-dark); }
.rtab svg { color: var(--color-muted); flex-shrink: 0; }
.rtab.is-current svg { color: var(--accent-dark); }
.rtab__x {
  padding: 3px 9px 3px 5px;
  color: var(--color-muted);
  font-size: 14px;
  line-height: 1;
}
.rtab__x:hover { color: var(--color-status-danger); text-decoration: none; }

/* =====================================================
   SCOPE BAR — search + selected business
   ===================================================== */

.scopebar {
  display: flex;
  align-items: center;
  gap: var(--s-12);
  flex-wrap: wrap;
  padding: var(--s-12) var(--s-16);
  background: var(--color-white);
  border-bottom: 1px solid var(--color-border);
}

.search {
  position: relative;
  flex: 1 1 340px;
  min-width: 220px;
}
.search input {
  width: 100%;
  padding: 9px var(--s-12);
  font: inherit;
  font-size: 13px;
  color: var(--color-body-text);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
}
.search input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

.btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 9px var(--s-16);
  font: inherit;
  font-size: 13px;
  font-weight: 600;
  border-radius: var(--r-md);
  border: 1px solid var(--color-border);
  background: var(--color-white);
  color: var(--color-body-text);
  cursor: pointer;
  white-space: nowrap;
}
.btn:hover { background: var(--color-cream); text-decoration: none; }
.btn--primary {
  background: var(--dark);
  border-color: var(--dark);
  color: var(--color-white);
}
.btn--primary:hover { background: var(--dark-deep); }
.btn--accent {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--dark-deep);
}
.btn--accent:hover { background: var(--accent-dark); }
.btn--sm { padding: 5px 10px; font-size: 12px; }
/* Removing a record is the one action that should look different from the
   rest before it is clicked, not only in the dialog that follows. */
.btn--danger {
  background: var(--color-status-danger-bg);
  border-color: var(--color-status-danger);
  color: var(--color-status-danger-dark);
}
.btn--danger:hover {
  background: var(--color-status-danger);
  color: var(--color-white);
}

/* A checkbox and its label as one clickable row, for the estimate flags. */
.est-check {
  display: flex;
  align-items: center;
  gap: var(--s-8);
  padding: 7px 0;
  font-size: 13px;
  color: var(--color-body-text);
  cursor: pointer;
}
.est-check input { width: 16px; height: 16px; flex-shrink: 0; accent-color: var(--accent); }

/* =====================================================
   CONTEXT BAR — what is in scope + secondary menu
   ===================================================== */

.contextbar {
  background: var(--dark);
  color: var(--color-white);
}
.contextbar__scope {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-12);
  padding: 7px var(--s-16);
  font-weight: 700;
  font-size: 13px;
  border-bottom: 1px solid rgba(255, 255, 255, .08);
}
.contextbar__menu {
  display: flex;
  align-items: center;
  gap: var(--s-4);
  padding: 6px var(--s-16);
  background: var(--dark-deep);
  overflow-x: auto;
  scrollbar-width: none;
}
.contextbar__menu::-webkit-scrollbar { display: none; }

/* The secondary menu's own hamburger. Desktop lays the menu out along the bar
   and has no use for it; under 1024px it is how the menu is reached — see
   NARROW SCREENS. */
.contextbar__toggle {
  display: none;
  align-items: center;
  gap: var(--s-8);
  width: 100%;
  padding: 8px var(--s-16);
  border: 0;
  border-top: 1px solid rgba(255, 255, 255, .08);
  background: var(--dark-deep);
  color: var(--color-white);
  font: inherit;
  font-size: 12.5px;
  font-weight: 600;
  text-align: left;
  cursor: pointer;
}
.contextbar__toggle:hover { background: rgba(255, 255, 255, .06); }
.contextbar__toggle span {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.contextbar__toggle svg { flex-shrink: 0; }

.contextbar__link {
  color: rgba(255, 255, 255, .80);
  padding: 5px 10px;
  border-radius: var(--r-sm);
  font-size: 12.5px;
  font-weight: 500;
  white-space: nowrap;
}
.contextbar__link:hover {
  background: rgba(255, 255, 255, .10);
  color: var(--color-white);
  text-decoration: none;
}
.contextbar__link.is-active {
  background: var(--accent);
  color: var(--dark-deep);
  font-weight: 600;
}

/* =====================================================
   WORKSPACE
   ===================================================== */

.workspace {
  padding: var(--s-24) var(--s-16) var(--s-48);
  max-width: 1400px;
  margin: 0 auto;
}

.page-head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--s-16);
  flex-wrap: wrap;
  margin-bottom: var(--s-16);
}
.page-head h1 {
  margin: 0;
  font-size: var(--fs-h2);
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--color-heading);
}
.page-head .sub {
  margin: 2px 0 0;
  font-size: 13px;
  color: var(--color-muted);
}

.card {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
  overflow: hidden;
}
.card + .card { margin-top: var(--s-16); }
.card__body { padding: var(--s-16); }

/* --- stat tiles --- */
.tiles {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(158px, 1fr));
  gap: var(--s-12);
  margin-bottom: var(--s-16);
}
.tile {
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  padding: var(--s-12) var(--s-16);
}
.tile__label {
  font-size: 10.5px;
  font-weight: 700;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--color-muted);
}
.tile__value {
  margin-top: 4px;
  font-size: 24px;
  font-weight: 700;
  letter-spacing: -0.02em;
  color: var(--color-heading);
}
.tile__value--accent { color: var(--accent-dark); }
.tile__unit {
  font-size: 11.5px;
  color: var(--color-muted);
  margin-top: 2px;
}
.tile--link { display: block; color: inherit; transition: border-color var(--t-fast), box-shadow var(--t-fast); }
.tile--link:hover {
  text-decoration: none;
  border-color: var(--accent);
  box-shadow: var(--shadow-sm);
}
.tile--link:hover .tile__value { color: var(--accent-dark); }

/* --- filter chips --- */
.chips {
  display: flex;
  flex-wrap: wrap;
  gap: var(--s-8);
  margin-bottom: var(--s-16);
}
.chip {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 13px;
  border-radius: var(--r-full);
  border: 1px solid var(--color-border);
  background: var(--color-white);
  color: var(--color-body-text);
  font-size: 12.5px;
  font-weight: 600;
}
.chip:hover { background: var(--color-cream); text-decoration: none; }
.chip.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--dark-deep);
}
.chip__count {
  display: inline-block;
  min-width: 18px;
  padding: 1px 6px;
  border-radius: var(--r-full);
  background: var(--color-status-neutral-bg);
  color: var(--color-muted);
  font-size: 11px;
  font-weight: 700;
  text-align: center;
}
.chip.is-active .chip__count {
  background: rgba(0, 0, 0, .16);
  color: var(--dark-deep);
}

/* =====================================================
   TABLE
   ===================================================== */

.table-wrap { overflow-x: auto; }

table.grid {
  width: 100%;
  border-collapse: collapse;
  font-size: 13px;
}
table.grid thead th {
  padding: 10px 14px;
  text-align: left;
  font-size: 11px;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: .05em;
  color: var(--color-muted);
  background: var(--color-off-white);
  border-bottom: 1px solid var(--color-border);
  white-space: nowrap;
}
table.grid tbody td {
  padding: 11px 14px;
  border-bottom: 1px solid var(--color-border);
  vertical-align: middle;
}
table.grid tbody tr:last-child td { border-bottom: 0; }
table.grid tbody tr:hover { background: var(--color-off-white); }

.cell-name { display: flex; align-items: center; gap: 10px; }

/* Contact photo. `cover` here, unlike the logos: a face fills a circle
   sensibly, where a wordmark would be sliced apart by the same crop. */
.cell-avatar {
  width: 34px;
  height: 34px;
  flex-shrink: 0;
  border-radius: 50%;
  object-fit: cover;
  border: 1.5px solid var(--color-border);
  background: var(--color-white);
}

/* Row logo. `contain`, not `cover`: these are wordmarks, and cropping one to a
   56px square usually leaves an unreadable slice of a letter. Letterboxing
   wastes a little space and keeps the logo recognisable, which is the whole
   reason for showing it. */
.cell-logo {
  width: 56px;
  height: 56px;
  flex-shrink: 0;
  border-radius: 8px;
  border: 2px solid var(--color-border);
  background: var(--color-white);
  object-fit: contain;
}
.cell-name__text { min-width: 0; }
.cell-name__title { font-weight: 600; }
.cell-name__sub {
  font-size: 11px;
  color: var(--color-muted);
  margin-top: 1px;
}
.muted { color: var(--color-muted); }
.nowrap { white-space: nowrap; }

.empty {
  padding: var(--s-48) var(--s-16);
  text-align: center;
  color: var(--color-muted);
}

/* =====================================================
   HATS — the profile badges
   A business wears one badge per profile type. Colour and
   glyph both come from inc/hats.php so the list, the filter
   chips and the detail page never drift apart.
   ===================================================== */

.hats { display: inline-flex; align-items: center; gap: 4px; flex-wrap: wrap; }

.hat {
  display: inline-flex;
  align-items: center;
  gap: 5px;
  padding: 2px 9px 2px 6px;
  border-radius: var(--r-full);
  font-size: 11px;
  font-weight: 700;
  line-height: 1.7;
  white-space: nowrap;
  border: 1px solid transparent;
}
.hat svg { width: 12px; height: 12px; flex-shrink: 0; }

/* Icon-only badge for list columns: the glyph in its hat colour, name on
   hover. The label still exists for screen readers and browser find — it is
   just not taking up a column's worth of width. */
.hat-mark {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 26px;
  height: 26px;
  border-radius: var(--r-md);
  border: 1px solid transparent;
  cursor: default;
}
.hat-mark svg { width: 13px; height: 13px; }

/* Icon-only variant, for the avatar slot in a list row */
.hat-stack {
  display: inline-flex;
  align-items: center;
  flex-shrink: 0;
}
.hat-stack .hat-icon + .hat-icon { margin-left: -8px; }

.hat-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 30px;
  height: 30px;
  border-radius: var(--r-md);
  border: 1.5px solid var(--color-white);
  box-shadow: var(--shadow-sm);
  flex-shrink: 0;
}
.hat-icon svg { width: 15px; height: 15px; }

.hat--lead          { background: var(--hat-lead-bg);          color: var(--hat-lead);          border-color: var(--hat-lead);          }
.hat--subcontractor { background: var(--hat-subcontractor-bg); color: var(--hat-subcontractor); border-color: var(--hat-subcontractor); }
.hat--contact       { background: var(--hat-contact-bg);       color: var(--hat-contact);       border-color: var(--hat-contact);       }
.hat--architect     { background: var(--hat-architect-bg);     color: var(--hat-architect);     border-color: var(--hat-architect);     }
.hat--estimator     { background: var(--hat-estimator-bg);     color: var(--hat-estimator);     border-color: var(--hat-estimator);     }
.hat--sales         { background: var(--hat-sales-bg);         color: var(--hat-sales);         border-color: var(--hat-sales);         }
.hat--none          { background: var(--hat-none-bg);          color: var(--hat-none);          border-color: var(--hat-none);          }

/* The icon-only variant keeps the tint but drops the white ring colour */
.hat-icon.hat--lead          { border-color: var(--color-white); }
.hat-icon.hat--subcontractor { border-color: var(--color-white); }
.hat-icon.hat--contact       { border-color: var(--color-white); }
.hat-icon.hat--architect     { border-color: var(--color-white); }
.hat-icon.hat--estimator     { border-color: var(--color-white); }
.hat-icon.hat--sales         { border-color: var(--color-white); }
.hat-icon.hat--none          { border-color: var(--color-white); }

/* =====================================================
   PAGINATION
   ===================================================== */

.pager {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-12);
  flex-wrap: wrap;
  padding: var(--s-12) var(--s-16);
  border-top: 1px solid var(--color-border);
  background: var(--color-off-white);
  font-size: 12.5px;
  color: var(--color-muted);
}
.pager__links { display: flex; gap: var(--s-4); }
.pager__links a, .pager__links span {
  padding: 4px 10px;
  border-radius: var(--r-sm);
  border: 1px solid var(--color-border);
  background: var(--color-white);
}
.pager__links span { color: var(--color-muted); opacity: .55; }
.pager__links a.is-active {
  background: var(--accent);
  border-color: var(--accent);
  color: var(--dark-deep);
  font-weight: 700;
}

/* =====================================================
   FILTER PANEL — the estimates screens
   Collapsed to a single pill by default so it never buries the table it
   filters; opened by the page when a filter is actually in play.
   ===================================================== */

.filters { margin-bottom: var(--s-16); }
.filters > summary {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 13px;
  border: 1px solid var(--color-border);
  border-radius: var(--r-full);
  background: var(--color-white);
  color: var(--color-body-text);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.filters > summary::-webkit-details-marker { display: none; }
.filters > summary::marker { content: ""; }
.filters > summary:hover { background: var(--color-cream); }
.filters[open] > summary {
  margin-bottom: var(--s-12);
  border-color: var(--accent);
}

.filters__panel {
  padding: var(--s-16);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}
.filters__grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--s-16);
}
.filters__grid .field { margin-bottom: 0; }
.filters__grid select[multiple] { padding: 4px; }

/* min / max and date-from / date-to sit on one line and share it evenly.
   min-width:0 is what stops a date input's fixed intrinsic width from pushing
   the pair wider than its grid column. */
.filters__range { display: flex; align-items: center; gap: 6px; }
.filters__range input { min-width: 0; flex: 1 1 0; }
.filters__range span { flex-shrink: 0; font-size: 12px; color: var(--color-muted); }

.filters__foot {
  display: flex;
  align-items: center;
  gap: var(--s-8);
  margin-top: var(--s-16);
  padding-top: var(--s-12);
  border-top: 1px solid var(--color-border);
}
.filters__note { font-size: 12px; color: var(--color-muted); margin-left: auto; }

/* =====================================================
   COMBO BOX — type-ahead over a long <select>
   The input is styled to match .field input exactly, because as far as the
   person using it is concerned that is what it is.
   ===================================================== */

.combo { position: relative; }
.combo__input {
  width: 100%;
  padding: 8px var(--s-12);
  font: inherit;
  font-size: 13px;
  color: var(--color-body-text);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background: var(--color-white);
}
.combo__input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

.combo__list {
  position: absolute;
  z-index: 40;
  left: 0;
  right: 0;
  top: calc(100% + 4px);
  max-height: 260px;
  overflow-y: auto;
  margin: 0;
  padding: 4px;
  list-style: none;
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
}
.combo__opt {
  padding: 7px var(--s-12);
  border-radius: var(--r-sm);
  font-size: 13px;
  color: var(--color-body-text);
  cursor: pointer;
}
.combo__opt:hover,
.combo__opt.is-active { background: var(--color-cream); }
.combo__opt.is-active { box-shadow: inset 2px 0 0 var(--accent); }
.combo__empty {
  padding: 7px var(--s-12);
  font-size: 12px;
  color: var(--color-muted);
}

/* --- add a note, from a Notes list ---
   Collapsed to a pill so the list stays the point of the page, and opened by
   the page when a save came back with an error. */
.addnote { margin-bottom: var(--s-16); }
.addnote > summary {
  display: inline-flex;
  align-items: center;
  gap: 7px;
  padding: 6px 13px;
  border: 1px solid var(--color-border);
  border-radius: var(--r-full);
  background: var(--color-white);
  color: var(--color-body-text);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  list-style: none;
}
.addnote > summary::-webkit-details-marker { display: none; }
.addnote > summary::marker { content: ""; }
.addnote > summary:hover { background: var(--color-cream); }
.addnote[open] > summary {
  margin-bottom: var(--s-12);
  border-color: var(--accent);
}
.addnote__panel {
  padding: var(--s-16);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-sm);
}
.addnote__panel .field { margin-bottom: 0; }
.addnote__foot { margin-top: var(--s-12); }

/* Jump straight to a page number. */
.pager__jump {
  display: flex;
  align-items: center;
  gap: 6px;
}
.pager__jump input {
  width: 70px;
  padding: 4px 8px;
  font: inherit;
  font-size: 12.5px;
  text-align: center;
  color: var(--color-body-text);
  background: var(--color-white);
  border: 1px solid var(--color-border);
  border-radius: var(--r-sm);
}
.pager__jump input:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}

/* =====================================================
   FEEDBACK
   ===================================================== */

.feedback-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 5px 12px;
  border-radius: var(--r-full);
  border: 1px solid rgba(255, 255, 255, .30);
  background: rgba(255, 255, 255, .08);
  color: var(--color-white);
  font-size: 12.5px;
  font-weight: 600;
  cursor: pointer;
  white-space: nowrap;
}
.feedback-btn:hover { background: rgba(255, 255, 255, .18); text-decoration: none; }

.modal {
  position: fixed;
  inset: 0;
  display: none;
  align-items: center;
  justify-content: center;
  padding: var(--s-16);
  background: rgba(0, 0, 0, .5);
  z-index: 600;
}
.modal[data-open="true"] { display: flex; }
.modal__panel {
  background: var(--color-white);
  border-radius: var(--r-lg);
  box-shadow: var(--shadow-lg);
  width: 100%;
  max-width: 520px;
  max-height: 90vh;
  overflow-y: auto;
}
.modal__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--s-16);
  border-bottom: 1px solid var(--color-border);
}
.modal__head h2 { margin: 0; font-size: var(--fs-h4); color: var(--color-heading); }
.modal__body { padding: var(--s-16); }
.modal__foot {
  display: flex;
  justify-content: flex-end;
  gap: var(--s-8);
  padding: var(--s-16);
  border-top: 1px solid var(--color-border);
  background: var(--color-off-white);
}

.field { margin-bottom: var(--s-12); }
.field label {
  display: block;
  font-size: 12px;
  font-weight: 600;
  color: var(--color-heading);
  margin-bottom: 4px;
}
.field input, .field select, .field textarea {
  width: 100%;
  padding: 8px var(--s-12);
  font: inherit;
  font-size: 13px;
  color: var(--color-body-text);
  border: 1px solid var(--color-border);
  border-radius: var(--r-md);
  background: var(--color-white);
}
.field textarea { min-height: 110px; resize: vertical; }
.field input:focus, .field select:focus, .field textarea:focus {
  outline: none;
  border-color: var(--accent);
  box-shadow: 0 0 0 3px var(--accent-glow);
}
.field .hint { font-size: 11.5px; color: var(--color-muted); margin-top: 4px; }

.badge {
  display: inline-block;
  padding: 2px 10px;
  border-radius: var(--r-full);
  font-size: 11px;
  font-weight: 700;
}
.badge--open    { background: var(--color-status-info-bg);    color: var(--color-status-info-dark); }
.badge--fixed   { background: var(--color-status-success-bg); color: var(--color-status-success-dark); }
.badge--closed  { background: var(--color-status-neutral-bg); color: var(--color-status-neutral); }

.flash {
  padding: var(--s-12) var(--s-16);
  border-radius: var(--r-md);
  margin-bottom: var(--s-16);
  font-size: 13px;
  font-weight: 500;
}
.flash--ok {
  background: var(--color-status-success-bg);
  color: var(--color-status-success-dark);
  border: 1px solid var(--color-status-success);
}
.flash--err {
  background: var(--color-status-danger-bg);
  color: var(--color-status-danger-dark);
  border: 1px solid var(--color-status-danger);
}

/* =====================================================
   BUSINESS VIEW — the 36 Rule dashboard
   Layout, colours and proportions reproduced from the live
   site's .bv-* classes (36rule.com, assets/css/styles.css).
   The original carried most of this as inline style
   attributes; here it is classes, as everywhere else.
   ===================================================== */

.eyebrow {
  font-size: var(--fs-eyebrow);
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  color: var(--accent);
}

.badge--info {
  background: var(--color-status-success-bg);
  color: var(--color-status-success-dark);
}

/* --- the two-column dashboard: main column + progress chart --- */
.bv-dashboard-row {
  display: grid;
  grid-template-columns: minmax(0, 3fr) minmax(240px, 1fr);
  align-items: stretch;
  gap: var(--s-16);
  width: 100%;
  margin-bottom: var(--s-16);
}
.bv-dashboard-row > * { min-width: 0; }

.bv-dashboard-main {
  display: flex;
  flex-direction: column;
  gap: var(--s-16);
  min-width: 0;
}
.bv-dashboard-main .bv-quadrants { margin-bottom: 0; }

/* --- business header card --- */
.bv-header-card {
  display: flex;
  align-items: flex-start;
  gap: 20px;
  padding: var(--s-16);
}
.bv-header-logo {
  width: 250px;
  height: 150px;
  flex-shrink: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--color-border);
  border-radius: var(--r-sm);
  background: var(--color-off-white);
  font-size: 11px;
  color: var(--color-muted);
  text-align: center;
  overflow: hidden;
}
.bv-header-logo img {
  width: 250px;
  height: 150px;
  border-radius: 8px;
  /* The live page uses `cover` here and it visibly mangles wide wordmarks —
     ALT Architecture renders as "alt chitectu". `contain` shows the logo. */
  object-fit: contain;
  border: 2px solid var(--color-border);
}
.bv-header-details { flex: 1; min-width: 0; }
.bv-header-name {
  font-size: var(--fs-h3);
  font-weight: 700;
  color: var(--dark);
}
.bv-header-meta {
  margin-top: var(--s-8);
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-4) var(--s-24);
  font-size: var(--fs-small);
  color: var(--color-muted);
}
.bv-header-meta a { color: var(--accent); }
.bv-header-pills {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 6px;
}

/* --- the four 9s ---
   Each quadrant carries its own palette as custom properties, so the
   table rules below are written once. Tints are the same Tailwind
   families the live site used. */
.bv-quadrants {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-8);
  margin-bottom: var(--s-16);
}
/* EACH QUADRANT HOLDS NINE, SO IT IS ALWAYS NINE TALL.
   An empty phase used to collapse to its header strip, which made the four
   read as four coloured bars rather than four containers, and the block
   jumped taller every time an activity was logged.

   The reserved space is not padding: the faint rules beneath the real rows
   are the nine slots, so a phase shows at a glance how much of its nine is
   done. Real rows carry an opaque --q-row-bg, so they paint over the slots
   they occupy and only the unfilled ones show through.

   --q-row-h DRIVES the row height (see .bv-quad tbody td) rather than being
   measured from it, so the rules and the rows cannot disagree. --q-head-h is
   measured from the rendered header; re-check it if its padding or font
   size changes. */
.bv-quad {
  --q-row-h: 26px;
  --q-head-h: 42px;
  border-radius: var(--r-sm);
  overflow: hidden;
  border: 1px solid var(--color-border);
  min-height: calc(var(--q-head-h) + 9 * var(--q-row-h));
  background-color: var(--q-row-bg);
  background-image: repeating-linear-gradient(
    to bottom,
    transparent 0,
    transparent calc(var(--q-row-h) - 1px),
    var(--q-line) calc(var(--q-row-h) - 1px),
    var(--q-line) var(--q-row-h));
  background-repeat: no-repeat;
  background-position: 0 var(--q-head-h);
  background-size: 100% calc(9 * var(--q-row-h));
}
.bv-quad--1 { --q-head-bg: #ffedd5; --q-head-fg: #c2410c; --q-line: #fed7aa;
              --q-row-bg: #fff7ed; --q-name: #c2410c; --q-cell: #ea580c; }
.bv-quad--2 { --q-head-bg: #ede9fe; --q-head-fg: #5b21b6; --q-line: #ddd6fe;
              --q-row-bg: #f5f3ff; --q-name: #5b21b6; --q-cell: #7c3aed; }
.bv-quad--3 { --q-head-bg: #dcfce7; --q-head-fg: #15803d; --q-line: #bbf7d0;
              --q-row-bg: #f0fdf4; --q-name: #15803d; --q-cell: #16a34a; }
.bv-quad--4 { --q-head-bg: #e0f2fe; --q-head-fg: #0369a1; --q-line: #bae6fd;
              --q-row-bg: #f0f9ff; --q-name: #0369a1; --q-cell: #0284c7; }

.bv-quad__head {
  background: var(--q-head-bg);
  color: var(--q-head-fg);
  font-weight: 700;
  font-size: var(--fs-small);
  padding: 10px 14px;
  border-bottom: 2px solid var(--q-line);
}
.bv-quad table {
  width: 100%;
  border-collapse: collapse;
  font-size: 12px;
}
.bv-quad tbody tr { background: var(--q-row-bg); }
/* Height comes from --q-row-h, not from padding, because the slot rules behind
   these rows are drawn on that same variable — set the two independently and
   the guides drift out of step with the rows by a pixel per row. line-height
   fills the row, which centres the text without padding to tune. */
.bv-quad tbody td {
  height: var(--q-row-h);
  padding: 0 12px;
  line-height: calc(var(--q-row-h) - 1px);
  border-bottom: 1px solid var(--q-line);
  color: var(--q-cell);
}
.bv-quad tbody tr:last-child td { border-bottom: 0; }
.bv-quad__who { font-weight: 600; color: var(--q-name); }
.bv-quad__when { white-space: nowrap; }

/* --- 36 Rule Progress donut ---
   The ring is a conic-gradient: each quadrant owns 90deg, and the filled
   part of it is (done / 9) * 90deg. The gradient itself is built per
   business in the page, because it is data. */
.bv-pie-card {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  min-width: 0;
  padding: var(--s-16);
  text-align: center;
}
.bv-pie-title {
  display: block;
  width: 100%;
  margin-bottom: var(--s-12);
  text-align: center;
}
.bv-pie-title .eyebrow {
  margin: 0;
  color: var(--color-2);
  font-size: 1.25rem;
}
/* One knob for the whole donut: the ring, and the hole scaled with it so the
   band keeps its proportion at any size. The quadrants beside it are now nine
   rows tall, so at desktop the ring grows to balance that column instead of
   floating in the middle of a tall empty card. */
.bv-pie-chart {
  --ring: 202px;
  position: relative;
  width: var(--ring);
  height: var(--ring);
  margin: 0 auto var(--s-16);
  flex: 0 0 auto;
}
.bv-pie-ring { width: var(--ring); height: var(--ring); border-radius: 50%; }
.bv-pie-hole {
  position: absolute;
  inset: calc(var(--ring) * 0.16);
  border-radius: 50%;
  background: var(--color-white);
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 1px;
}
.bv-pie-done { font-size: 1.4rem; font-weight: 800; color: var(--dark); line-height: 1; }
.bv-pie-total { font-size: 1.25rem; color: var(--color-muted); line-height: 1; }

/* Four phase rows rather than four dots on a line. Each row is its own small
   progress bar in the phase's colour, so the card answers "how far into each
   nine am I" without the eye having to decode the donut's four arcs. Colours
   come in per item as --phase / --track, because they are data. */
.bv-pie-legend {
  display: flex;
  flex-direction: column;
  gap: var(--s-16);
  width: 100%;
  margin-top: var(--s-24);
  text-align: left;
  flex: 0 0 auto;
}
.bv-legend-item { min-width: 0; }
.bv-legend-head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--s-8);
  margin-bottom: 6px;
}
.bv-legend-name {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--phase);
  letter-spacing: 0.01em;
}
.bv-legend-count {
  font-size: 12.5px;
  font-weight: 700;
  color: var(--color-heading);
  /* Tabular figures so the counts sit in a column as they change. */
  font-variant-numeric: tabular-nums;
  flex-shrink: 0;
}
.bv-legend-count span { font-weight: 600; color: var(--color-muted); }
.bv-legend-bar {
  height: 7px;
  border-radius: var(--r-full);
  background: var(--track);
  overflow: hidden;
}
.bv-legend-bar i {
  display: block;
  height: 100%;
  border-radius: var(--r-full);
  background: var(--phase);
}
.bv-legend-sub {
  margin-top: 5px;
  font-size: 11px;
  line-height: 1.35;
  color: var(--color-muted);
}

/* --- appointments ---
   .card carries no padding of its own (the table wants to run full bleed),
   so the header row supplies its own. */
.bv-appt-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-12);
  padding: var(--s-16) var(--s-16) var(--s-12);
}
.bv-appt-head .eyebrow { margin: 0; }

/* --- the quick-add pair: two cards side by side --- */
.bv-quick-add {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-16);
  margin-bottom: var(--s-16);
}
.bv-quick-add > .card { padding: var(--s-16); }
.bv-quick-add .eyebrow { margin: 0 0 var(--s-12); }

/* ===== EVERY GRID OF CARDS MUST BE LISTED HERE =====
   `.card + .card { margin-top: 16px }` (see CARDS, above) is right for cards
   stacked in normal flow. It is always wrong inside a grid or flex container:
   the second item in a row is still an adjacent SIBLING, so it collects the
   margin and sits 16px below the first, and in a single column it doubles the
   container's own gap.

   There is no "my parent is a grid" selector in CSS, so each such container
   has to switch it off by hand. ADD ANY NEW CARD GRID TO THIS LIST — it has
   already been missed twice: once for the quick-add pairs, and again for
   .cv-lists, where Appointments hung 16px below Notes.

   To check after a change, run over the rendered page and flag any .card whose
   parent computes to display:grid/flex and whose margin-top is not 0. */
.bv-quick-add > .card + .card,
.bv-dashboard-row > .card + .card,
.cv-lists > .card + .card { margin-top: 0; }

/* Paired cards are the same height (grid stretch), but their forms are not —
   Schedule Appointment has two fields where Log Activity has four. Left alone
   the shorter card's buttons float in its middle with dead space beneath.
   Pushing the action row down with margin:auto lands both pairs' buttons on
   the same line. */
.bv-quick-add > .card { display: flex; flex-direction: column; }
.bv-quick-add > .card > form { display: flex; flex-direction: column; flex: 1 1 auto; }
.bv-quick-add > .card > form > .bv-form-actions { margin-top: auto; padding-top: var(--s-12); }

/* The accent rule along the top is what marks these as the "do something"
   cards, as opposed to the read-only ones above. */
.card--accent-top { border-top: 3px solid var(--accent); }

.bv-form-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0 var(--s-12);
}
.bv-form-actions {
  display: flex;
  justify-content: flex-end;
  gap: var(--s-8);
  margin-top: var(--s-4);
}
.req { color: var(--color-status-danger); }

/* --- contact view: the header box holds a face, not a logo ---
   Square rather than the logo's 250x150 landscape, and it crops instead of
   letterboxing, because a portrait in a wide box is mostly empty box. */
.bv-header-logo--avatar {
  width: 150px;
  height: 150px;
  overflow: hidden;
}
.bv-header-logo--avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.bv-avatar-initials {
  font-family: var(--font-2);
  font-size: 44px;
  font-weight: 700;
  line-height: 1;
  color: var(--accent-dark);
}

/* Whose 36 this is, and the account's total underneath it, so a person's
   count is never read as the whole relationship's. */
.bv-pie-sub {
  margin: 2px 0 0;
  font-size: 12px;
  font-weight: 600;
  color: var(--color-heading);
}
.bv-pie-foot {
  margin: var(--s-12) 0 0;
  font-size: 11.5px;
  color: var(--color-muted);
  text-align: center;
}

/* =====================================================
   CONTACT VIEW — the four record lists
   Two to a row. Each is a card with its own head (title + add + "All") and a
   capped body, so one screen shows all four rather than four full-width bands
   you have to scroll past.
   ===================================================== */

/* On the contact view the header card stands on its own rather than inside
   .bv-dashboard-main, whose flex gap used to space it. */
.bv-header-card--solo { margin-bottom: var(--s-16); }

.cv-lists {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--s-16);
  margin-bottom: var(--s-16);
}
.cv-list {
  display: flex;
  flex-direction: column;
  min-width: 0;
}
.cv-list__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s-8);
  padding: var(--s-12) var(--s-16);
  border-bottom: 1px solid var(--color-border);
}
.cv-list__head .eyebrow { margin: 0; }
.cv-list__actions {
  display: flex;
  align-items: center;
  gap: 6px;
  flex-shrink: 0;
}

/* Capped rather than unbounded: a contact with forty notes would otherwise set
   the height of the whole row and leave its neighbour padded out to match.
   Everything past the cap is one click away on the "All" list. */
.cv-list__body {
  padding: var(--s-16);
  max-height: 300px;
  overflow-y: auto;
  flex: 1 1 auto;
}
/* Tables run edge to edge so their header band and row rules reach the card
   sides; the cells supply the inset instead (see below). */
.cv-list__body--flush { padding: 0; }

/* ONE LEFT EDGE FOR ALL FOUR CARDS.
   These four sit in a column on tablet, where any difference in where their
   text starts reads as the cards being misaligned — and there were three
   different edges: the note body indented 16px and the empty paragraph added
   16px of its own on top of it (33 -> 49), while table cells carry 14px, two
   short of the card's 16 (31). So the empty state brings no padding of its
   own, and the outer table cells are pushed out to the card's 16. Everything
   now starts on the same line as the card title above it. */
.cv-list__empty {
  margin: 0;
  padding: 0;
  font-size: 13px;
  color: var(--color-muted);
}
.cv-list__body--flush .cv-list__empty { padding: var(--s-16); }
.cv-list__body--flush table.grid th:first-child,
.cv-list__body--flush table.grid td:first-child { padding-left: var(--s-16); }
.cv-list__body--flush table.grid th:last-child,
.cv-list__body--flush table.grid td:last-child { padding-right: var(--s-16); }

.cv-modal__who {
  margin: 0 0 var(--s-12);
  font-size: 12.5px;
  font-weight: 600;
  color: var(--color-muted);
}

/* One column on tablet and below: two lists side by side stop being readable
   once each is under ~300px. */
@media (max-width: 900px) {
  .cv-lists { grid-template-columns: minmax(0, 1fr); }
}

/* --- notes --- */
.bv-note { font-size: 13px; }
.bv-note + .bv-note {
  margin-top: var(--s-12);
  padding-top: var(--s-12);
  border-top: 1px solid var(--color-border);
}
.bv-note__date {
  font-weight: 600;
  color: var(--color-heading);
  margin-bottom: var(--s-4);
}
.bv-note__body {
  color: var(--color-body-text);
  overflow-wrap: anywhere;
}

/* --- desktop: the donut scales up to the height of the nine-row column ---
   min(…, 100%) rather than a flat 288px: this column is `minmax(240px, 1fr)`,
   so just above the tablet breakpoint it bottoms out near 240px wide and a
   fixed ring would push straight out of its own card. */
@media (min-width: 901px) {
  .bv-pie-chart { --ring: min(288px, 100%); }
}

/* --- tablet: the chart drops below the main column ---
   Stacked, the chart card is as wide as the page, and a 200px donut centred
   in it left two-thirds of the card empty — the "doesn't look good" of a
   tall, mostly blank box. Laid out as a row instead, the title spans the top
   and the donut sits beside its legend, so the card is as wide as it needs to
   be and no taller. */
@media (max-width: 900px) {
  .bv-dashboard-row { grid-template-columns: minmax(0, 1fr); }
  .bv-pie-card {
    min-height: 0;
    flex-direction: row;
    flex-wrap: wrap;
    align-items: center;
    justify-content: center;
    column-gap: var(--s-32);
  }
  .bv-pie-title { flex: 1 1 100%; margin-bottom: var(--s-16); }
  .bv-pie-chart { margin: 0; }
  /* The base rule is width:100%, which is what a centred column wants and
     what stops this sitting beside the donut — at 834px wide it could never
     share a line with it, so it wrapped underneath and the row layout looked
     no different from the column one. Shrink it to its contents instead. */
  /* Beside the donut rather than under it, and narrow enough that the two
     together stay centred in the card. */
  .bv-pie-legend {
    width: auto;
    flex: 0 1 320px;
    margin: 0;
    gap: var(--s-12);
  }
  .bv-pie-foot { flex: 1 1 100%; margin-top: var(--s-16); }
}

/* --- phone --- */
@media (max-width: 640px) {
  .bv-dashboard-row { gap: var(--s-12); }
  .bv-header-card { flex-direction: column; align-items: center; gap: var(--s-12); }
  .bv-header-logo { width: 160px; height: 96px; }
  .bv-header-logo img { width: 160px; height: 96px; }
  .bv-header-details { width: 100%; text-align: center; }
  .bv-header-meta { grid-template-columns: minmax(0, 1fr); justify-items: center; }

  /* One column now, so nine reserved slots each would be four screens of
     mostly-empty phase. Reserve four and let a busy phase grow past it —
     the slot rules still show how much of the nine is done. */
  .bv-quadrants { grid-template-columns: minmax(0, 1fr); }
  .bv-quad { min-height: calc(var(--q-head-h) + 4 * var(--q-row-h)); }

  /* Back to a column: side by side needs width this does not have.
     flex-wrap MUST be reset with the direction. The tablet rule above turns
     this card into a wrapping ROW; leaving the wrap on while flipping to a
     column makes it wrap into extra COLUMNS instead — with .bv-pie-title
     still carrying flex-basis:100%, which is now 100% of the HEIGHT, the
     title filled the first column and pushed the donut into a second one
     450px off the side of the phone. The flex bases below are the other half
     of that reset: on a column they size height, not width. */
  .bv-pie-card {
    min-height: 0;
    flex-direction: column;
    flex-wrap: nowrap;
    justify-content: flex-start;
    column-gap: 0;
  }
  .bv-pie-title { flex: 0 0 auto; margin-bottom: var(--s-12); }
  .bv-pie-chart { --ring: 202px; flex: 0 0 auto; margin: 0 auto var(--s-16); }
  .bv-pie-foot { flex: 0 0 auto; }
  .bv-pie-legend { flex: 0 0 auto; width: 100%; max-width: 320px; margin: var(--s-16) auto 0; }
  .bv-quick-add { grid-template-columns: minmax(0, 1fr); }
  .bv-form-grid { grid-template-columns: minmax(0, 1fr); }
}

/* =====================================================
   NARROW SCREENS
   ===================================================== */

/* NOTHING IN THE SHELL MAY BE WIDER THAN THE SCREEN.
   .topnav is `position: sticky`, which pins vertically only. So anything that
   makes the document wider than the viewport lets the page scroll sideways,
   and the hamburger — the only way to the menu once .topnav__menu is hidden —
   scrolls off the left edge and cannot be got back. That is what happened
   here: the signed-in name plus "Sign out" made the bar 504px on a 412px
   phone. Wide content belongs in its own .table-wrap, never in the shell. */

/* --- each menu collapses at the width it actually stops fitting ---------
   The two menus are nowhere near the same size, so one shared breakpoint was
   wrong in both directions: it clipped the top menu for 400px and hid the
   secondary menu for 300px while it still fitted fine.

   MEASURED, not guessed:
     top menu       eight items = 885px of links, and the rest of the bar
                    (brand, hamburger, Feedback, name, Sign out, gaps) takes
                    554px. It therefore needs 1439px of viewport, and below
                    that #navDrawer carries it.
     secondary menu seven items = 601px of links, so it fits to well under
                    720px; it only collapses at the phone breakpoint.

   Re-measure both if the labels ever change — the numbers above are the whole
   justification for the two values.

   The orange edge tab is NOT switched here: it belongs to the recent/business
   pop-out, which is wanted at every width.

   The .98 is so a fractional viewport — browser zoom, a scaled display —
   cannot land between the rules and match neither. */
@media (max-width: 1439.98px) {
  .topnav__menu { display: none; }   /* #navDrawer carries it instead */
}

@media (max-width: 719.98px) {
  /* Collapsed behind its own toggle, opening in place under the bar rather
     than as a pop-out, because it belongs to the record on screen. */
  .contextbar__toggle { display: flex; }
  .contextbar__menu { display: none; }
  .contextbar__menu[data-open="true"] {
    display: flex;
    flex-direction: column;
    align-items: stretch;
    gap: 2px;
    overflow-x: visible;
  }
  .contextbar__menu[data-open="true"] .contextbar__link { padding: 8px 10px; }
}

@media (max-width: 720px) {
  .topnav {
    gap: var(--s-8);
    padding: 0 var(--s-12);
  }
  .topnav__brand {
    font-size: 15px;
    flex-shrink: 1;
    min-width: 0;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* The name is in the pop-out head at this width, so hiding it loses nothing. */
  .topnav__who { display: none; }

  .workspace { padding: var(--s-16) var(--s-12) var(--s-32); }
  .page-head h1 { font-size: var(--fs-h3); }
}

/* --- a .grid table becomes a list of cards on a phone -------------------
   WHAT WAS BROKEN: at 390px a six-column table cannot keep its columns
   readable. .table-wrap scrolls, but `table.grid { width: 100% }` squeezes the
   table into the card FIRST and only scrolls once the cells can shrink no
   further — so Appointments ended up with its Notes column a few pixels wide,
   wrapping one character per line down a row hundreds of pixels tall.

   Widening the table and leaving it to scroll sideways would fix the wrapping
   and leave a phone dragging a table around to read a row. So below 640px the
   row becomes the card: each cell on its own line under its column heading.
   The headings are stamped onto the cells by the script in layout.php, which
   is why no page's markup mentions them. */
@media (max-width: 640px) {
  .table-wrap { overflow-x: visible; }

  table.grid,
  table.grid tbody,
  table.grid tr,
  table.grid td { display: block; width: auto; }
  table.grid thead { display: none; }

  table.grid tbody tr {
    padding: var(--s-12) var(--s-16);
    border-bottom: 1px solid var(--color-border);
  }
  table.grid tbody tr:last-child { border-bottom: 0; }
  /* The row is the hit target now, so the hover tint on it is just noise. */
  table.grid tbody tr:hover { background: transparent; }

  table.grid tbody td {
    padding: 4px 0;
    border: 0;
    white-space: normal;
  }
  /* The per-page edge insets are for a real table; a stacked cell takes its
     inset from the row. Reset them rather than fight them with !important. */
  .cv-list__body--flush table.grid td:first-child,
  .cv-list__body--flush table.grid td:last-child { padding-left: 0; padding-right: 0; }

  /* Label beside the value, not above it. Stacked, a nine-column row like an
     estimate ran to eighteen lines; side by side it is nine, and the values
     line up in a column the eye can run down. The label column is allowed to
     shrink so a long value still gets most of the width. */
  table.grid tbody td[data-label] {
    display: grid;
    grid-template-columns: minmax(76px, 32%) 1fr;
    gap: var(--s-12);
    align-items: baseline;
    padding: 5px 0;
  }
  table.grid tbody td[data-label]::before {
    content: attr(data-label);
    font-size: 10px;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: .05em;
    color: var(--color-muted);
    /* The label is a whole grid column; keep it on its own line rather than
       wrapping "BID DATE" into two and throwing the baseline out. */
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  /* The identity cell (logo/avatar + name) carries no label — see layout.php.
     It heads the card, with a rule under it separating it from the fields.

     :not(.empty) matters: an empty-state cell is a lone <td>, so it is both
     the first AND the last child and has no label either. Without the
     exclusion it collected this heading rule and drew a stray line under
     "Nothing scheduled." */
  table.grid tbody td:first-child:not([data-label]):not(.empty) {
    margin-bottom: 6px;
    padding-bottom: 8px;
    border-bottom: 1px solid var(--color-border);
    font-size: 14px;
  }
  /* Trailing action cells ("Open") have an empty heading and stand alone. */
  table.grid tbody td:last-child:not([data-label]):not(.empty) { padding-top: var(--s-8); }

  /* Empty states span the row and carry no heading. */
  table.grid tbody td.empty {
    padding: var(--s-8) 0;
    margin: 0;
    border: 0;
    text-align: left;
  }

  /* A cell with nothing in it would otherwise show its heading over a blank
     line; the em-dash placeholder the pages render keeps it honest, but a
     truly empty one should just go. */
  table.grid tbody td:empty { display: none; }
}

/* Small phones: the Feedback button keeps its icon and drops its label. */
@media (max-width: 460px) {
  .feedback-btn { padding: 5px 9px; }
  .feedback-btn__label { display: none; }
}
