:root {
  color-scheme: light dark;
  --bg: #f6f6f4;
  --surface: #ffffff;
  --surface-2: #fbfbf9;
  --border: #e4e4e0;
  --border-strong: #d3d3ce;
  --text: #1b1b19;
  --text-muted: #6c6c66;
  --text-faint: #9a9a92;
  --accent: #17614a;
  --accent-hover: #124d3b;
  --accent-soft: #e7f1ed;
  --flag: #97590a;
  --flag-bg: #fbf0df;
  --score-track: #eceae4;
  --score-fill: #bfe0d3;
  --shadow: 0 1px 2px rgba(20, 20, 18, 0.04), 0 8px 24px rgba(20, 20, 18, 0.06);
}

@media (prefers-color-scheme: dark) {
  :root {
    --bg: #131417;
    --surface: #1c1e21;
    --surface-2: #202327;
    --border: #2f3237;
    --border-strong: #3c4046;
    --text: #ececea;
    --text-muted: #9ca0a3;
    --text-faint: #6f7377;
    --accent: #52d6ac;
    --accent-hover: #6fe1bd;
    --accent-soft: #16302810;
    --flag: #f0c079;
    --flag-bg: #38290f;
    --score-track: #2b2f35;
    --score-fill: #2f6b58;
    --shadow: 0 1px 2px rgba(0, 0, 0, 0.3), 0 10px 30px rgba(0, 0, 0, 0.35);
  }
}

* { box-sizing: border-box; }

body {
  margin: 0;
  background: var(--bg);
  color: var(--text);
  font: 14.5px/1.55 -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
  -webkit-font-smoothing: antialiased;
}

a { color: var(--accent); }
img { max-width: 100%; }

.wrap {
  max-width: 1280px;
  margin: 0 auto;
  padding: 0 28px;
}

/* ---------- header ---------- */
.site-header {
  /* NOT sticky, on purpose: it needs to coordinate its height with the
     dataset table's own sticky header (see thead th below), and
     position:sticky with a non-zero offset set dynamically (either via a
     CSS custom property or inline style) proved unreliable here -- it
     silently stopped sticking altogether instead of sitting below the
     site header. table-header stickiness is worth far more when scanning
     a 1000+ row table than the nav staying pinned while scrolling one. */
  background: var(--surface);
  border-bottom: 1px solid var(--border);
}

.header-inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 16px;
  padding: 16px 0;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  text-decoration: none;
  color: var(--text);
  flex: none;
}

.brand-mark {
  width: 14px;
  height: 14px;
  border-radius: 4px;
  background: var(--accent);
  transform: rotate(45deg);
  flex: none;
}

.brand-text {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.brand-name {
  font-size: 1.15rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.brand-tag {
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.03em;
  text-transform: uppercase;
  color: var(--text-muted);
}

/* ---------- main / views ---------- */
main.wrap {
  padding-top: 30px;
  padding-bottom: 64px;
}

/* ---------- landing ---------- */
.hero {
  padding: 56px 0 40px;
  max-width: 760px;
}

.hero-title {
  margin: 0;
  font-size: 2.5rem;
  font-weight: 800;
  letter-spacing: -0.02em;
  line-height: 1.12;
}

.hero-sub {
  margin: 18px 0 0;
  max-width: 62ch;
  color: var(--text-muted);
  font-size: 1.05rem;
  line-height: 1.6;
}

.hero-actions { margin-top: 28px; }

.btn-large {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: 13px 24px;
  font-size: 0.98rem;
  border-radius: 10px;
}

.hero-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 28px;
  margin-top: 40px;
  padding-top: 28px;
  border-top: 1px solid var(--border);
}

.stat { display: flex; flex-direction: column; gap: 2px; }
.stat-num {
  font-size: 1.6rem;
  font-weight: 800;
  color: var(--accent);
  letter-spacing: -0.01em;
  font-variant-numeric: tabular-nums;
}
.stat-label { color: var(--text-muted); font-size: 0.82rem; }

.feature-grid {
  margin: 12px 0 20px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 16px;
}

.feature {
  padding: 22px 20px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
}

.feature-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 34px;
  height: 34px;
  margin-bottom: 12px;
  border-radius: 9px;
  background: var(--accent-soft);
  color: var(--accent);
  font-size: 1.05rem;
}

.feature h3 {
  margin: 0 0 6px;
  font-size: 0.98rem;
  font-weight: 700;
  letter-spacing: -0.005em;
}

.feature p {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.87rem;
  line-height: 1.55;
}

/* ---------- landing: expanded sections ---------- */
.landing-section {
  padding: 48px 0 4px;
  border-top: 1px solid var(--border);
}
.landing-section-first { padding-top: 44px; }

.section-eyebrow {
  margin: 0;
  color: var(--accent);
  font-size: 0.76rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
}

.section-title {
  margin: 6px 0 24px;
  max-width: 46ch;
  font-size: 1.4rem;
  font-weight: 800;
  letter-spacing: -0.015em;
  line-height: 1.25;
}

/* same card shape as .feature (border/radius/padding), no icon badge --
   these three are personas, not features, so the emphasis sits on the
   heading instead of an icon. */
.audience-grid {
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(230px, 1fr));
  gap: 16px;
}

.audience-card {
  padding: 22px 20px;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
}

.audience-card h3 {
  margin: 0 0 8px;
  font-size: 1.02rem;
  font-weight: 700;
  letter-spacing: -0.008em;
}

.audience-card p {
  margin: 0;
  color: var(--text-muted);
  font-size: 0.87rem;
  line-height: 1.55;
}

.landing-cta {
  /* var(--accent-soft) was the first pass here -- looked fine in light
     mode but, measured in dark mode, its ~6% alpha over --bg (#131417) is
     nearly indistinguishable from the page background at this size (it's
     only ever used elsewhere as a small icon-badge fill, not a large
     block). var(--surface) + a border is the same bordered-card treatment
     already proven everywhere else on this page (.feature, .audience-card,
     .lead-card) -- reads consistently in both themes. */
  margin: 52px 0 40px;
  padding: 40px 32px;
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--surface);
  text-align: center;
}
.landing-cta h2 {
  margin: 0;
  color: var(--accent);
  font-size: 1.4rem;
  font-weight: 800;
  letter-spacing: -0.015em;
}
.landing-cta p {
  margin: 10px 0 22px;
  color: var(--text-muted);
  font-size: 0.92rem;
}

@media (max-width: 640px) {
  .hero { padding: 36px 0 28px; }
  .hero-title { font-size: 1.9rem; }
  .landing-section { padding-top: 36px; }
  .landing-cta { padding: 32px 22px; margin: 40px 0 28px; }
}

.view-title {
  margin: 0;
  font-size: 1.4rem;
  font-weight: 700;
  letter-spacing: -0.015em;
}

.view-sub {
  margin: 6px 0 0;
  color: var(--text-muted);
  font-size: 0.92rem;
}

.loading {
  color: var(--text-muted);
  font-size: 0.9rem;
  padding: 24px 0;
}

/* ---------- home filters ---------- */
.home-filters {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 10px;
  margin-top: 20px;
}

.home-filters select {
  /* Real report, 2026-09-18: "Most recent" (the longest of the 3 option
     labels here) rendered with its text almost touching the arrow --
     confirmed live, the SVG background-image had no explicit
     background-size, so it fell back to "auto" (an unpredictable
     intrinsic size for a viewBox-only SVG, no width/height attributes of
     its own) instead of a known, small icon size. Fixed with an explicit
     background-size below plus a bit more reserved padding, rather than
     leaving the actual rendered arrow size to browser guesswork. */
  padding: 8px 34px 8px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font-size: 0.86rem;
  font-weight: 600;
  cursor: pointer;
  /* plain <select> arrow, drawn instead of relying on the OS default so
     it reads consistently in both themes */
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='%239a9a92'%3E%3Cpath d='M5.5 7.5l4.5 4.5 4.5-4.5z'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 10px center;
  background-size: 12px;
}
.home-filters select:focus { outline: 2px solid var(--accent); outline-offset: 1px; }

.link-btn {
  flex: none;
  border: none;
  background: none;
  padding: 4px 2px;
  color: var(--accent);
  font-size: 0.84rem;
  font-weight: 600;
  cursor: pointer;
}
.link-btn:hover { text-decoration: underline; }

/* ---------- home cards ---------- */
.card-grid {
  margin-top: 18px;
  display: grid;
  grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
  gap: 16px;
}

.lead-card {
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  text-decoration: none;
  color: inherit;
  overflow: hidden;
  transition: transform 0.14s ease, border-color 0.14s ease, box-shadow 0.14s ease;
}

.lead-card:hover {
  transform: translateY(-2px);
  border-color: var(--accent);
  box-shadow: var(--shadow);
}

.lead-card-body { padding: 20px 20px 16px; }

.lead-card-body h3 {
  margin: 0;
  font-size: 1.06rem;
  font-weight: 700;
  letter-spacing: -0.01em;
}

.lead-card-loc {
  margin: 3px 0 14px;
  color: var(--text-muted);
  font-size: 0.9rem;
}

.chips { display: flex; flex-wrap: wrap; gap: 6px; }

.chip {
  font-size: 0.72rem;
  font-weight: 600;
  padding: 3px 9px;
  border-radius: 999px;
  background: var(--surface-2);
  border: 1px solid var(--border);
  color: var(--text-muted);
  white-space: nowrap;
}

.lead-card-foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 12px 20px;
  border-top: 1px solid var(--border);
  background: var(--surface-2);
  color: var(--accent);
  font-weight: 600;
  font-size: 0.85rem;
}

/* ---------- dataset header ---------- */
.back-link {
  display: inline-block;
  margin-bottom: 16px;
  color: var(--text-muted);
  text-decoration: none;
  font-size: 0.85rem;
  font-weight: 600;
}
.back-link:hover { color: var(--accent); }

.dataset-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: 16px;
  margin-bottom: 20px;
}

/* ---------- notes / legend ---------- */
.intel-note {
  margin: 0 0 16px;
  padding: 10px 13px;
  border: 1px solid var(--flag);
  background: var(--flag-bg);
  color: var(--flag);
  border-radius: 8px;
  font-size: 0.85rem;
}

.legend {
  margin: 0 0 18px;
  font-size: 0.85rem;
  color: var(--text-muted);
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  padding: 12px 14px;
}
.legend summary { cursor: pointer; font-weight: 600; color: var(--text); }
.legend ul { margin: 10px 0 4px; padding-left: 20px; }
.legend li { margin: 5px 0; }
.legend strong { color: var(--text); }
.legend-foot { margin: 8px 0 0; font-style: italic; }

/* ---------- controls ---------- */
.controls {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: 10px 14px;
  margin-bottom: 10px;
}

#search-box {
  flex: 1 1 auto;
  min-width: 0;
  max-width: 420px;
  padding: 9px 12px;
  border: 1px solid var(--border-strong);
  border-radius: 8px;
  background: var(--surface);
  color: var(--text);
  font-size: 0.9rem;
}
#search-box:focus { outline: 2px solid var(--accent); outline-offset: 1px; border-color: transparent; }

.result-count {
  flex: 1 1 auto;
  color: var(--text-faint);
  font-size: 0.82rem;
  white-space: nowrap;
}

.export-menu {
  position: relative;
  flex: none;
  margin-left: auto;
}

.btn-solid {
  padding: 9px 16px;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.86rem;
  cursor: pointer;
  white-space: nowrap;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
  text-decoration: none;  /* also used on <a> (the hero CTA) */
}
.btn-solid:hover:not(:disabled) { background: var(--accent-hover); border-color: var(--accent-hover); }
.btn-solid:disabled { opacity: 0.5; cursor: not-allowed; }
@media (prefers-color-scheme: dark) { .btn-solid { color: #10221c; } }

.export-panel {
  position: absolute;
  top: calc(100% + 6px);
  right: 0;
  z-index: 5;
  min-width: 170px;
  border: 1px solid var(--border-strong);
  border-radius: 10px;
  background: var(--surface);
  box-shadow: var(--shadow);
  padding: 6px;
}
.export-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 8px 12px;
  border: none;
  border-radius: 6px;
  background: none;
  color: var(--text);
  font-size: 0.86rem;
  font-weight: 600;
  cursor: pointer;
}
.export-item:hover { background: var(--surface-2); color: var(--accent); }

.scroll-hint {
  margin: 0 0 8px;
  color: var(--text-faint);
  font-size: 0.78rem;
}

/* ---------- table ----------
   A combined lead + intelligence table is genuinely a lot of columns.
   Two things minimize horizontal scroll rather than force-fitting widths
   down to illegibility: (1) .table-bleed breaks the table out of .wrap's
   1280px reading-width column to the full viewport width -- the standard
   "full-bleed section inside a centered page" trick (margin equal to half
   the viewport minus half the (still constrained) parent, so it re-centers
   at 100vw regardless of nesting); (2) comfortable but not padded-out
   per-column pixel widths (table-layout:auto, not fixed, so a column can
   still flex slightly for genuinely long content). On anything wider than
   a small laptop this fits every column with zero horizontal scroll --
   verified at 1440/1536/1920px, see the commit this landed in. Narrower
   than that, .table-wrap still scrolls sideways rather than crushing
   content -- the business-name column stays pinned (position: sticky) so
   you always know which row you're on. #scroll-hint only shows when a
   render actually overflows (toggled in app.js), not as a standing claim
   that might not be true at the viewer's own width. */
.table-bleed {
  /* Pure breakout, uncapped -- always genuinely 100vw, so the
     calc(50% - 50vw) trick (50% of the *constrained* parent minus 50% of
     the true viewport) always correctly re-centers it at the real
     viewport edges regardless of nesting. Capping this element directly
     with a max-width does NOT work: past the cap this is no longer
     really 100vw, so the formula overshoots and the element ends up
     nowhere near centered (confirmed -- shipped that, then measured it on
     a real ultrawide width and it wasn't). The cap belongs on the child
     below instead, where centering it is just an ordinary margin:auto
     within a genuinely full-viewport-width parent. */
  width: 100vw;
  margin-left: calc(50% - 50vw);
  padding: 0 28px;  /* matches .wrap's own side gutter */
}
.table-bleed-inner {
  max-width: 2200px;  /* stop growing past a sane point on a genuinely
                          huge/ultrawide monitor rather than stretching
                          every column absurdly wide */
  margin: 0 auto;  /* centers correctly because .table-bleed (its parent)
                       is truly 100vw wide, unlike .table-bleed itself */
}

.table-wrap {
  overflow-x: auto;
  /* Also bounds the height and scrolls it (rather than leaving overflow-y
     at its default) so thead's position:sticky has an ancestor that
     actually scrolls vertically to stick against. Setting overflow-x
     alone, with overflow-y left "visible", doesn't give a truly unbounded
     vertical overflow the way it looks like it would -- per the CSS
     overflow spec, "visible" paired with a non-visible sibling axis
     computes to "auto" instead, so .table-wrap was ALREADY the sticky
     header's scrolling ancestor even without max-height/overflow-y here;
     it just never actually scrolled vertically (unbounded height), so the
     header silently never stuck. Bounding the height is what makes that
     already-implied scroll container a real, working one -- a nice side
     effect being a 1900-row table no longer makes the whole page enormous.
  */
  max-height: 70vh;
  overflow-y: auto;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
}

table {
  table-layout: fixed;  /* each column gets EXACTLY its th's width as a
                            MINIMUM, not just a hint content can override
                            -- table-layout:auto (used before 2026-09-11)
                            only ever treats a th's width as a suggestion;
                            any nowrap cell with long content (a contact
                            name, three flag badges) silently overrode it
                            and the column ended up wider than intended
                            regardless of what was specified. */
  width: 100%;  /* fixed layout's own rule: when the table's resolved
                    width exceeds the sum of its column widths, the extra
                    is distributed proportionally across columns rather
                    than left blank -- this is what actually makes the
                    table fill a wide/ultrawide monitor's real estate
                    instead of stopping at its minimum width and leaving
                    the rest of a full-bleed .table-wrap empty. app.js
                    sets min-width (not width) to that column-width sum,
                    so a narrower container still shrinks the table down
                    to it (and scrolls) rather than crushing columns. */
  border-collapse: separate;  /* required for position:sticky cells to be
                                  reliable -- collapse breaks it in Safari */
  border-spacing: 0;
  font-size: 0.85rem;
}

th, td {
  text-align: left;
  padding: 7px 11px;
  border-bottom: 1px solid var(--border);
  /* Everything wraps by default (2026-09-11) -- a contact name, a city, a
     URL, a header label like "BBB complaints" all just flow onto a 2nd
     line rather than getting cut off. An earlier pass here used ellipsis
     truncation instead (nowrap + a title="" tooltip for the hidden part);
     on a real phone there's no hover to reveal that tooltip with, so
     truncated text was just permanently unreadable -- wrapping costs
     some row height, never costs information. overflow stays visible
     (not hidden) for the same reason: a score bar or badge that's ever
     very slightly wider than its column should spill a few px rather
     than be invisibly clipped -- column widths already have real
     breathing room over what they hold, so this is a rare-case backstop,
     not something expected to fire often. */
  white-space: normal;
  overflow-wrap: break-word;
  /* 2026-09-22, Nick's report: "looks chunky." Root cause, measured live:
     a wrapping column (Top complaint especially, sometimes Flags) can
     genuinely wrap to 4-5 lines and drive a row's real height up past
     200px, and every td's own default vertical-align (middle, the plain
     browser default -- nothing here ever set it explicitly before) then
     centers a short cell's one line of content in the middle of all that
     extra height, reading as a wide band of empty padding above and below
     it. The row height itself is unavoidable (it's real content, and
     truncating it would just hide information -- see this rule's own
     "everything wraps" comment above), but pinning every cell's content
     to the top removes the empty-band illusion without losing anything.
     Paired with a couple px less padding for an overall denser table. */
  vertical-align: top;
}

/* Sticky-while-scrolling targets .name-cell (the Business column's own
   class from COLUMNS) rather than :first-child now -- 2026-09-22, when the
   new "Details" column was added at the END of COLUMNS. A positional
   :first-child selector would have silently migrated this sticky/shadow
   treatment onto whichever column happens to render first, regardless of
   which one it actually is -- decoupling it from position (Business is
   always meant to stay pinned; nothing else should, wherever it sits) is
   what actually matches the intent here. */
thead th.name-cell, tbody td.name-cell {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--surface);
  box-shadow: 1px 0 0 var(--border);
}
thead th.name-cell { z-index: 3; background: var(--surface-2); }

thead th {
  position: sticky;
  top: 0;  /* the site header isn't sticky, so this is the true viewport top -- see its comment */
  z-index: 2;
  background: var(--surface-2);
  color: var(--text-muted);
  font-weight: 600;
  font-size: 0.74rem;
  letter-spacing: 0.02em;
  text-transform: uppercase;
  cursor: pointer;
  user-select: none;
}
thead th:hover { color: var(--text); }
thead th.sorted::after { content: " \25BC"; font-size: 0.7em; }
thead th.sorted-desc::after { content: " \25B2"; font-size: 0.7em; }

tbody tr:last-child td { border-bottom: none; }
tbody tr:nth-child(even) td { background: color-mix(in srgb, var(--text) 3%, var(--surface)); }
tbody tr:hover td { background: color-mix(in srgb, var(--accent) 6%, var(--surface)); }
tbody tr:nth-child(even) td.name-cell { background: color-mix(in srgb, var(--text) 3%, var(--surface)); }
tbody tr:hover td.name-cell { background: color-mix(in srgb, var(--accent) 6%, var(--surface)); }
/* The stronger hover tint just tracks which row the mouse is over while
   scanning a dense table -- no cursor:pointer here (2026-09-24, Nick's
   ask: only the row's own "Details" button should open the drill-down,
   not a click anywhere on the row -- see onRowActivate's own comment in
   app.js) since the row itself isn't a click target anymore. */

td { color: var(--text); font-variant-numeric: tabular-nums; }
td.num-cell, thead th.num-cell { text-align: right; }

td.name-cell { font-weight: 600; }
td.name-cell a { color: var(--text); text-decoration: none; }
td.name-cell a:hover { color: var(--accent); text-decoration: underline; }

.muted { color: var(--text-faint); }

/* ---------- mobile card list (replaces the table below 560px) ----------
   2026-09-17, Nick's report: the sticky Business column alone measured
   200 of 375 real phone-viewport px -- 53% of the screen gone before a
   visitor saw even one more of the table's 15 columns. A narrower sticky
   column still isn't a real fix; a wide table is fundamentally the wrong
   shape for a phone. #mobile-cards stays empty and display:none above the
   breakpoint (app.js only ever builds its innerHTML when
   MOBILE_BREAKPOINT.matches, so this is also a real perf guard, not just
   visual hiding) -- style.css's own @media (max-width: 560px) block below
   flips this on and the table off. */
#mobile-sort { display: none; }
.mobile-cards { display: none; }

.lrc {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  padding: 14px 16px;
  margin-bottom: 12px;
}
.lrc-head { margin-bottom: 10px; }
.lrc-name { font-weight: 700; font-size: 1.02rem; }
.lrc-name a { color: var(--text); text-decoration: none; }
.lrc-name a:hover { color: var(--accent); text-decoration: underline; }
.lrc-sub { color: var(--text-muted); font-size: 0.86rem; margin-top: 2px; }

/* The two numbers a rep scans for first (lead priority, reach) pulled out
   of the field list into their own strip -- same "at a glance" reasoning
   as the Lists page's own chips, just for one business instead of one
   dataset. */
.lrc-stats {
  display: flex;
  gap: 18px;
  padding: 10px 0;
  margin-bottom: 4px;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.lrc-stat { display: flex; flex-direction: column; gap: 4px; }
.lrc-stat-label {
  color: var(--text-muted);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

/* Every other column as a plain label:value row -- a real <dl>, not a
   table, so nothing here is pretending to be a grid it isn't at this
   width. dt/dd share a line when both are short and wrap independently
   when they're not, same "never truncate, let it wrap" rule the table's
   own td already follows. */
.lrc-fields { margin: 0; }
.lrc-field {
  display: flex;
  flex-wrap: wrap;
  gap: 4px 10px;
  padding: 7px 0;
  border-bottom: 1px solid var(--border);
  font-size: 0.86rem;
}
.lrc-field:last-child { border-bottom: none; }
.lrc-field dt { flex: 0 0 auto; min-width: 118px; color: var(--text-muted); font-weight: 600; }
/* min-width: 0 overrides a flex item's default min-width:auto (its content's
   own min-content size) -- without it, a long unbroken run in a dd's own
   text (a social link rendered as its raw URL, e.g. a percent-encoded
   "...%3A%2F%2Fwww..." stretch with no real "/" or "-" characters to break
   on) forces the dd wider than its 140px basis instead of wrapping, and
   since nothing here scrolls, that excess is invisibly clipped past the
   card's edge rather than pushed onto a new line -- confirmed live 2026-09-28,
   a real business's LinkedIn link (found via facebook_social_links, not
   BBB's own socials) reproduced this exactly on mobile. overflow-wrap
   mirrors the desktop table's own th/td rule above for the identical
   long-unbroken-content problem. */
.lrc-field dd { flex: 1 1 140px; min-width: 0; margin: 0; color: var(--text); font-variant-numeric: tabular-nums; overflow-wrap: break-word; }

/* BBB grade + accreditation share one cell now (was two columns) --
   this is just the accreditation half of it. */
.accredited-check {
  color: var(--accent);
  font-weight: 700;
}

/* badges + chips in cells */
.badge {
  display: inline-block;
  padding: 2px 8px;
  border-radius: 999px;
  font-size: 0.72rem;
  font-weight: 600;
  white-space: nowrap;
}
.badge-yes { background: var(--accent-soft); color: var(--accent); }
.badge-flag { background: var(--flag-bg); color: var(--flag); }
.badge-soft { border: 1px solid var(--border-strong); color: var(--text-muted); }

/* Flags cell can hold up to 3 badges that wrap onto more than one line in
   a narrow column -- a real flex `gap` (both row- and column-wise) instead
   of relying on a plain text-node space between them, which collapses
   right at a line-wrap boundary and let two badges' rounded pill edges
   touch/overlap with no visible gap there. */
.badge-group {
  display: inline-flex;
  flex-wrap: wrap;
  gap: 5px 6px;
}

/* Top complaint cell: theme is a short lightweight label ahead of the real
   sentence, not a badge -- it's reading as one clause, not a separate tag. */
.top-complaint-theme {
  color: var(--text-muted);
  font-weight: 600;
  text-transform: capitalize;
}
/* How many negative/mixed reviews actually share the theme above (2026-09-29)
   -- deliberately lighter/smaller than the theme label itself, a footnote
   confirming "this isn't just one opinion", not a competing focal point. */
.top-complaint-count {
  color: var(--text-muted);
  font-weight: 400;
  font-size: 0.85em;
}

/* score bar */
.score {
  position: relative;
  display: inline-flex;
  align-items: center;
  min-width: 78px;
  height: 22px;
  padding: 0 9px;
  border-radius: 5px;
  background: var(--score-track);
  overflow: hidden;
}
.score-bar {
  position: absolute;
  inset: 0 auto 0 0;
  background: var(--score-fill);
}
.score-val { position: relative; font-weight: 600; font-variant-numeric: tabular-nums; }

/* Google's own per-star review breakdown (2026-09-26) -- 5 rows, one per
   star level, so a business whose average alone reads fine but has a real
   1-2-star cluster underneath it (the thing a plain average hides) is
   actually visible, not just a number. Color-coded the same way the rest
   of the site already does (--accent for good, --flag for the reviews a
   rep would actually cite), not a new palette. */
.google-star-breakdown { display: flex; flex-direction: column; gap: 4px; margin-top: 8px; }
.gsb-row { display: flex; align-items: center; gap: 8px; font-size: 0.82rem; }
.gsb-label { flex: 0 0 24px; color: var(--text-muted); font-weight: 600; text-align: right; }
.gsb-track { flex: 1 1 auto; height: 9px; border-radius: 4px; background: var(--score-track); overflow: hidden; }
.gsb-fill { display: block; height: 100%; }
.gsb-fill-negative { background: var(--flag); }
.gsb-fill-neutral { background: var(--border-strong); }
.gsb-fill-positive { background: var(--accent); }
.gsb-count { flex: 0 0 32px; text-align: right; font-variant-numeric: tabular-nums; color: var(--text-muted); }
.gsb-note { margin: 6px 0 0; font-size: 0.82rem; color: var(--flag); font-weight: 600; }

.empty-state {
  text-align: center;
  color: var(--text-muted);
  padding: 44px 0;
}

/* ---------- row "Details" button + mobile card, same affordance ----------
   Last column on desktop (not first -- see the .name-cell sticky comment
   above for why position matters here), and repeated next to the name in
   a mobile card's own head. One real, clearly-labeled <button> per row is
   the actual accessible entry point into the drill-down -- a bare <tr> or
   .lrc can't be reached by a keyboard/screen-reader user at all, so
   "the whole row is also clickable" (tbody tr { cursor: pointer } above,
   .lrc below) is a mouse convenience layered on TOP of this, never a
   replacement for it.

   2026-09-22, Nick's feedback: make it more apparent. It was a quiet
   outline pill, the same visual weight as a plain data badge -- easy to
   read as just another cell instead of the one button on the row that
   actually does something. Filled solid in --accent, same treatment as
   the page's other real buttons (.btn-solid -- Export, "Browse lead
   lists"), and (2026-09-24, Nick's ask: "less of a pill, cleaner like a
   button") now shares .btn-solid's own rounded-rect radius instead of a
   999px pill with its own custom chevron -- one consistent button shape
   for every real action on the page, not a second one just for this. The
   column also pins to the right edge of the table (a mirror of
   .name-cell's own sticky-left below) so it's never scrolled out of view
   on a wide table -- discoverable regardless of where the table happens
   to be scrolled to. */
.row-detail-btn {
  padding: 6px 12px;
  border-radius: 8px;
  font-weight: 600;
  font-size: 0.78rem;
  cursor: pointer;
  white-space: nowrap;
  border: 1px solid var(--accent);
  background: var(--accent);
  color: #fff;
}
.row-detail-btn:hover { background: var(--accent-hover); border-color: var(--accent-hover); }
@media (prefers-color-scheme: dark) { .row-detail-btn { color: #10221c; } }
td.detail-cell { text-align: center; }

/* Sticky-right, mirroring .name-cell's sticky-left above -- same reasons,
   opposite edge: the Details column stays visible at the right of the
   viewport no matter how far the table is scrolled sideways. Needs its
   own solid background + shadow for the same reason .name-cell does (a
   sticky cell sits above scrolling siblings, so the page's usual
   semi-transparent striping/hover tints would let content show through
   underneath it as it scrolls past). */
thead th.detail-cell, tbody td.detail-cell {
  position: sticky;
  right: 0;
  z-index: 1;
  background: var(--surface);
  box-shadow: -1px 0 0 var(--border);
}
thead th.detail-cell { z-index: 3; background: var(--surface-2); }
tbody tr:nth-child(even) td.detail-cell { background: color-mix(in srgb, var(--text) 3%, var(--surface)); }
tbody tr:hover td.detail-cell { background: color-mix(in srgb, var(--accent) 6%, var(--surface)); }

.lrc-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }

/* ---------- detail drill-down (every column, one business, grouped) ----------
   2026-09-22, Nick's ask: click into a row and see basically every column
   from the Excel export, in the browser, without leaving the list. A modal
   over the current table (not a separate page) so filters/sort/scroll
   position on the list behind it are never disturbed -- closing it lands
   you exactly back where you were. Still genuinely linkable/shareable and
   back-button-friendly: app.js drives open/close purely off the URL hash
   (#/<dataset>/biz/<profile-url>), so a bookmark or a pasted link reopens
   the same business's detail directly, and the browser's own Back closes
   it like any other navigation -- see app.js's route()/openDetail() for
   why every close path (X button, Escape, backdrop click) goes through a
   hash change rather than hiding the modal directly. */
.detail-overlay {
  position: fixed;
  inset: 0;
  z-index: 100;
  display: flex;
  align-items: flex-start;
  justify-content: center;
  padding: 5vh 20px;
  background: color-mix(in srgb, black 55%, transparent);
  /* Not scrollable itself (2026-09-24, real report: content felt "cut off,
     have to scroll" to see it all) -- .detail-modal already caps at 90vh
     and .detail-body below is the one designated scroll region for
     everything inside it, so a second scrollable ancestor here just meant
     a stray wheel/touch gesture over the backdrop could scroll THIS
     instead of the modal's own content, or fight it via scroll chaining --
     worse on mobile, where .detail-modal fills the whole viewport (see the
     mobile block below) and this had nothing left to scroll anyway. */
  overflow: hidden;
}
/* A plain `[hidden]` attribute selector and this class selector are equal
   CSS specificity -- whichever rule comes later in the stylesheet wins,
   attribute or not, so `display: flex` above silently overrode the
   browser's own `[hidden] { display: none }` and the (empty, since
   detailBody.innerHTML is only ever filled by openDetail()) modal shell
   showed up floating over the page on every single load. Caught live in
   the browser pane, 2026-09-22, first real check of this feature. */
.detail-overlay[hidden] { display: none; }
.detail-modal {
  position: relative;
  width: min(94vw, 860px);
  max-height: 90vh;
  display: flex;
  flex-direction: column;
  border-radius: 14px;
  background: var(--surface);
  box-shadow: var(--shadow);
  outline: none;  /* it's still a real focus target (tabindex="-1", moved
                      to programmatically on open) -- just without the
                      default browser focus ring a plain click never
                      needs to see; keyboard users still get the ring on
                      every real control inside (buttons, links). */
}
.detail-close {
  position: absolute;
  top: 14px;
  right: 14px;
  z-index: 1;
  width: 34px;
  height: 34px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--border-strong);
  border-radius: 999px;
  background: var(--surface);
  color: var(--text-muted);
  font-size: 1.2rem;
  line-height: 1;
  cursor: pointer;
}
.detail-close:hover { color: var(--text); border-color: var(--accent); }

/* overscroll-behavior: contain -- real report, 2026-09-28: on mobile,
   dragging to the very bottom of a long expanded modal (e.g. down to "All
   fields (technical)") didn't reliably get there. body.style.overflow =
   "hidden" (openDetail(), app.js) stops desktop wheel/rubber-band scroll
   but is well-documented as NOT reliably stopping a touch-drag from
   scrolling the page behind a fixed overlay on mobile Safari -- once this
   element hit ITS OWN scroll limit mid-gesture, the rest of the drag could
   chain onto the background page instead of stopping, since body is a real
   scrollable ancestor here (body > #detail-overlay > .detail-modal >
   .detail-body). This tells the browser to stop the chain right at this
   element's own boundary instead of handing overscroll to whatever's
   behind it. */
.detail-body { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; padding: 26px 28px 30px; }

.detail-head { padding-right: 34px; }  /* clears the close button */
.detail-head h2 {
  margin: 0;
  font-size: 1.32rem;
  font-weight: 800;
  letter-spacing: -0.015em;
}
.detail-head-sub { margin: 5px 0 0; color: var(--text-muted); font-size: 0.88rem; }

.detail-stats {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 16px 22px;
  margin-top: 16px;
  padding: 14px 0;
  border-top: 1px solid var(--border);
  border-bottom: 1px solid var(--border);
}
.detail-stat { display: flex; flex-direction: column; gap: 4px; }
.detail-stat-label {
  color: var(--text-muted);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.detail-flags { margin-top: 12px; }

/* Latest activity's own headline + per-source grid (2026-09-24, Nick's ask:
   the per-source dates read as a flat list -- "make it more appealing").
   .activity-headline mirrors .detail-stat's label-over-value shape (the
   modal's own header stats above use the identical pattern) so the single
   most important number here reads with the same weight as Lead priority/
   BBB/Reach do up top, not as just another list row. */
.activity-headline {
  display: flex;
  align-items: baseline;
  gap: 10px;
  flex-wrap: wrap;
}
.activity-headline-label {
  color: var(--text-muted);
  font-size: 0.68rem;
  font-weight: 600;
  letter-spacing: 0.02em;
  text-transform: uppercase;
}
.activity-headline-value { font-size: 1.05rem; font-weight: 700; }
.activity-source-grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
  gap: 10px;
  margin-top: 14px;
}
.activity-source-card {
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  padding: 10px 12px;
}
.activity-source-name {
  font-weight: 700;
  font-size: 0.82rem;
  margin-bottom: 6px;
}
.activity-source-row {
  display: flex;
  justify-content: space-between;
  gap: 8px;
  font-size: 0.82rem;
  color: var(--text-muted);
}
.activity-source-row + .activity-source-row { margin-top: 3px; }
.activity-source-row strong { color: var(--text); font-weight: 600; }
/* Same "this is the actionable one" red every negative sentiment badge
   already uses elsewhere on this page (.badge-flag), not a new color. */
.activity-source-row-negative strong { color: var(--flag); }
/* Data captured is bookkeeping, not part of the "what's this business's
   review activity" story above -- de-emphasized and set off with a rule
   instead of just another dl row in the same visual weight. Last updated
   (redundant with it) dropped entirely 2026-09-24, Nick's ask. */
.activity-meta {
  margin-top: 14px;
  padding-top: 10px;
  border-top: 1px dashed var(--border);
  font-size: 0.82rem;
  opacity: 0.85;
}

/* Each group of fields is a native <details> -- same collapsible pattern
   as the column legend above (#legend), so it needs no new keyboard/
   screen-reader handling of its own: <summary> is already a real,
   focusable toggle. Only Contact & location and Latest activity open by
   default (2026-09-28, Nick's explicit call -- what a rep reaches for
   first); everything else -- scoring detail, reviews, the Data sources
   dropdown (BBB/Yelp/Google/Facebook nested inside it, 2026-10-01) and its
   own per-source toggles, Specialties, and the last-resort raw field
   dump -- starts collapsed so the modal opens at a scannable length
   instead of a full data dump. Social links no longer has its own section
   at all (2026-10-01) -- folded into Contact & location, see
   contactBody's own comment in app.js. See renderDetailHtml() in app.js
   for which detailSection() calls pass `true`. */
.detail-section {
  margin-top: 14px;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface-2);
  padding: 12px 15px;
}
.detail-section summary {
  cursor: pointer;
  font-weight: 700;
  font-size: 0.92rem;
}
.detail-section[open] summary { margin-bottom: 10px; }
.detail-section .detail-section {
  background: var(--surface);
  margin-top: 10px;
}

/* Field rows reuse the mobile card's own dt/dd layout (.lrc-field/-fields)
   -- identical "label wraps independently from value, never truncates"
   shape, no reason for the drill-down to invent a second version of it. */
.detail-section .lrc-fields { margin-top: 2px; }

.detail-chip-group {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-top: 4px;
}

.detail-review-list {
  /* No max-height/overflow of its own (2026-09-24) -- .detail-body is
     already the one scroll region for the whole drawer; a second
     scrollable box nested inside it just meant fighting two scroll
     gestures to read one list, worse on mobile. Each review is now a
     collapsed-by-default <details> (see reviewCard's own comment in
     app.js) instead, so even a 25-review list reads as a compact, scannable
     stack of one-line summaries, not a wall of paragraphs needing its own
     scrollbar to begin with. */
  display: flex;
  flex-direction: column;
  gap: 8px;
}
.detail-review {
  border: 1px solid var(--border);
  border-radius: 8px;
  background: var(--surface);
  padding: 10px 12px;
}
/* A <details>'s own marker is the only visual cue that a review expands --
   kept (not hidden/replaced) for the same reason detail-section's own
   summary doesn't remove it either: one consistent "there's a triangle,
   it's clickable" language for every collapsible thing on this page. */
.detail-review > summary { cursor: pointer; }
.detail-review[open] > summary { margin-bottom: 8px; }
.detail-review-head {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 6px 10px;
  font-size: 0.82rem;
}
.detail-review-name { font-weight: 700; }
.detail-review-date { color: var(--text-faint); }
.detail-review-rating { color: var(--text-muted); font-variant-numeric: tabular-nums; }
.detail-review-extras { display: flex; flex-wrap: wrap; gap: 5px 6px; margin-top: 6px; }
.detail-review-text { margin: 8px 0 0; font-size: 0.87rem; line-height: 1.55; }
.detail-review-thread {
  margin-top: 8px;
  padding-top: 8px;
  border-top: 1px dashed var(--border);
  font-size: 0.83rem;
}
.detail-review-thread p { margin: 4px 0; }
.detail-review-thread strong { color: var(--text-muted); }
.detail-review-empty { color: var(--text-faint); font-size: 0.85rem; font-style: italic; }

.detail-raw-note {
  margin: 0 0 10px;
  color: var(--text-muted);
  font-size: 0.82rem;
  font-style: italic;
}

/* ---------- footer ---------- */
.site-footer {
  border-top: 1px solid var(--border);
  padding: 22px 0 44px;
  color: var(--text-faint);
  font-size: 0.8rem;
}
.site-footer a { color: inherit; }

@media (max-width: 560px) {
  .wrap { padding: 0 18px; }
  .dataset-head { flex-direction: column; }
  #search-box { max-width: none; flex-basis: 100%; }
  #mobile-sort { display: block; flex-basis: 100%; }
  .result-count { order: 3; }
  .export-menu { flex-basis: 100%; margin-left: 0; }
  .btn-solid { width: 100%; text-align: center; }
  .export-panel { left: 0; right: 0; min-width: 0; }
  /* Card list replaces the table+sticky-column below this width -- see
     .lrc's own comment above for why. */
  .scroll-hint, .table-bleed { display: none; }
  .mobile-cards { display: block; }

  /* A centered 860px-wide card makes no sense on a real phone -- full
     screen, no backdrop margin, so the drill-down gets the whole viewport
     instead of a cramped floating box. */
  .detail-overlay { padding: 0; }
  .detail-modal { width: 100vw; max-height: 100vh; height: 100vh; border-radius: 0; }
  .detail-body { padding: 22px 18px 26px; }
}
