  /* Design tokens (--bg, --surface, --accent, brand --ib-*, light-mode, …) now
     live in css/tokens.css — the single source of truth, linked before this
     file on every page. Consume them via var(); do not redefine here. */

  /* Theme toggle button */
  /* Icon buttons — header utilities + sub-app navigation. Replaces a mess
     of inline-styled .btn elements that mixed emojis, text glyphs, and
     SVGs at inconsistent sizes. All header icons now share the same
     pixel-snapped 32×32 footprint and 16px stroked SVG glyph; only the
     color accent differs between utility and themed-navigation variants. */
  .icon-btn {
    width: 32px; height: 32px;
    border-radius: 6px;
    border: 1px solid var(--border2);
    background: var(--surface2);
    color: var(--text2);
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    transition: all 0.15s;
    flex-shrink: 0;
    text-decoration: none;
    font-family: inherit;
    padding: 0;
  }
  .icon-btn:hover { color: var(--text); border-color: var(--border2); background: var(--bg); }
  .icon-btn svg { width: 16px; height: 16px; display: block; }
  /* Birdie is the primary way in (index.html) — tint the header icon in the
     brand hue so it stands out from the neutral utility icons beside it, while
     keeping the clean monochrome line-bird glyph (coloured, not multi-colour).
     color-mix keeps it theme-aware off --link, which is defined for both themes. */
  #birdieBtn { color: var(--link); border-color: color-mix(in srgb, var(--link) 40%, var(--border2)); background: color-mix(in srgb, var(--link) 12%, var(--surface2)); }
  #birdieBtn:hover { color: var(--link); border-color: var(--link); background: color-mix(in srgb, var(--link) 22%, var(--surface2)); }

  /* "What's new" pulse dot on the help button — set by app.js on load
     when the user hasn't yet opened the panel since the latest update,
     and cleared by IBHelp.open(). Pure CSS animation, sized to read at
     a glance without competing with the icon itself. */
  .icon-btn.has-help-update { position: relative; }
  .icon-btn.has-help-update::after {
    content: '';
    position: absolute; top: 4px; right: 4px;
    width: 7px; height: 7px;
    background: #F04B24;
    border-radius: 50%;
    box-shadow: 0 0 0 2px var(--surface2);
    animation: ib-help-pulse 1.8s ease-in-out infinite;
  }
  @keyframes ib-help-pulse {
    0%, 100% { transform: scale(1);   opacity: 1; }
    50%      { transform: scale(1.35); opacity: 0.7; }
  }

  /* Themed sub-app launchers. The color accent carries semantic meaning
     (cyan = BD intelligence, green = Portfolio Scout extraction, amber =
     admin tooling). Same footprint as the neutral variant, just tinted. */
  .icon-btn--bd {
    background: linear-gradient(135deg, #0c2a3d, #0c3347);
    border-color: #164e63;
    color: #7dd3fc;
  }
  .icon-btn--bd:hover { background: linear-gradient(135deg, #0e3247, #0e3a52); border-color: #1e6889; color: #bae6fd; }

  .icon-btn--scout {
    background: linear-gradient(135deg, #052e16, #0c3d20);
    border-color: #166534;
    color: #86efac;
  }
  .icon-btn--scout:hover { background: linear-gradient(135deg, #073a1c, #0e4a26); border-color: #1d8040; color: #bbf7d0; }

  .icon-btn--admin {
    background: linear-gradient(135deg, #2a1810, #3d2418);
    border-color: #78350f;
    color: #fbbf24;
  }
  .icon-btn--admin:hover { background: linear-gradient(135deg, #321e14, #46291c); border-color: #92450e; color: #fef3c7; }

  /* Light-mode overrides for the three themed variants. The dark gradients
     are tuned for the dark header — against a light surface they read as
     heavy and oversaturated. Drop the gradients, use pale pastel fills with
     a saturated accent color, and let the icon stroke carry the semantic
     meaning. Hover *darkens* in light mode (opposite of dark mode, which
     lightens) so the affordance reads consistently in both themes. */
  body.light-mode .icon-btn--bd {
    background: #e0f2fe; border-color: #7dd3fc; color: #0369a1;
  }
  body.light-mode .icon-btn--bd:hover {
    background: #bae6fd; border-color: #38bdf8; color: #075985;
  }
  body.light-mode .icon-btn--scout {
    background: #dcfce7; border-color: #86efac; color: #15803d;
  }
  body.light-mode .icon-btn--scout:hover {
    background: #bbf7d0; border-color: #4ade80; color: #166534;
  }
  body.light-mode .icon-btn--admin {
    background: #fef3c7; border-color: #fcd34d; color: #b45309;
  }
  body.light-mode .icon-btn--admin:hover {
    background: #fde68a; border-color: #f59e0b; color: #92400e;
  }

  /* ── HEADER OVERFLOW (mobile) ─────────────────────────────────
     Five header icons (export, BD, portfolio, import, admin) collapse
     into a single ⋯ button on narrow screens to keep the header from
     wrapping or clipping. Desktop keeps every icon visible inline. */
  .hdr-overflow { display: none; }
  @media (max-width: 768px) {
    .hdr-collapsible { display: none !important; }
    .hdr-overflow { display: inline-flex; }
  }
  .hdr-overflow-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    min-width: 200px;
    background: var(--surface);
    border: 1px solid var(--border2);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.35);
    padding: 4px;
    z-index: 1200;
  }
  .hdr-overflow-item {
    display: flex; align-items: center; gap: 10px;
    width: 100%; padding: 9px 12px;
    background: none; border: none; color: var(--text);
    font-family: var(--font-main); font-size: 13px; font-weight: 500;
    text-align: left; cursor: pointer; border-radius: 6px;
    text-decoration: none;
  }
  .hdr-overflow-item:hover { background: var(--surface2); }
  .hdr-overflow-item svg { width: 16px; height: 16px; flex-shrink: 0; color: var(--text2); }

  * { box-sizing: border-box; margin: 0; padding: 0; }

  body {
    font-family: var(--font-main);
    background: var(--bg);
    color: var(--text);
    min-height: 100vh;
    font-size: 14px;
  }

  /* Mobile: prevent the page itself from ever scrolling horizontally.
     Long-standing issue where an off-viewport child (a wide deal chip,
     a long unbroken address word, an overlay shadow) widened the body
     and forced iOS Safari to zoom out so the rendered width never matched
     the device width on first paint. `overflow-x: clip` confines the
     overflow without creating a scroll container, so position: sticky
     on the toolbar still works. */
  @media (max-width: 768px) {
    html, body { max-width: 100vw; overflow-x: clip; }
  }

  /* HEADER */
  .header {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    /* Pad past the status bar / notch in standalone PWA mode (black-translucent
       status bar). env() resolves to 0 on non-notched devices/browsers. */
    padding: env(safe-area-inset-top, 0px) 24px 0;
    display: flex;
    align-items: center;
    gap: 16px;
    height: calc(56px + env(safe-area-inset-top, 0px));
    position: sticky;
    top: 0;
    z-index: 100;
  }
  .header-brand { display: flex; align-items: center; gap: 10px; margin-right: 16px; }
  /* Brand mark — IB "buildings" icon on a white tile so both the orange and
     the navy read against the dark header (navy alone vanishes on near-black). */
  .header-logo { width: 32px; height: 32px; flex-shrink: 0; }
  /* Flyover mark carries its own navy tile — no white chrome behind it. The
     mark's own squircle reads at this size; the 7px clip just guarantees a
     crisp corner against the header. */
  .header-logo img {
    width: 100%; height: 100%; border-radius: 7px; display: block;
    object-fit: cover;
  }
  .header-title {
    font-size: 13px;
    font-weight: 600;
    color: var(--text);
    letter-spacing: 0.02em;
    white-space: nowrap;
  }
  .header-sub {
    font-size: 11px;
    color: var(--text3);
    font-family: var(--font-mono);
  }
  .header-right {
    margin-left: auto;
    display: flex;
    align-items: center;
    gap: 10px;
  }
  .btn {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 14px;
    border-radius: 6px;
    font-size: 12px;
    font-weight: 600;
    cursor: pointer;
    border: 1px solid var(--border2);
    background: var(--surface2);
    color: var(--text2);
    font-family: var(--font-main);
    transition: all 0.15s;
  }
  /* var(--border) is #222228 in dark (unchanged) but light grey in light mode —
     the old hardcoded #222228 turned buttons black on hover in light mode, and
     on touch that :hover sticks after a tap (looked like a permanent black
     button on the detail modal). */
  .btn:hover { border-color: var(--border2); color: var(--text); background: var(--border); }
  /* IB Scout CTA — keep the dark-green gradient in dark mode but go light-green
     in light mode (was a hardcoded dark gradient that read as a black button on
     light). Own hover rules out-rank .btn:hover so it doesn't grey out. */
  .scout-cta { background: linear-gradient(135deg,#052e16,#14532d); border: 2px solid #16a34a; color: #4ade80; }
  .scout-cta.has-brief { border-color: rgba(74,222,128,0.47); }
  .btn.scout-cta:hover { background: linear-gradient(135deg,#063d1e,#1a6b3c); border-color: #22c55e; color: #4ade80; }
  body.light-mode .scout-cta { background: #dcfce7; border-color: #16a34a; color: #15803d; }
  body.light-mode .scout-cta.has-brief { border-color: #86efac; }
  body.light-mode .btn.scout-cta:hover { background: #bbf7d0; border-color: #16a34a; color: #166534; }
  .btn-primary {
    background: var(--accent);
    color: #000;
    border-color: var(--accent);
    font-weight: 700;
  }
  .btn-primary:hover { background: var(--accent2); border-color: var(--accent2); color: #000; }

  /* STATUS BAR */
  .status-bar {
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    padding: 10px 24px;
    display: flex;
    align-items: center;
    gap: 24px;
    flex-wrap: wrap;
  }
  /* Desktop: the nav (Cockpit/Table/Map) is the persistent top strip; the
     KPI status-bar drops BELOW it as the contextual row (it's hidden in
     Cockpit anyway). order:90 pushes the status-bar to the end of the flex
     flow; flex-basis 100% forces its own row; negative side margins pull it
     out to the page edges and the negative bottom margin lets it sit flush
     above the table. border-top divides it from the nav row. Mobile rules
     below reset all of this. */
  .toolbar > .status-bar {
    order: 90;
    flex: 0 0 100%;
    margin: 12px -24px -12px;
    border-bottom: none;
    border-top: 1px solid var(--border);
  }
  .stat-pill {
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 6px 10px;
    border-radius: 8px;
    cursor: pointer;
    border: 1px solid transparent;
    background: none;
    font-family: inherit;
    color: inherit;
    transition: background 0.12s, border-color 0.12s;
    text-align: left;
  }
  .stat-pill:hover { background: var(--surface2); border-color: var(--border2); }
  .stat-pill.stat-readonly { cursor: default; }
  .stat-pill.stat-readonly:hover { background: none; border-color: transparent; }
  .stat-pill.stat-active { background: var(--accent-dim); border-color: var(--accent2); }
  .stat-pill.stat-active .stat-num { color: var(--accent); }
  .stat-num {
    font-family: var(--font-mono);
    font-size: 20px;
    font-weight: 500;
    color: var(--text);
    min-width: 32px;
  }
  .stat-label {
    font-size: 11px;
    color: var(--text3);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    line-height: 1.3;
  }
  .stat-dot {
    width: 8px; height: 8px;
    border-radius: 50%;
    flex-shrink: 0;
  }
  /* Size pill: units and SF render as two equal-weight metrics (matching
     .stat-num font) side by side, separated by a middot. */
  .stat-size-grp { display: inline-flex; align-items: baseline; gap: 8px; }
  .stat-size-sep { width: 1px; height: 24px; background: var(--border); align-self: center; flex-shrink: 0; }
  /* Mobile: keep the two size metrics side by side (number over label, like
     the other compact pills) instead of stacking into a column, and fence the
     whole size group off from the project-count pills. On mobile the inter-pill
     dividers are hidden, so SF/Units otherwise butt straight against the
     IB-Active count and read as one garbled number ("64 392.7M"). */
  @media (max-width: 768px) {
    #spill-size {
      flex-direction: row; max-width: none; gap: 10px;
      border-left: 1px solid var(--border); padding-left: 14px; margin-left: 2px;
    }
    #spill-size .stat-size-grp { flex-direction: column; align-items: center; gap: 0; }
  }
  .dot-planned { background: var(--blue); }
  .dot-construction { background: var(--amber); }
  .dot-conversion { background: var(--purple); }
  .dot-existing { background: #34d399; }
  .dot-proposed { background: #fb923c; }
  .dot-ib { background: var(--accent); }
  .divider { width: 1px; height: 28px; background: var(--border); }

  /* TOOLBAR */
  .toolbar {
    padding: 12px 24px;
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    border-bottom: 1px solid var(--border);
  }
  .search-wrap {
    position: relative;
    flex: 1;
    min-width: 200px;
    max-width: 340px;
  }
  .search-wrap svg {
    position: absolute;
    left: 10px;
    top: 50%;
    transform: translateY(-50%);
    color: var(--text3);
    pointer-events: none;
  }
  /* ── OMNIBOX ─────────────────────────────────────────────────
     Unified search bar that replaced three separate toolbar controls
     (Recent dropdown, address search, owner/developer filter). Layout
     is: input on the left with a search icon, a clear (✕) button when
     non-empty, and an absolutely-positioned dropdown panel below that
     holds the grouped suggestion sections (Recent / Owners / Projects). */
  .omnibox-wrap {
    position: relative;
    flex: 1;
    min-width: 200px;
    max-width: 480px;
  }
  .omnibox-icon {
    position: absolute;
    left: 10px; top: 50%; transform: translateY(-50%);
    color: var(--text3);
    pointer-events: none;
  }
  .omnibox-input {
    width: 100%;
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 8px 36px 8px 34px;
    font-size: 13px;
    color: var(--text);
    font-family: var(--font-main);
    outline: none;
    transition: border-color 0.15s;
  }
  .omnibox-input:focus { border-color: var(--accent); }
  .omnibox-input::placeholder { color: var(--text3); }
  .omnibox-clear {
    position: absolute;
    right: 4px; top: 50%; transform: translateY(-50%);
    width: 28px; height: 28px;
    border: none;
    background: none;
    color: var(--text3);
    cursor: pointer;
    font-size: 12px;
    /* Visibility is CSS-driven (not JS-toggled) via the
       :placeholder-shown sibling selector below — atomic with the
       input's value so there's no race against multiple input events
       Mobile Safari fires during a Playwright fill(). Default state:
       hidden; revealed when the input has content. */
    display: none;
    align-items: center; justify-content: center;
    border-radius: 50%;
    font-family: inherit;
  }
  .omnibox-input:not(:placeholder-shown) ~ .omnibox-clear { display: inline-flex; }
  .omnibox-clear:hover { color: var(--text); background: var(--bg); }
  .omnibox-panel {
    position: absolute;
    top: calc(100% + 4px); left: 0; right: 0;
    background: var(--surface);
    border: 1px solid var(--border2);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.35);
    max-height: min(420px, 60dvh);
    overflow-y: auto;
    overscroll-behavior: contain;
    z-index: 2000;
    padding: 4px;
  }
  .omnibox-section { padding: 4px 0; }
  .omnibox-section + .omnibox-section {
    border-top: 1px solid var(--border);
    margin-top: 4px;
    padding-top: 8px;
  }
  .omnibox-section-title {
    font-size: 10px;
    font-weight: 700;
    color: var(--text3);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    padding: 4px 10px 6px;
    font-family: var(--font-mono);
  }
  .omnibox-row {
    width: 100%;
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 10px;
    border: none;
    background: none;
    text-align: left;
    cursor: pointer;
    border-radius: 5px;
    color: var(--text);
    font-family: inherit;
    transition: background 0.1s;
  }
  .omnibox-row:hover,
  .omnibox-row.active { background: var(--surface2); }
  .omnibox-row-icon {
    font-size: 14px;
    width: 22px;
    flex-shrink: 0;
    text-align: center;
  }
  .omnibox-row-text { flex: 1; min-width: 0; }
  .omnibox-row-primary {
    font-size: 13px;
    color: var(--text);
    font-weight: 500;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .omnibox-row-secondary {
    font-size: 11px;
    color: var(--text3);
    font-family: var(--font-mono);
    margin-top: 1px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .omnibox-empty {
    padding: 16px 12px;
    font-size: 12px;
    color: var(--text3);
    text-align: center;
    font-family: var(--font-mono);
  }

  /* Create (+) control beside the omnibox — opens the Add/Import menu that
     replaced the old floating FABs. Reuses .icon-btn (button) and .recent-menu
     (dropdown); the menu is left-anchored since this control sits at the left
     of the toolbar (recent-menu defaults to right:0). */
  .create-wrap { position: relative; flex-shrink: 0; }
  .create-btn[aria-expanded="true"] { color: var(--accent); border-color: var(--accent2); background: var(--accent-dim); }
  /* The Create button sits at the right end of the toolbar, so the menu opens
     down-and-left (right:0, inherited from .recent-menu) to stay on screen. */
  .create-menu { min-width: 200px; }

  .search-input {
    width: 100%;
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 7px 10px 7px 32px;
    font-size: 13px;
    color: var(--text);
    font-family: var(--font-main);
    outline: none;
    transition: border-color 0.15s;
  }
  .search-input:focus { border-color: var(--accent); }
  .search-input::placeholder { color: var(--text3); }

  .filter-tabs {
    display: flex;
    gap: 4px;
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 3px;
  }
  .filter-tab {
    padding: 5px 12px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    color: var(--text3);
    border: none;
    background: none;
    font-family: var(--font-main);
    transition: all 0.15s;
  }
  .filter-tab.active {
    background: var(--accent-dim2);
    color: var(--accent);
    border: 1px solid rgba(240, 75, 36,0.3);
  }
  .filter-tab:hover:not(.active) { color: var(--text2); }

  /* Metro picker */
  .metro-select-wrap {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 6px;
    height: 32px;
    padding: 0 10px;
    font-size: 12px;
    color: var(--text2);
    flex-shrink: 0;
  }
  .metro-select-wrap select {
    background: none;
    border: none;
    color: var(--text);
    font-size: 12px;
    font-family: var(--font-main);
    font-weight: 500;
    cursor: pointer;
    outline: none;
    padding: 0;
    max-width: 160px;
  }
  /* (The market picker moved to the toolbar — the .header-metro instance
     styles were removed with it. Narrow-phone header spacing kept below.) */
  @media (max-width: 480px) {
    .header { gap: 6px; padding: env(safe-area-inset-top, 0px) 8px 0; }
    .header-right { gap: 6px; }
  }
  .metro-select-wrap select option { background: var(--surface2); }

  /* Deal pipeline filter row */
  .deal-filter-row {
    display: flex;
    align-items: center;
    gap: 4px;
    padding: 0 4px;
    flex-wrap: nowrap;
  }
  .deal-filter-label {
    font-size: 10px;
    font-weight: 700;
    font-family: var(--font-mono);
    text-transform: uppercase;
    letter-spacing: .07em;
    color: var(--text3);
    white-space: nowrap;
    padding: 0 4px;
  }
  .deal-filter-tab {
    display: inline-flex;
    align-items: center;
    gap: 4px;
    padding: 4px 10px;
    border-radius: 4px;
    font-size: 11px;
    font-weight: 500;
    cursor: pointer;
    color: var(--text3);
    border: 1px solid transparent;
    background: none;
    font-family: var(--font-main);
    transition: all 0.15s;
    white-space: nowrap;
  }
  .deal-filter-tab.active { background: rgba(255,122,89,0.12); color: #ff7a59; border-color: rgba(255,122,89,0.3); }
  .deal-filter-tab.active-won { background: rgba(34,197,94,0.12); color: #22c55e; border-color: rgba(34,197,94,0.3); }
  .deal-filter-tab.active-lost { background: rgba(156,163,175,0.12); color: #9ca3af; border-color: rgba(156,163,175,0.3); }
  .deal-filter-tab.active-none { background: rgba(255,255,255,0.06); color: var(--text2); border-color: var(--border); }
  .deal-filter-tab:hover:not(.active):not(.active-won):not(.active-lost):not(.active-none) { color: var(--text2); }

  /* TABLE */
  /* overflow-x:clip lets the table extend wider than the viewport without
     creating a scroll container — critical so position:sticky on thead
     is relative to the viewport (not trapped inside an overflow ancestor) */
  .table-scroll-outer {
    overflow-x: clip;
  }
  .table-wrap {
    padding: 0 24px 24px;
    min-width: 948px; /* 900px table + 2×24px padding */
  }
  table {
    width: 100%;
    border-collapse: collapse;
    margin-top: 16px;
    min-width: 900px;
  }
  thead th {
    text-align: left;
    font-size: 11px;
    font-weight: 700;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text2);
    padding: 9px 12px;
    border-bottom: 2px solid var(--border2);
    font-family: var(--font-mono);
    white-space: nowrap;
    background: var(--surface);
    position: sticky;
    top: 56px; /* below the fixed nav bar */
    z-index: 5;
  }
  tbody tr {
    border-bottom: 1px solid var(--border);
    cursor: pointer;
    transition: background 0.1s;
    /* Virtual scrolling, browser-native. content-visibility:auto lets the
       browser skip layout + paint for off-screen rows, so a 4000-row
       tbody behaves like a ~30-row one. contain-intrinsic-size gives
       the browser a height hint so the scrollbar stays accurate while
       the off-screen rows are uncomputed. No-op on browsers that don't
       support it (Safari < 18, Firefox < 124) — they render normally. */
    content-visibility: auto;
    contain-intrinsic-size: 0 50px;
  }
  tbody tr:nth-child(even) { background: rgba(255,255,255,0.02); }
  body.light-mode tbody tr:nth-child(even) { background: rgba(0,0,0,0.025); }
  tbody tr:hover { background: var(--surface2) !important; }
  tbody td {
    padding: 13px 12px;
    font-size: 13px;
    color: var(--text2);
    vertical-align: middle;
    max-width: 200px;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }
  tbody td.address-cell {
    color: var(--text);
    font-weight: 600;
    max-width: 220px;
  }
  thead th.sortable-col {
    cursor: pointer;
    user-select: none;
  }
  thead th.sortable-col:hover { color: var(--accent); }
  thead th.sortable-col.sort-active { color: var(--accent); }
  .sort-ind { font-size: 10px; opacity: 0.5; margin-left: 2px; }
  thead th.sort-active .sort-ind { opacity: 1; }
  .badge {
    display: inline-flex;
    align-items: center;
    gap: 5px;
    padding: 3px 8px;
    border-radius: 4px;
    font-size: 11px;
    font-weight: 600;
    font-family: var(--font-mono);
    white-space: nowrap;
  }
  .badge-planned { background: rgba(96,165,250,0.1); color: var(--blue); border: 1px solid rgba(96,165,250,0.2); }
  .badge-construction { background: rgba(251,191,36,0.1); color: var(--amber); border: 1px solid rgba(251,191,36,0.2); }
  .badge-conversion { background: rgba(167,139,250,0.1); color: var(--purple); border: 1px solid rgba(167,139,250,0.2); }
  .badge-existing { background: rgba(52,211,153,0.1); color: #34d399; border: 1px solid rgba(52,211,153,0.25); }
  .badge-proposed { background: rgba(251,146,60,0.1); color: #fb923c; border: 1px solid rgba(251,146,60,0.25); }
  .badge-ib { background: var(--accent-dim2); color: var(--accent); border: 1px solid rgba(240, 75, 36,0.25); }
  .badge-neutral { background: var(--surface2); color: var(--text3); border: 1px solid var(--border); }
  .badge-yes { background: var(--accent-dim); color: var(--accent); border: 1px solid rgba(240, 75, 36,0.2); }
  .badge-no { background: rgba(248,113,113,0.08); color: var(--red); border: 1px solid rgba(248,113,113,0.2); }

  /* Card freshness pills (scout 🔭 / permit 🔧). Color-coded by age:
     fresh→green, mid→amber, stale→orange. Dark by default; light mode gets
     tinted fills + darker text so they don't read as black boxes on the white
     cards (matches the .badge-* family above). */
  .feed-badge {
    display: inline-flex; align-items: center; gap: 2px;
    padding: 1px 5px; border-radius: 8px;
    font-size: 9px; font-weight: 700; white-space: nowrap;
    font-family: var(--font-mono); vertical-align: middle;
    margin-left: 4px; border: 1px solid transparent;
  }
  .feed-badge--fresh { background: #052e16; color: #4ade80; border-color: rgba(74,222,128,0.35); }
  .feed-badge--mid   { background: #422006; color: #fbbf24; border-color: rgba(251,191,36,0.35); }
  .feed-badge--stale { background: #431407; color: #fb923c; border-color: rgba(251,146,60,0.35); }
  body.light-mode .feed-badge--fresh { background: #dcfce7; color: #15803d; border-color: #86efac; }
  body.light-mode .feed-badge--mid   { background: #fef3c7; color: #b45309; border-color: #fcd34d; }
  body.light-mode .feed-badge--stale { background: #ffedd5; color: #c2410c; border-color: #fdba74; }

  .empty-state {
    text-align: center;
    padding: 64px 24px;
    color: var(--text3);
  }
  .empty-state h3 { font-size: 16px; margin-bottom: 8px; color: var(--text2); }
  .empty-state p { font-size: 13px; }
  /* Primary "get unstuck" action on a zero-result view (usually a filter is
     hiding everything). Accent + white reads as primary in both themes; 44px
     min keeps it a real touch target per ui-standards. */
  .empty-state .es-action {
    margin-top: 16px; min-height: 44px; padding: 10px 20px;
    border-radius: 22px; cursor: pointer;
    background: var(--accent); border: 1px solid var(--accent); color: #fff;
    font-size: 14px; font-weight: 600; font-family: inherit;
  }
  .empty-state .es-action:hover { filter: brightness(1.06); }

  /* LOADING — top-of-page progress bar that's wired to setSyncing()
     in js/app.js. Whenever any background request starts (loadProjects,
     save, delete, address verify, etc.) this bar pulses across the top
     so slow-connection users have a clear "we're working" cue. The
     header sync dot is the desktop equivalent; on mobile it's too
     small, hence this universal bar. */
  .loading-bar {
    position: fixed;
    top: 0; left: 0;
    height: 2px;
    background: var(--accent);
    width: 0%;
    transition: width 0.3s;
    z-index: 999;
  }
  .loading-bar.active { animation: loading 1.2s ease infinite; }
  @media (max-width: 768px) {
    /* Slightly thicker on phones where the bar is the primary signal. */
    .loading-bar { height: 3px; }
  }
  @keyframes loading {
    0% { width: 0%; left: 0; }
    50% { width: 70%; left: 0; }
    100% { width: 0%; left: 100%; }
  }

  /* TOAST */
  .toast {
    position: fixed;
    bottom: 24px;
    right: 24px;
    background: var(--surface2);
    border: 1px solid var(--border2);
    border-radius: 8px;
    padding: 12px 16px;
    font-size: 13px;
    color: var(--text);
    z-index: 9999;
    display: flex;
    align-items: center;
    gap: 10px;
    transform: translateY(80px);
    opacity: 0;
    transition: all 0.25s;
    max-width: 320px;
  }
  .toast.show { transform: translateY(0); opacity: 1; }
  .toast.success { border-color: rgba(74,222,128,0.4); } /* semantic success — stays green */
  .toast.error { border-color: rgba(248,113,113,0.4); }
  .toast-icon { font-size: 16px; }

  /* MODAL */
  .modal-overlay {
    position: fixed;
    inset: 0;
    background: rgba(0,0,0,0.7);
    z-index: 2000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 20px;
    opacity: 0;
    pointer-events: none;
    transition: opacity 0.2s;
    backdrop-filter: blur(4px);
  }
  .modal-overlay.open { opacity: 1; pointer-events: all; }
  .modal {
    background: var(--surface);
    border: 1px solid var(--border2);
    border-radius: 12px;
    width: 100%;
    max-width: 680px;
    max-height: 90vh;
    overflow-y: auto;
    /* Never scroll the modal sideways — it's a form + brief, all meant to wrap
       or fit. Without this, overflow-y:auto coerces overflow-x to auto, so any
       rogue-width child (a chip row, the permit chart, a photo/map panel) let
       the whole modal scroll left/right with an empty gutter on mobile. */
    overflow-x: hidden;
    overscroll-behavior: contain;
    transform: translateY(16px);
    transition: transform 0.2s;
  }
  .modal-overlay.open .modal { transform: translateY(0); }
  /* Desktop: float the detail panel instead of dimming the whole screen. It docks to
     the right and the overlay is click-through, so the map (and the campus you're
     inspecting) stays visible AND interactive while a building is open. Mobile keeps
     the bottom sheet (the ≤768px block far below). The overlay itself is click-through,
     but a document-level handler closes the panel on any click outside it (except one
     that opened/switched it); ✕ / the Scout breadcrumb / Esc also close. */
  @media (min-width: 769px) {
    /* Only the building-detail view floats (.modal-floating) — the add/edit form
       keeps the normal centered, dismissable modal. */
    .modal-overlay.modal-floating, .modal-overlay.modal-floating.open {
      background: transparent;
      backdrop-filter: none;
      justify-content: flex-end;
      padding: 20px 18px;
      pointer-events: none;
    }
    .modal-overlay.modal-floating .modal { pointer-events: auto; box-shadow: 0 14px 48px rgba(0,0,0,0.45); }
    body.light-mode .modal-overlay.modal-floating .modal { box-shadow: 0 14px 48px rgba(15,18,30,0.22); }
  }
  .modal-header {
    padding: 13px 22px 11px;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 12px;
    position: sticky;
    top: 0;
    background: var(--surface);
    /* Above the photo/map frames' controls (z-index:1001) + Leaflet panes — else,
       as the body scrolls, the map frame slides up *behind* this sticky header but
       paints *over* it (ghost "address" label + Sat/Map buttons bleeding through). */
    z-index: 1101;
    border-radius: 12px 12px 0 0;
  }
  .modal-address {
    font-size: 16px;
    font-weight: 700;
    color: var(--text);
    line-height: 1.2;
  }
  /* Street address beneath the property-name title (see #modalSubAddr). Muted and
     lighter than the title so the name reads as the primary identity. */
  .modal-subaddr {
    font-size: 12.5px;
    color: var(--text2);
    line-height: 1.25;
    margin-top: 2px;
    word-break: break-word;
  }
  .modal-close {
    background: none;
    border: none;
    color: var(--text3);
    cursor: pointer;
    font-size: 18px;
    padding: 2px;
    line-height: 1;
    flex-shrink: 0;
    transition: color 0.15s;
  }
  .modal-close:hover { color: var(--text); }
  /* Owner avatar (header top-right) — the clickable owner logo (→ portfolio) with
     a portfolio-count badge. The single owner-logo home now that the inline
     subtitle logo and the duplicate "Owner / operator" gate row are gone. */
  .modal-owner-avatar {
    position: relative; flex-shrink: 0; align-self: center;
    width: 38px; height: 38px; padding: 0;
    border: 1px solid var(--border2); border-radius: 9px;
    background: var(--surface2); cursor: pointer; overflow: visible;
    display: inline-flex; align-items: center; justify-content: center;
    transition: border-color 0.15s, box-shadow 0.15s;
  }
  .modal-owner-avatar:hover { border-color: var(--accent); box-shadow: 0 0 0 3px var(--accent-dim2); }
  .modal-owner-avatar img { width: 38px; height: 38px; border-radius: 8px; object-fit: contain; background: #f8f9fa; }
  .moa-badge {
    position: absolute; top: -6px; right: -6px;
    min-width: 17px; height: 17px; padding: 0 4px; box-sizing: border-box;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 9px; background: var(--accent); color: #fff;
    font-size: 10px; font-weight: 700; font-family: var(--font-mono);
    border: 2px solid var(--surface); line-height: 1;
  }
  /* Owner name on the subtitle line — clickable → portfolio, same as the avatar.
     Persistent dotted underline so the tap target reads on touch (not hover-only). */
  .modal-owner-link {
    cursor: pointer; text-decoration: underline dotted; text-underline-offset: 2px;
    text-decoration-color: var(--border2);
  }
  .modal-owner-link:hover { color: var(--accent); text-decoration-color: var(--accent); }
  /* Street address → tappable (opens Maps). Same dotted-underline affordance. */
  .modal-subaddr-link {
    cursor: pointer; text-decoration: underline dotted; text-underline-offset: 2px;
    text-decoration-color: var(--border2);
  }
  .modal-subaddr-link:hover { color: var(--accent); text-decoration-color: var(--accent); }
  /* Chips on their own row, then the primary actions on their OWN full-width row
     below — left-aligned with the title/chips. (Earlier this pushed the actions
     right with margin-left:auto on the same row; once #158 widened the bar with
     the labelled "Create Deal", that bar wrapped to a second line and the auto
     margin stranded it to the RIGHT. Giving it its own left-aligned row is the
     predictable fix at every width above the phone breakpoint.) */
  .modal-headrow { display: flex; align-items: center; flex-wrap: wrap; gap: 8px 12px; margin-top: 9px; }
  .modal-headrow .detail-chips { margin: 0; }
  .modal-headrow .modal-actionbar { margin-top: 8px; margin-left: 0; flex: 1 0 100%; width: 100%; }
  .modal-tabs {
    display: flex;
    gap: 0;
    border-bottom: 1px solid var(--border);
    padding: 0 12px;
    flex-shrink: 0;
  }
  /* Equal-width tab bar — all tabs fit on one row (no horizontal scroll) on
     both mobile and desktop. Short labels + icon keep each readable. */
  .modal-tab {
    flex: 1 1 0;
    min-width: 0;
    text-align: center;
    white-space: nowrap;
    padding: 12px 6px;
    font-size: 13px;
    font-weight: 500;
    cursor: pointer;
    color: var(--text3);
    border-bottom: 2px solid transparent;
    margin-bottom: -1px;
    border-left: none; border-right: none; border-top: none;
    background: none;
    font-family: var(--font-main);
    transition: all 0.15s;
  }
  .modal-tab.active { color: var(--accent); border-bottom-color: var(--accent); }
  .modal-tab:hover:not(.active) { color: var(--text2); }
  .modal-body { padding: 14px 22px 22px; }

  /* Brief (news + permits) collapse. Desktop: always shown, no toggle. Mobile:
     collapsed behind a compact summary chip to keep the modal header short. */
  .modal-brief-toggle {
    display: inline-flex; align-items: center; gap: 6px;
    margin-top: 7px; padding: 5px 11px; border-radius: 8px;
    border: 1px solid var(--border2); background: var(--surface2);
    color: var(--text2); font-size: 11px; font-weight: 600;
    font-family: var(--font-mono); cursor: pointer;
  }
  .modal-brief-toggle:hover { color: var(--text); }
  @media (min-width: 769px) {
    .modal-brief-toggle { display: none !important; }
    .modal-brief-body { display: block !important; }
  }
  @media (max-width: 768px) {
    .modal-brief-body { display: none; }
    .modal-brief-body.open { display: block; }
  }

  .tab-panel { display: none; }
  .tab-panel.active { display: block; }

  /* FORM */
  .form-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 14px;
  }
  .form-group { display: flex; flex-direction: column; gap: 5px; }
  .form-group.full { grid-column: 1 / -1; }
  .form-label {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.08em;
    text-transform: uppercase;
    color: var(--text3);
    font-family: var(--font-mono);
  }
  .form-input, .form-select, .form-textarea {
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 8px 10px;
    font-size: 13px;
    color: var(--text);
    font-family: var(--font-main);
    outline: none;
    transition: border-color 0.15s;
    width: 100%;
  }
  .form-input:focus, .form-select:focus, .form-textarea:focus { border-color: var(--accent); }
  .form-select { cursor: pointer; }
  .form-select option { background: var(--surface2); }
  .form-textarea { resize: vertical; min-height: 80px; }

  /* Segmented picker — replaces native <select> for short fixed enums (Status,
     Building Class). Pills wrap to multiple rows on narrow screens. The active
     Status pill adopts that status's map-legend colour, so the form and the map
     pins stay visually in sync. */
  /* Equal-width segmented control — all options fit one row (no wrap), each an
     icon over a short label. Used for Status (5) and Building Class (3). */
  .seg-control { display: flex; flex-wrap: nowrap; gap: 4px; }
  .seg-btn {
    position: relative;
    flex: 1 1 0; min-width: 0;
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 3px;
    padding: 8px 4px; border-radius: 8px;
    border: 1px solid var(--border); background: var(--surface2);
    color: var(--text2); font-family: var(--font-main); font-size: 12px; font-weight: 500;
    cursor: pointer; transition: all 0.12s; text-align: center;
  }
  .seg-ico { font-size: 16px; line-height: 1; }
  .seg-label { font-size: 10.5px; line-height: 1.15; }
  .seg-btn:hover { color: var(--text); border-color: var(--border2); }
  .seg-btn.active { font-weight: 700; }
  /* Selected marker — makes the pick-one choice unmistakable (single-select),
     without the wrong "multi-select" signal a checkbox would send. Inherits the
     active segment's colour. */
  .seg-btn.active::after {
    content: '✓';
    position: absolute; top: 3px; right: 5px;
    font-size: 10px; font-weight: 700; line-height: 1;
  }
  .seg-dot { width: 8px; height: 8px; border-radius: 50%; flex-shrink: 0; }
  /* Active = the status's map colour (mirrors .dot-* in the map legend). */
  .seg-existing.active     { color: #34d399;        border-color: #34d399;        background: rgba(52,211,153,0.12); }
  .seg-proposed.active     { color: #fb923c;        border-color: #fb923c;        background: rgba(251,146,60,0.12); }
  .seg-planned.active      { color: var(--blue);    border-color: var(--blue);    background: rgba(96,165,250,0.12); }
  .seg-construction.active { color: var(--amber);   border-color: var(--amber);   background: rgba(251,191,36,0.12); }
  .seg-conversion.active   { color: var(--purple);  border-color: var(--purple);  background: rgba(167,139,250,0.12); }
  /* Generic accent active (e.g. Building Class A/B/C). */
  .seg-accent.active       { color: var(--accent);  border-color: var(--accent);  background: var(--accent-dim2); }
  /* Phone: grow the pills to a comfortable touch target (~44px). */
  @media (max-width: 768px) {
    .seg-btn { min-height: 48px; padding: 8px 3px; }
  }

  .modal-footer {
    padding: 16px 24px;
    border-top: 1px solid var(--border);
    display: flex;
    justify-content: flex-end;
    gap: 10px;
    position: sticky;
    bottom: 0;
    background: var(--surface);
    border-radius: 0 0 12px 12px;
    flex-wrap: wrap;
    transition: background 0.2s;
  }
  /* Footer "More" menu. Desktop: the wrap is display:contents so the secondary
     actions stay inline in the footer (alongside Cancel/Save), and the More
     button is hidden. Mobile rules (in the max-width:768px block) turn it into
     a dropdown so the footer collapses to one row. */
  .footer-more-wrap { display: contents; }
  .footer-more-btn { display: none; }
  .footer-actions { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }

  /* ── BUILDING DETAIL — read view ─────────────────────────────────────────
     A scan-and-decide read screen: who controls it, what the signals say,
     who to call. Edit is one tap away via the action bar. */
  .detail-chips { display: flex; flex-wrap: wrap; gap: 6px; align-items: center; margin-top: 8px; }
  .dchip {
    display: inline-flex; align-items: center; gap: 6px;
    border-radius: 999px; padding: 3px 10px;
    font-size: 12.5px; font-weight: 600; line-height: 1.2; white-space: nowrap;
  }
  .dchip-neutral { background: var(--surface2); color: var(--text); border: 1px solid var(--border); }
  .dchip-sf-est { color: var(--text2); font-weight: 500; font-size: 11.5px; }
  .dchip-dot { width: 7px; height: 7px; border-radius: 999px; flex-shrink: 0; }

  /* Overview sections */
  .ov-block { margin-bottom: 22px; }
  .ov-block:last-child { margin-bottom: 0; }
  .ov-section-label {
    font-size: 11px; font-weight: 700; letter-spacing: 0.06em;
    color: var(--text3); text-transform: uppercase; margin: 0 0 8px;
  }
  .ov-row {
    display: flex; align-items: center; gap: 12px;
    padding: 12px 0; border-bottom: 1px solid var(--border); min-height: 48px;
  }
  .ov-row.last { border-bottom: none; }
  .ov-row.clickable { cursor: pointer; border-radius: 8px; transition: background 0.12s; }
  .ov-row.clickable:hover { background: var(--surface2); }
  .ov-row.clickable:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .ov-ico {
    width: 34px; height: 34px; border-radius: 9px; background: var(--surface2);
    display: flex; align-items: center; justify-content: center;
    color: var(--text2); flex-shrink: 0;
  }
  .ov-ico-logo { padding: 0; overflow: hidden; }
  .ov-ico-logo img { width: 34px; height: 34px; object-fit: cover; }
  .ov-rowmain { flex: 1; min-width: 0; }
  .ov-role { font-size: 12px; color: var(--text3); font-weight: 600; }
  .ov-name {
    font-size: 15px; color: var(--text); font-weight: 600;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .ov-name.empty { color: var(--text3); font-weight: 500; }
  .ov-add {
    display: inline-flex; align-items: center; gap: 4px;
    background: transparent; border: 1px solid var(--border); color: var(--text2);
    border-radius: 8px; padding: 6px 10px; font-size: 13px; font-weight: 600;
    cursor: pointer; flex-shrink: 0; min-height: 36px;
  }
  .ov-add:hover { border-color: var(--border2); color: var(--text); }
  .ov-chev { color: var(--text3); flex-shrink: 0; }
  .ov-signals-empty { font-size: 13px; color: var(--text3); padding: 4px 0; }
  .ov-sig {
    display: flex; align-items: center; justify-content: space-between; gap: 12px;
    padding: 11px 0; border-bottom: 1px solid var(--border); min-height: 44px;
  }
  .ov-sig:last-child { border-bottom: none; }
  .ov-sig-left { font-size: 15px; font-weight: 600; color: var(--text); }
  .ov-sig-right {
    display: inline-flex; align-items: center; gap: 6px;
    font-size: 14px; font-weight: 600; text-align: right;
  }
  /* Active signal row (links out to the source record, e.g. code enforcement). */
  .ov-sig-link { text-decoration: none; cursor: pointer; border-radius: 8px; transition: background 0.12s; }
  .ov-sig-link:hover { background: var(--surface2); }
  .ov-sig-link:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .ov-extlink { flex-shrink: 0; opacity: 0.75; }
  @media (prefers-reduced-motion: reduce) { .ov-sig-link { transition: none; } }

  /* Read-mode action bar — full-width row (wraps below the More menu). */
  .read-actions { display: flex; gap: 8px; flex: 1 1 100%; }
  .read-actions .btn-edit,
  .read-actions .btn-brief {
    flex: 1; display: inline-flex; align-items: center; justify-content: center; gap: 7px;
    border-radius: 12px; padding: 11px 0; font-size: 15px; font-weight: 700;
    cursor: pointer; min-height: 44px;
  }
  .read-actions .btn-edit { background: var(--surface); border: 1.5px solid var(--text); color: var(--text); }
  .read-actions .btn-edit:hover { background: var(--surface2); }
  .read-actions .btn-brief { background: var(--accent); border: none; color: #fff; }
  .read-actions .btn-brief:hover { background: var(--accent2); }
  .read-actions .btn-scout {
    width: 50px; display: inline-flex; align-items: center; justify-content: center;
    background: var(--surface); border: 1.5px solid var(--border); color: var(--text2);
    border-radius: 12px; cursor: pointer; min-height: 44px; flex-shrink: 0;
  }
  .read-actions .btn-scout:hover { color: var(--text); border-color: var(--border2); }
  .read-actions button:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

  /* Edit-mode header hint */
  .edit-mode-hint {
    display: flex; align-items: center; gap: 8px; margin-bottom: 16px;
    font-size: 16px; font-weight: 700; color: var(--text);
  }
  .edit-mode-hint svg { color: var(--accent); flex-shrink: 0; }

  /* PASS 3 polish ───────────────────────────────────────────────────────── */
  /* One clean sans hierarchy: drop monospace everywhere inside the detail
     modal so it reads as a finished product, not a console. Scoped to .modal
     (the building-detail screen) — the table/cards/map keep their mono. */
  .modal { --font-mono: var(--font-main); }

  /* Status segmented control: let the five options wrap to comfortable,
     content-sized tiles instead of squeezing onto one row and truncating
     ("Under Constr.", "Conversion"). Building Class (A/B/C) still fits one row. */
  .seg-control[data-seg="f-status"] { flex-wrap: wrap; }
  .seg-control[data-seg="f-status"] .seg-btn { flex: 1 1 28%; min-width: 96px; }
  .seg-btn .seg-label { white-space: normal; overflow-wrap: anywhere; }

  /* Keyboard focus on the new detail controls. */
  .modal-tab:focus-visible,
  .ov-add:focus-visible,
  .dchip:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }

  /* Respect reduced-motion across the detail-screen transitions. */
  @media (prefers-reduced-motion: reduce) {
    .modal-tab, .ov-row.clickable, .ov-add,
    .read-actions .btn-edit, .read-actions .btn-brief, .read-actions .btn-scout { transition: none; }
  }

  /* ── PERMITS DRILL-IN MODAL ──────────────────────────────────────────────
     Stacks over the detail modal; sans hierarchy (override --font-mono). */
  .permits-overlay {
    position: fixed; inset: 0; z-index: 3000;
    display: flex; align-items: flex-end; justify-content: center;
    background: rgba(0,0,0,0.55); backdrop-filter: blur(4px);
    opacity: 0; pointer-events: none; transition: opacity 0.2s;
  }
  .permits-overlay.open { opacity: 1; pointer-events: all; }
  .permits-modal {
    --font-mono: var(--font-main);
    width: 100%; max-width: 560px; max-height: 88vh;
    display: flex; flex-direction: column;
    background: var(--surface); border: 1px solid var(--border2);
    border-radius: 16px 16px 0 0;
    transform: translateY(16px); transition: transform 0.2s;
    overflow: hidden;
  }
  .permits-overlay.open .permits-modal { transform: translateY(0); }
  .permits-modal-header {
    display: flex; align-items: flex-start; justify-content: space-between; gap: 12px;
    padding: 18px 20px 12px; border-bottom: 1px solid var(--border); flex-shrink: 0;
  }
  .permits-modal-title { font-size: 17px; font-weight: 700; color: var(--text); }
  .permits-modal-sub { font-size: 13px; color: var(--text3); margin-top: 2px; }
  .permits-filters {
    display: flex; gap: 6px; flex-wrap: wrap;
    padding: 12px 20px; border-bottom: 1px solid var(--border); flex-shrink: 0;
  }
  .permit-filter {
    background: var(--surface2); border: 1px solid var(--border); color: var(--text2);
    border-radius: 999px; padding: 6px 12px; font-size: 13px; font-weight: 600;
    cursor: pointer; min-height: 36px; white-space: nowrap;
  }
  .permit-filter.active { color: var(--text); border-color: var(--text2); }
  .permit-filter:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  .permit-filter-n { color: var(--text3); font-weight: 500; }
  .permits-list { overflow-y: auto; overscroll-behavior: contain; padding: 8px 20px 20px; }
  .permits-empty { color: var(--text3); font-size: 14px; padding: 24px 0; text-align: center; }
  .permit-row { padding: 14px 0; border-bottom: 1px solid var(--border); }
  .permit-row:last-child { border-bottom: none; }
  .permit-row-head { display: flex; align-items: center; justify-content: space-between; gap: 10px; margin-bottom: 6px; }
  .permit-cat {
    display: inline-flex; align-items: center; gap: 6px;
    border-radius: 999px; padding: 2px 9px; font-size: 12px; font-weight: 700; white-space: nowrap;
  }
  .permit-cat-dot { width: 7px; height: 7px; border-radius: 999px; flex-shrink: 0; }
  .permit-date { font-size: 12px; color: var(--text3); font-weight: 600; flex-shrink: 0; }
  .permit-kw { font-size: 15px; font-weight: 600; color: var(--text); line-height: 1.3; }
  .permit-desc { font-size: 13px; color: var(--text2); margin-top: 3px; line-height: 1.4; }
  .permit-vendor {
    display: flex; align-items: center; gap: 6px; margin-top: 7px;
    font-size: 14px; font-weight: 700; color: var(--purple);
  }
  .permit-vendor-ico { flex-shrink: 0; }
  .permit-vendor-soft { color: var(--text2); font-weight: 600; }
  .permit-vendor-none { color: var(--text3); font-weight: 500; font-style: italic; }
  .permit-meta { font-size: 12px; color: var(--text3); margin-top: 5px; }

  /* Tappable permit timeline in the header brief. */
  .permit-timeline-tap { cursor: pointer; border-radius: 6px; }
  .permit-timeline-tap:hover { background: var(--surface2); }

  @media (prefers-reduced-motion: reduce) {
    .permits-overlay, .permits-modal { transition: none; }
  }
  @media (min-width: 600px) {
    .permits-overlay { align-items: center; }
    .permits-modal { border-radius: 16px; max-height: 84vh; }
  }

  /* ── AI BRIEF / report overlay (in-app iframe) ──────────────────────────────
     Bar + iframe are absolutely positioned (not flex): an <iframe flex:1>
     does not stretch on iOS Safari, which left the report near-zero height. */
  .brief-overlay {
    position: fixed; inset: 0; z-index: 3500;
    display: none; background: var(--surface);
  }
  .brief-overlay.open { display: block; }
  .brief-overlay-bar {
    /* Respect the top safe-area inset (notch/Dynamic Island) so the ← Back
       button + title never ride up under the iOS status bar (clock/battery),
       same fix as the detail sheet. Bar grows by the inset; content stays a
       52px row centred below it. */
    position: absolute; top: 0; left: 0; right: 0;
    height: calc(52px + env(safe-area-inset-top, 0px));
    display: flex; align-items: center; gap: 12px;
    padding: env(safe-area-inset-top, 0px) calc(16px + env(safe-area-inset-right, 0px)) 0 calc(16px + env(safe-area-inset-left, 0px));
    border-bottom: 1px solid var(--border);
    background: var(--surface); box-sizing: border-box;
  }
  .brief-back {
    display: inline-flex; align-items: center; gap: 5px;
    background: var(--surface2); border: 1px solid var(--border); color: var(--text);
    border-radius: 8px; padding: 7px 12px; font-size: 14px; font-weight: 600;
    cursor: pointer; min-height: 40px;
  }
  .brief-back:hover { border-color: var(--border2); }
  .brief-overlay-title { font-size: 15px; font-weight: 700; color: var(--text); flex: 1; }
  .brief-overlay-bar .modal-close { font-size: 18px; }
  .brief-back:focus-visible,
  .brief-overlay-bar .modal-close:focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
  #briefFrame {
    /* iframe is a replaced element — top+bottom won't stretch it; an explicit
       height is required (this is what was leaving the report near-zero-height). */
    position: absolute; top: calc(52px + env(safe-area-inset-top, 0px)); left: 0; right: 0;
    width: 100%; height: calc(100% - 52px - env(safe-area-inset-top, 0px)); border: none; background: #f9fafb;
  }

  /* AUTO-FILL PANEL */
  .autofill-panel {
    background: var(--surface2);
    border: 1px solid var(--border2);
    border-radius: 8px;
    padding: 12px 16px;
    margin: 0 0 8px;
  }
  .autofill-header {
    font-size: 11px;
    font-weight: 700;
    color: var(--accent);
    text-transform: uppercase;
    letter-spacing: 0.08em;
    margin-bottom: 10px;
    display: flex;
    align-items: center;
    justify-content: space-between;
  }
  .autofill-row {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 6px 0;
    border-bottom: 1px solid var(--border);
    font-size: 12px;
  }
  .autofill-row:last-of-type { border-bottom: none; }
  .autofill-row input[type=checkbox] { accent-color: var(--accent); width: 14px; height: 14px; flex-shrink: 0; cursor: pointer; }
  .autofill-row label { display: flex; flex: 1; gap: 10px; align-items: center; cursor: pointer; }
  .autofill-key { color: var(--text3); width: 110px; flex-shrink: 0; font-family: var(--font-mono); font-size: 11px; }
  .autofill-val { color: var(--text); flex: 1; }
  .autofill-src { font-size: 10px; color: var(--text3); font-family: var(--font-mono); margin-left: auto; white-space: nowrap; }
  .autofill-actions { display: flex; gap: 8px; margin-top: 10px; justify-content: flex-end; }

  /* ── CONTACTS ──────────────────────────────────────────────── */
  .contact-card {
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 8px;
    padding: 12px 14px;
    display: flex;
    align-items: flex-start;
    gap: 12px;
  }
  .contact-avatar {
    width: 36px; height: 36px; border-radius: 50%;
    background: var(--accent-dim2);
    color: var(--accent);
    display: flex; align-items: center; justify-content: center;
    font-weight: 700; font-size: 14px; flex-shrink: 0;
  }
  .contact-info { flex: 1; min-width: 0; }
  .contact-name { font-weight: 600; font-size: 13px; color: var(--text); }
  .contact-title { font-size: 11px; color: var(--text3); margin-top: 1px; }
  .contact-links { display: flex; gap: 10px; margin-top: 6px; flex-wrap: wrap; }
  .contact-link {
    font-size: 11px; font-family: var(--font-mono);
    color: var(--accent); text-decoration: none; display: flex; align-items: center; gap: 4px;
  }
  .contact-link:hover { text-decoration: underline; }
  .contact-src {
    font-size: 9px; font-family: var(--font-mono);
    padding: 2px 6px; border-radius: 4px; border: 1px solid;
    white-space: nowrap; align-self: flex-start; font-weight: 600;
  }
  .contact-src-hs     { background:#fff4f1; color:#ff5c35; border-color:#ffd0c2; }
  .contact-src-apollo { background:#f0f0ff; color:#6b48ff; border-color:#c9c2ff; }
  .contact-src-manual { background:var(--surface2); color:var(--text3); border-color:var(--border); }
  .contact-candidate {
    background: var(--surface2); border: 1px solid var(--border);
    border-radius: 8px; padding: 10px 12px;
    display: flex; align-items: flex-start; gap: 10px;
  }
  .contact-candidate.selected { border-color: var(--accent); background: var(--accent-dim); }
  .contact-candidate input[type=checkbox] { accent-color: var(--accent); margin-top: 2px; flex-shrink: 0; cursor: pointer; }
  .contact-delete { background: none; border: none; color: var(--text3); cursor: pointer; padding: 2px 4px; font-size: 14px; border-radius: 4px; }
  .contact-delete:hover { color: var(--red); background: rgba(248,113,113,0.1); }
  .contact-info-line {
    font-size: 11px; color: var(--text3); font-family: var(--font-mono);
    margin-top: 3px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .contact-actions { display: flex; gap: 6px; margin-top: 8px; flex-wrap: wrap; }
  .contact-action-btn {
    display: inline-flex; align-items: center; gap: 4px;
    padding: 5px 10px; border-radius: 6px; font-size: 11px;
    font-family: var(--font-mono); text-decoration: none;
    border: 1px solid; white-space: nowrap; cursor: pointer;
    max-width: 240px; overflow: hidden; text-overflow: ellipsis;
    transition: opacity 0.15s, transform 0.1s;
    min-height: 28px; font-weight: 600; flex-shrink: 0;
  }
  .contact-action-btn:active { transform: scale(0.97); opacity: 0.85; }
  .contact-action-btn:hover  { opacity: 0.82; }
  .contact-action-phone { background:rgba(240, 75, 36,0.14); color:var(--accent); border-color:rgba(240, 75, 36,0.4); }
  .contact-action-email { background:rgba(96,165,250,0.14); color:var(--blue);   border-color:rgba(96,165,250,0.4); }
  .contact-action-li    { background:rgba(107,72,255,0.14); color:#6b48ff;       border-color:rgba(107,72,255,0.4); }
  .contact-action-hs    { background:rgba(255,92,53,0.12);  color:#ff5c35;       border-color:rgba(255,92,53,0.35); }
  .contact-actions { display:flex; flex-wrap:wrap; gap:6px; margin-top:8px; }

  /* ── OWNERSHIP SECTION ─────────────────────────────────── */
  .ownership-role-tag {
    display: inline-flex; align-items: center; justify-content: center;
    font-size: 10px; font-weight: 700; font-family: var(--font-mono);
    padding: 2px 7px; border-radius: 4px; flex-shrink: 0; letter-spacing: 0.04em; min-width: 28px;
  }
  .ownership-role-gp { background: rgba(240, 75, 36,0.15); color: var(--accent); border: 1px solid rgba(240, 75, 36,0.3); }
  .ownership-role-lp { background: rgba(96,165,250,0.12); color: var(--blue);   border: 1px solid rgba(96,165,250,0.3); }
  .lp-entry { display: flex; align-items: center; gap: 6px; margin-bottom: 6px; }
  .lp-remove-btn {
    background: none; border: none; cursor: pointer; color: var(--text3);
    font-size: 15px; padding: 0 4px; line-height: 1; flex-shrink: 0;
  }
  .lp-remove-btn:hover { color: var(--red); }
  .ownership-discover-btn {
    font-size: 11px; padding: 3px 9px;
    background: rgba(96,165,250,0.1); color: var(--blue);
    border: 1px solid rgba(96,165,250,0.3); border-radius: 5px; cursor: pointer;
  }
  .ownership-discover-btn:hover { background: rgba(96,165,250,0.2); }
  .lp-badge {
    font-size: 9px; font-family: var(--font-mono); font-weight: 700;
    padding: 1px 5px; border-radius: 3px; letter-spacing: 0.04em;
    background: rgba(96,165,250,0.12); color: var(--blue);
    border: 1px solid rgba(96,165,250,0.25); margin-left: 4px; vertical-align: middle;
  }

  /* ── KNOWN CONTACTS (company-level reuse) ──────────────── */
  .known-contacts-header {
    font-size: 11px; font-weight: 700; text-transform: uppercase;
    letter-spacing: 0.08em; font-family: var(--font-mono);
    color: var(--accent); margin-bottom: 6px;
    display: flex; align-items: center; gap: 6px;
  }
  .known-contact-card {
    background: var(--surface2); border: 1px solid var(--border);
    border-radius: 8px; padding: 10px 12px;
    display: flex; align-items: flex-start; gap: 10px;
    opacity: 0.6;
  }
  .known-contact-card.selected { border-color: var(--accent); background: var(--accent-dim2); opacity: 1; }
  .known-contact-card input[type=checkbox] { accent-color: var(--accent); margin-top: 2px; flex-shrink: 0; cursor: pointer; }
  .known-contact-src-label {
    font-size: 9px; font-family: var(--font-mono); font-weight: 700;
    padding: 2px 6px; border-radius: 4px; white-space: nowrap;
    background: var(--accent-dim2); color: var(--accent);
    border: 1px solid rgba(240, 75, 36,0.3); align-self: flex-start;
  }
  /* Sync phones button */
  .sync-phones-btn {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 11px; padding: 5px 10px; border-radius: 6px;
    background: rgba(96,165,250,0.1); color: var(--blue);
    border: 1px solid rgba(96,165,250,0.3); cursor: pointer;
    font-family: var(--font-mono); font-weight: 600; transition: opacity 0.15s;
  }
  .sync-phones-btn:hover { opacity: 0.8; }

  .modal-footer.confirming {
    background: rgba(248,113,113,0.06);
    border-top-color: rgba(248,113,113,0.25);
  }
  .delete-confirm-msg {
    flex: 1 1 100%;
    font-size: 12px;
    color: var(--red);
    display: flex;
    align-items: center;
    gap: 8px;
    padding: 2px 0 4px;
  }
  .delete-confirm-msg svg { flex-shrink: 0; }
  .btn-danger {
    background: rgba(248,113,113,0.12);
    color: var(--red);
    border-color: rgba(248,113,113,0.35);
    font-weight: 700;
  }
  .btn-danger:hover { background: rgba(248,113,113,0.22); border-color: var(--red); }

  /* RESEARCH LINKS */
  .research-section { margin-bottom: 20px; }
  .research-section-title {
    font-size: 11px;
    font-weight: 600;
    letter-spacing: 0.1em;
    text-transform: uppercase;
    color: var(--text3);
    margin-bottom: 10px;
    font-family: var(--font-mono);
  }
  .research-grid {
    display: grid;
    grid-template-columns: 1fr 1fr;
    gap: 8px;
  }
  .research-link {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding: 10px 12px;
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 6px;
    text-decoration: none;
    color: var(--text2);
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    transition: all 0.15s;
    gap: 8px;
  }
  .research-link:hover { border-color: var(--border2); color: var(--text); background: var(--rp-research-link-hover); }
  .research-link-icon { font-size: 14px; flex-shrink: 0; }
  .research-link-name { flex: 1; }
  .research-link-arrow { color: var(--text3); font-size: 11px; }

  /* ── Property detail: sticky header action bar (single-scroll read view) ── */
  .modal-actionbar { display: flex; align-items: center; gap: 6px; flex-wrap: wrap; margin-top: 9px; }
  .mab-btn {
    display: inline-flex; align-items: center; gap: 5px;
    padding: 6px 11px; border-radius: 8px; cursor: pointer;
    font-family: inherit; font-size: 12.5px; font-weight: 600;
    background: var(--surface2); border: 1px solid var(--border); color: var(--text);
    transition: border-color 0.12s ease, background 0.12s ease;
  }
  .mab-btn:hover { border-color: var(--border2); }
  .mab-ico { flex: 0 0 auto; display: block; }
  .mab-call { background: var(--accent); border-color: var(--accent); color: #fff; }
  .mab-call:hover { background: var(--accent2); border-color: var(--accent2); }
  /* Birdie is the FIRST (leftmost) action and the one we want a BD rep to reach
     for — so it's a FILLED, colourful button in the link/blue brand hue (distinct
     from Call's solid orange right beside it). Unlike the other actions, it keeps
     its label on phones (see the mobile block) so it stays an obvious CTA instead
     of disappearing into the icon row. */
  .mab-birdie { background: var(--link); border-color: var(--link); color: #fff; }
  .mab-birdie .mab-ico { color: #fff; }
  .mab-birdie:hover { border-color: var(--link); background: var(--link); filter: brightness(1.07); }
  .mab-more { padding: 8px 11px; font-size: 16px; line-height: 1; }
  /* ⋯ menu is a popover on every width (override the old footer inline behaviour).
     margin-left:auto pins it to the right so it reads as a distinct overflow
     affordance — and, when the bar wraps on narrow phones, it lands flush-right
     instead of orphaned alone on the left. */
  .modal-actionbar .footer-more-wrap { display: inline-flex !important; position: relative; margin-left: auto; }
  .modal-actionbar .footer-actions {
    position: absolute; top: calc(100% + 6px); right: 0; z-index: 30;
    display: none !important; flex-direction: column; align-items: stretch; gap: 6px;
    min-width: 210px; padding: 8px;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 10px; box-shadow: 0 12px 30px rgba(0,0,0,0.35);
  }
  .modal-actionbar .footer-more-wrap.open .footer-actions { display: flex !important; }
  .modal-actionbar .footer-actions .btn { justify-content: flex-start; }
  /* Scrim behind the mobile ⋯ bottom sheet (shown only on phones when open). */
  .footer-sheet-backdrop { display: none; }
  @keyframes footerSheetFade { from { opacity: 0; } to { opacity: 1; } }

  /* ── Decision gates — why it surfaced · owner · contact · next action ── */
  .modal-gates { display: flex; flex-direction: column; gap: 6px; margin-bottom: 12px; }
  .gate {
    display: flex; gap: 9px; align-items: baseline;
    padding: 8px 11px; border-radius: 9px;
    background: var(--surface2); border: 1px solid var(--border);
    border-left: 3px solid var(--border2);
  }
  .gate-ico { flex: 0 0 auto; font-size: 14px; line-height: 1.4; }
  .gate-l { flex: 0 0 92px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.05em; color: var(--text3); }
  .gate-v {
    flex: 1 1 auto; min-width: 0; font-size: 13px; font-weight: 600; color: var(--text); line-height: 1.4;
    /* Keep each gate to a glanceable ~2 lines — the long Signal rationale used to
       wall-of-text the top of the card. Tap the gate to drill into the section
       (or open the Brief for the full narrative). */
    display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; overflow: hidden;
  }
  .gate-sub { font-weight: 500; color: var(--text3); }
  /* Jump cue — present only on gates that open a section; Next action has none. */
  .gate-arrow { flex: 0 0 auto; align-self: center; margin-left: 4px; color: var(--text3); font-size: 14px; }
  .gate-link { cursor: pointer; transition: background 0.12s ease, border-color 0.12s ease; }
  .gate-link:hover { background: var(--surface); border-color: var(--border2); }
  .gate-link:hover .gate-arrow { color: var(--accent); }
  .gate.gate-on { border-left-color: #34d399; }
  .gate.gate-warn { border-left-color: #fbbf24; }
  .gate.gate-warn .gate-v { color: #fbbf24; }
  .gate.gate-off { border-left-color: var(--border2); }
  .gate.gate-off .gate-v { color: var(--text3); font-weight: 500; }
  .gate.gate-next { border-left-color: var(--accent); }

  /* ── Collapsible read sections (native <details>) ── */
  .modal-sec { border: 1px solid var(--border); border-radius: 10px; margin-bottom: 10px; background: var(--surface); overflow: hidden; }
  .modal-sec-h {
    list-style: none; cursor: pointer; padding: 12px 14px;
    font-size: 13px; font-weight: 700; color: var(--text);
    display: flex; align-items: center; gap: 8px;
  }
  .modal-sec-ico { flex: 0 0 auto; color: var(--text2); }
  .modal-sec-h::-webkit-details-marker { display: none; }
  .modal-sec-h::after { content: '⌄'; margin-left: auto; color: var(--text3); font-size: 14px; transition: transform 0.15s ease; }
  .modal-sec[open] > .modal-sec-h::after { transform: rotate(180deg); }
  .modal-sec-h:hover { background: var(--surface2); }
  .modal-sec-b { padding: 2px 14px 14px; }

  .checklist { display: flex; flex-direction: column; gap: 8px; margin-top: 4px; }
  .checklist-item {
    display: flex;
    align-items: center;
    gap: 10px;
    padding: 8px 12px;
    background: var(--surface2);
    border-radius: 6px;
    font-size: 12px;
    color: var(--text2);
  }
  .checklist-item input[type=checkbox] { accent-color: var(--accent); width: 14px; height: 14px; cursor: pointer; flex-shrink: 0; }
  .checklist-item label { cursor: pointer; line-height: 1.4; }

  /* ADD BUTTON */
  /* (The floating Add/Import FABs were removed — their actions now live in the
     Create menu + omnibox Actions section. .add-fab styles deleted with them.) */

  /* SYNC STATUS */
  .sync-status {
    font-size: 11px;
    color: var(--text3);
    font-family: var(--font-mono);
    display: flex;
    align-items: center;
    gap: 6px;
    cursor: pointer;          /* tappable → connection diagnostics panel */
    border-radius: 6px;
    padding: 2px 4px;
    margin: -2px -4px;        /* keep the dot's visual position unchanged */
  }
  .sync-status:hover { background: var(--surface2); }
  .sync-status:focus-visible { outline: 2px solid var(--link); outline-offset: 1px; }
  .sync-status.is-error .sync-label { color: var(--red); }
  /* Health colour by state: green = connected/idle (the resting "all good"
     signal), amber pulse = a request in flight, red = the last request
     failed or we're offline. Orange is reserved for activity, so a healthy
     connection never reads as orange and an error never reads as healthy. */
  .sync-dot {
    width: 6px; height: 6px;
    border-radius: 50%;
    background: var(--green);
    flex-shrink: 0;
  }
  .sync-dot.syncing { background: var(--accent); animation: pulse 1s infinite; }
  .sync-dot.error   { background: var(--red); }
  @keyframes pulse { 0%,100% { opacity: 1; } 50% { opacity: 0.3; } }

  /* Connection diagnostics popover — opened by tapping the status dot.
     Fixed-positioned (JS clamps it into the viewport) so it works the same
     in the desktop header and the tight mobile header. */
  .sync-diag {
    position: fixed;
    z-index: 1300;
    width: 270px; max-width: calc(100vw - 16px);
    background: var(--surface);
    border: 1px solid var(--border2);
    border-radius: 10px;
    box-shadow: 0 12px 32px rgba(0,0,0,0.35);
    padding: 12px 14px 14px;
    font-family: var(--font-main);
    color: var(--text);
  }
  .sync-diag-hd {
    display: flex; align-items: center; justify-content: space-between;
    font-size: 12px; font-weight: 700; letter-spacing: .04em;
    text-transform: uppercase; color: var(--text2); margin-bottom: 6px;
  }
  .sync-diag-x {
    background: none; border: none; color: var(--text3); cursor: pointer;
    font-size: 13px; line-height: 1; padding: 2px 4px; border-radius: 4px;
  }
  .sync-diag-x:hover { background: var(--surface2); color: var(--text); }
  .sync-diag-desc { font-size: 12px; color: var(--text2); line-height: 1.45; margin-bottom: 10px; }
  .sync-diag-rows { display: flex; flex-direction: column; gap: 5px; margin-bottom: 12px; }
  .sync-diag-row {
    display: flex; gap: 10px; align-items: baseline;
    font-size: 12px; font-family: var(--font-mono);
  }
  .sync-diag-row .k { color: var(--text3); flex: 0 0 70px; }
  .sync-diag-row .v { color: var(--text); flex: 1; min-width: 0; overflow-wrap: anywhere; }
  .sync-diag-actions { display: flex; gap: 8px; }
  .sync-diag-btn {
    flex: 1; padding: 7px 8px; border-radius: 7px;
    border: 1px solid var(--border2); background: var(--surface2);
    color: var(--text); font-family: inherit; font-size: 12px; font-weight: 600;
    cursor: pointer; white-space: nowrap;
  }
  .sync-diag-btn:hover { border-color: var(--text3); }
  .sync-diag-btn.primary { background: var(--accent); border-color: var(--accent); color: #fff; }
  .sync-diag-btn.primary:hover { background: var(--accent2); }

  /* ── MOBILE BUILDING CARDS ──────────────────────────────────── */
  .mobile-cards { display: none; padding: 8px 12px 80px; flex-direction: column; gap: 8px; }
  .mc {
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 12px;
    padding: 11px 14px;
    cursor: pointer;
    display: flex;
    gap: 12px;
    align-items: flex-start;
    min-height: 0;
    /* See `tbody tr` for the rationale on content-visibility virtual
       scrolling. Mobile cards swap in for the table on phones (the
       `.table-wrap{display:none}` / `.mobile-cards{display:flex}` flip
       at <=768px), so they get the same off-screen skip treatment. */
    content-visibility: auto;
    contain-intrinsic-size: 0 90px;
    -webkit-tap-highlight-color: transparent;
    transition: border-color 0.15s, background 0.1s;
  }
  .mc:active { background: var(--surface2); border-color: var(--border2); }
  .mc-left { flex: 1; min-width: 0; }
  /* Top-of-card row: address on the left, size pill (SF / units / rooms)
     in the upper-right corner per the property type. */
  .mc-head {
    display: flex;
    align-items: flex-start;
    justify-content: space-between;
    gap: 10px;
  }
  .mc-address { font-size: 15px; font-weight: 700; color: var(--text); line-height: 1.3; flex: 1; min-width: 0; }
  .mc-size {
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 600;
    color: var(--text2);
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 5px;
    padding: 2px 7px;
    white-space: nowrap;
    flex-shrink: 0;
    margin-top: 2px;
    line-height: 1.4;
  }
  .mc-name { font-size: 12px; color: var(--text3); margin-top: 3px; }
  .mc-owner { font-size: 13px; color: var(--text2); margin-top: 5px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .mc-meta { font-size: 11px; color: var(--text3); margin-top: 4px; font-family: var(--font-mono); }
  .mc-right { display: flex; flex-direction: column; align-items: flex-end; gap: 6px; flex-shrink: 0; padding-top: 2px; }
  .mc-hs { font-size: 10px; color: #ff7a59; font-family: var(--font-mono); font-weight: 600; }

  /* Expand chevron + collapsible extras. Collapsed by default so each
     card stays headline-only (address, property name, status badges);
     tapping the ▾ swings it open inline with owner + building meta. */
  .mc-extra { display: none; }
  .mc.mc-open .mc-extra { display: block; }
  .mc-expand {
    width: 28px; height: 22px;
    border-radius: 6px;
    border: 1px solid var(--border);
    background: var(--surface2);
    color: var(--text3);
    cursor: pointer;
    display: inline-flex; align-items: center; justify-content: center;
    transition: transform 0.18s, background 0.12s, border-color 0.12s;
    padding: 0;
    flex-shrink: 0;
  }
  .mc-expand svg { width: 14px; height: 14px; display: block; }
  .mc-expand:hover { color: var(--text); border-color: var(--border2); background: var(--bg); }
  .mc.mc-open .mc-expand { transform: rotate(180deg); color: var(--accent); border-color: var(--accent2); }

  /* Mobile: vertical flow + status badge → colored dot.
     Default card height was driven by the right column stacking 4–5
     pills vertically. Switch the card to column direction so the right
     column collapses into a single compact horizontal strip beneath
     the address, then shrink the status badge into a 12px legend dot
     (color encodes meaning; the full word lives in the title attr for
     tap-and-hold). */
  @media (max-width: 768px) {
    .mc { flex-direction: column; gap: 6px; padding: 10px 12px; align-items: stretch; }
    .mc-right {
      flex-direction: row;
      align-items: center;
      justify-content: flex-start;
      gap: 8px;
      padding-top: 4px;
      flex-wrap: wrap;
    }
    .mc-right .badge {
      width: 12px; height: 12px; min-width: 0;
      padding: 0; border-radius: 50%;
      font-size: 0; border-width: 0; line-height: 0;
      flex-shrink: 0;
    }
    .mc-right .badge-existing     { background: #34d399; }
    .mc-right .badge-proposed     { background: #fb923c; }
    .mc-right .badge-planned      { background: var(--blue); }
    .mc-right .badge-construction { background: var(--amber); }
    .mc-right .badge-conversion   { background: var(--purple); }
    .mc-right .badge-neutral      { background: var(--text3); opacity: 0.5; }
    .mc-right .badge-ib           { background: var(--accent); }
    .mc-right .badge-yes          { background: var(--accent); }
    .mc-right .badge-no           { background: var(--red); }
    /* Expand chevron sits flush right at the end of the strip. */
    .mc-expand { margin-left: auto; }
  }

  /* ── MOBILE RESPONSIVE ─────────────────────────────────────── */
  @media (max-width: 768px) {
    /* Header — slim. z-index lifted above the sticky toolbar (1000) so the
       ⋯ overflow dropdown — a descendant of this stacking context — can
       paint over the toolbar that sits directly underneath. Without this,
       Safari mobile silently opens the menu behind the toolbar and the
       button reads as broken. */
    .header { padding: env(safe-area-inset-top, 0px) 12px 0; height: calc(46px + env(safe-area-inset-top, 0px)); gap: 8px; z-index: 1100; }
    /* Mobile: show just the compact logo mark — the "IB Scout" wordmark + the
       in-header breadcrumb are hidden to save scarce horizontal space (the
       breadcrumb still appears below the toolbar). Keeps the page branded on
       phones, consistent with the sibling pages. */
    .header-brand { display: flex; margin-right: 8px; }
    .header-brand-text { display: none; }
    .header-logo { width: 28px; height: 28px; }
    /* Hide the sync label by default on mobile (no room), but reveal it
       while a request is actively in flight so the user gets textual
       confirmation alongside the top progress bar — "Saving…", "Deleting…",
       "Verifying address…", etc. The is-syncing class is toggled by
       setSyncing() in js/app.js. */
    /* Keep a compact label beside the dot so the health indicator is never a
       bare, unexplained dot on a phone. Idle reads "Connected" (muted); while
       a request is in flight it swaps to the accent-coloured narration
       ("Saving…", "Verifying address…", …). */
    .sync-status .sync-label {
      display: inline; font-size: 11px; color: var(--text3);
      max-width: 120px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
    }
    .sync-status.is-syncing .sync-label { color: var(--accent); }

    /* Stats bar — horizontal scroll, packed tight to recover vertical
       space on phones where every row before the first card hurts. */
    .status-bar {
      padding: 5px 12px;
      gap: 10px;
      overflow-x: auto;
      flex-wrap: nowrap;
      -webkit-overflow-scrolling: touch;
      scrollbar-width: none;
    }
    .status-bar::-webkit-scrollbar { display: none; }
    .divider { display: none; }
    .stat-num { font-size: 17px; min-width: 24px; }

    /* Toolbar — sticky on mobile so filters always visible. Tightened
       on this branch: market + view-toggle now share row 1 (both narrow),
       owner + near-me share the last row, so the toolbar drops from
       six stacked rows to four. */
    .toolbar {
      padding: 6px 12px 8px; gap: 6px; flex-wrap: wrap;
      /* z-index 1000 sits above Leaflet's tile/marker/control panes
         (200-800) so the omnibox dropdown — which lives inside this
         sticky toolbar — can paint over the map. Anything modal-level
         (.modal-overlay / .bd-rail-layer at z 3000) still wins. */
      position: sticky; top: 46px; z-index: 1000;
      background: var(--surface);
    }
    /* Filter tabs: constrained row with right-fade showing more content to swipe */
    .filter-tabs-wrap {
      position: relative;
      flex: 0 0 100%;        /* own full row */
      overflow: hidden;       /* clips the tabs at right edge */
      max-width: 100%;
    }
    .filter-tabs-wrap::after {
      content: '';
      position: absolute; top: 0; right: 0; bottom: 0; width: 40px;
      background: linear-gradient(to right, transparent, var(--surface));
      pointer-events: none; z-index: 2;
    }
    .filter-tabs {
      overflow-x: auto; -webkit-overflow-scrolling: touch;
      scrollbar-width: none; flex-wrap: nowrap;
      padding-right: 36px;  /* room for the fade */
      width: 100%;
    }
    .filter-tabs::-webkit-scrollbar { display: none; }
    .filter-tab { white-space: nowrap; padding: 6px 12px; font-size: 12px; flex-shrink: 0; }
    .deal-filter-row {
      overflow-x: auto; -webkit-overflow-scrolling: touch; scrollbar-width: none;
      flex: 0 0 100%; order: 1; max-width: 100%;
    }
    .deal-filter-row::-webkit-scrollbar { display: none; }
    /* Mobile toolbar layout (three rows, top-down):
       Row 1: omnibox alone (full width, max dropdown real estate)
       Row 2: [Table|Map]  ·  KPI strip (horizontal scroll)
       Row 3: Deal filter (horizontal scroll, own row)
       The status-bar takes a normal-flow flex item here — reset the
       desktop negative-margin / 100% basis trick. */
    /* Market scope takes its own light row at the very top of the toolbar
       (above the omnibox), left-aligned at its natural pill width. */
    .toolbar > .metro-select-wrap { order: -3; }
    .omnibox-wrap { order: -2; flex: 0 0 100%; max-width: 100%; }
    .view-toggle { order: -1; flex: 0 0 auto; }
    .view-toggle .view-btn { padding: 6px 12px; font-size: 12px; }
    .toolbar > .status-bar {
      order: 0;
      flex: 1 1 auto;
      min-width: 0;
      margin: 0;
      padding: 0;
      background: none;
      border-bottom: none;
    }

    /* Hide desktop table, show cards */
    .table-scroll-outer,
    .table-wrap { display: none !important; }
    .mobile-cards { display: flex; }

    /* Map view on mobile: hide the cards. Driven by body.view-map (set by
       switchView in app.js). Two paths:
       1. Fallback — display:none. Works everywhere, but the browser drops
          the cards' layout state, so the next Map → Table tap re-pays the
          layout cost of ~4 k cards (reads as a multi-hundred-ms delay).
       2. Progressive — content-visibility:hidden where supported (iOS
          Safari 18+, Chrome 85+, Firefox 125+). The cards stay mounted at
          zero intrinsic size, but the browser caches their rendering state
          so toggling back to visible is near-instant. The user does NOT
          see scrollable empty space below the map because the element
          collapses to its default intrinsic size when hidden. */
    body.view-map .mobile-cards,
    body.view-this-week .mobile-cards { display: none; }
    @supports (content-visibility: hidden) {
      body.view-map .mobile-cards,
      body.view-this-week .mobile-cards {
        display: flex;
        content-visibility: hidden;
        /* content-visibility:hidden skips the CONTENTS but still lays out the
           element's own box — so .mobile-cards' 8px/80px padding kept reserving
           ~88px of empty space. In This Week view the cards sit above the queue
           in the DOM, so that padding showed as a gap below the sticky header.
           Collapse the box (padding + intrinsic size) while hidden. */
        padding: 0;
        contain-intrinsic-size: 0;
      }
    }

    /* Modal — bottom sheet */
    /* Full-bleed: drop the overlay's desktop 20px gutter so the sheet spans edge
       to edge (matches the top-only radius + removed side/bottom borders below). */
    .modal-overlay { align-items: flex-end; padding: 0; }
    .modal {
      width: 100% !important;
      max-width: 100% !important;
      /* Keep the iOS status-bar / notch (safe-area-inset-top) clear ABOVE the
         sheet so the header title + ← Back never ride up under the clock/battery
         (the "overlap at the top" bug). The sheet is bottom-anchored, so trimming
         its height drops the top edge below the safe area; the extra 12px leaves a
         sliver of scrim visible so it still reads as a sheet, not a full page. */
      max-height: calc(100dvh - env(safe-area-inset-top, 0px) - 12px);
      border-radius: 18px 18px 0 0;
      border-left: none;
      border-right: none;
      border-bottom: none;
      transform: translateY(100%);
    }
    .modal-overlay.open .modal { transform: translateY(0); }
    /* Tightened for limited phone height: smaller drag-handle gap, padding and
       address so the sticky header eats less of the bottom sheet. */
    /* Don't pin the header on phones: a tall sticky header (especially with the
       brief expanded) cramps the scroll area, and the photo panel's
       z-index:1001 Sat/Map buttons bleed up over it (z-index:10) while
       scrolling. Let the sheet scroll naturally; the footer stays pinned. */
    /* position:relative (not sticky — see note above) so the floated avatar +
       ← Back below anchor to the header. */
    .modal-header { padding: 8px 16px 8px; position: relative; }
    /* Float the owner avatar + ← Back out of flow on phones. In flow they were
       in-flow flex items in the header row, reserving a ~110px column for the
       full header height — which squeezed the left column (and the action bar
       nested inside it) down to ~227px, forcing the five actions onto a 2nd row.
       Out of flow, the left column reclaims the FULL modal width, so the action
       bar fits on one line. Only the top text needs to clear them; the chips +
       action row sit below their bottom edge. */
    .modal-header > .modal-close { position: absolute; top: 10px; right: 16px; z-index: 2; }
    .modal-header > .modal-owner-avatar { position: absolute; top: 8px; right: 92px; z-index: 2; }
    .modal-header .modal-address,
    .modal-header .modal-subaddr,
    .modal-header #modalSubtitle { padding-right: 150px; }
    .modal-address { font-size: 16px; line-height: 1.25; }
    /* Reclaim header height on phones: the breadcrumb just repeats the address
       (already the big title) and the "← Back" button handles nav, so drop it.
       Also pull the brief rows in tighter. */
    .modal .modal-bc { display: none; }  /* .modal- prefix out-ranks the base .modal-bc rule defined later in the file */
    #modalSubtitle { margin-top: 2px !important; }
    #modalNewsRow, #modalPermitRow, #modalServicesRow { margin-top: 4px !important; }
    /* App-like icon action row on phones: drop the button labels (the title
       tooltip + the ? Help icon guide cover naming), square the buttons. The
       accent Call stays filled so the primary action still reads at a glance;
       the ⋯ overflow stays pinned right. */
    .modal-actionbar { gap: 6px; margin-top: 8px; }
    /* The action bar is a flex child of .modal-headrow. Left to shrink-to-fit, a
       nested flex-wrap container collapses to ~half the header width (~196px),
       which wrapped its own five buttons onto a 2nd row even though the modal had
       plenty of room. Force it onto its OWN full-width line (below the chips) so
       the buttons share the entire modal width and stay on one row. Needs the
       .modal-headrow descendant specificity to beat the margin-left:auto rule. */
    .modal-headrow .modal-actionbar { flex: 1 0 100%; width: 100%; margin-left: 0; }
    .modal-actionbar .mab-label { display: none; }
    .modal-actionbar .mab-btn { padding: 0; width: 36px; height: 36px; justify-content: center; gap: 0; }
    /* Two labelled pills carry the BD intent — Birdie (the one action a rep should
       always notice) and Create Deal (the funnel step that was getting lost as a
       bare icon). Everything else collapses to a 36px icon tile so all five actions
       fit on ONE row on a phone instead of wrapping to a second (precious vertical
       space). "Ask Birdie" shortens to "Birdie" on phones to buy that width back —
       the filled blue pill keeps it unmistakable. */
    .modal-actionbar .mab-birdie,
    .modal-actionbar #hubspotBtn { width: auto; padding: 0 9px; gap: 5px; }
    .modal-actionbar .mab-birdie .mab-label,
    .modal-actionbar #hubspotBtn .mab-label { display: inline; }
    .modal-actionbar .mab-ask { display: none; }
    .modal-actionbar .mab-more { width: 36px; height: 36px; padding: 0; }
    /* Read-view ⋯ "More actions" menu: a bottom SHEET on phones, not an absolute
       dropdown (which got clipped by the modal's overflow-x and collided with the
       photo panel's z-index:1001 controls) and not an in-flow list (which shoved
       the photo + decision signals below the fold). The modal is itself a
       bottom-anchored sheet whose transform is the containing block, so
       position:fixed pins the menu to the modal's bottom edge (= viewport bottom)
       without overflow clipping. It overlays content instead of reflowing it. */
    .modal-actionbar .footer-more-wrap { display: contents !important; }
    .modal-actionbar .footer-more-wrap > .mab-more { margin-left: auto; }
    .modal-actionbar .footer-more-wrap.open .footer-sheet-backdrop {
      display: block; position: fixed; inset: 0; z-index: 1190;
      background: rgba(0,0,0,0.45); animation: footerSheetFade 0.18s ease;
    }
    .modal-actionbar .footer-actions {
      display: flex !important;
      /* top:auto is load-bearing — the desktop rule sets top:calc(100%+6px),
         which would otherwise pin the sheet just *below* the modal, off-screen. */
      position: fixed; left: 0; right: 0; top: auto; bottom: 0; z-index: 1200;
      flex-direction: column; align-items: stretch; gap: 0;
      /* height:fit-content is required: a position:fixed element inside the
         modal's transform (its containing block) otherwise collapses its
         auto-height and the sheet renders as a sliver. */
      min-width: 0; margin: 0; height: fit-content; max-height: 80dvh; overflow-y: auto;
      padding: 6px 10px calc(10px + env(safe-area-inset-bottom, 0px));
      background: var(--surface);
      border: 0; border-top: 1px solid var(--border);
      border-radius: 18px 18px 0 0;
      box-shadow: 0 -10px 30px rgba(0,0,0,0.35);
      transform: translateY(110%); visibility: hidden;
      transition: transform 0.26s cubic-bezier(0.32,0.72,0,1), visibility 0.26s;
    }
    .modal-actionbar .footer-more-wrap.open .footer-actions {
      transform: translateY(0); visibility: visible;
    }
    /* Rows: native-style action list — leading icon, hairline divider, comfy tap
       target. No drag handle (it would imply a drag-to-dismiss this doesn't do —
       dismiss is via the backdrop). */
    .modal-actionbar .footer-actions .btn {
      justify-content: flex-start; width: 100%;
      min-height: 52px; padding: 0 12px; gap: 13px;
      font-size: 15px;
      border: 0 !important; border-radius: 0 !important;
      background: transparent !important;
      border-bottom: 1px solid var(--border) !important;
    }
    .modal-actionbar .footer-actions .btn:last-child { border-bottom: 0 !important; }
    .modal-actionbar .footer-actions .btn:active { background: var(--surface2) !important; }
    /* (Removed the bottom-sheet drag handle — it implied a drag-to-dismiss
       gesture this modal doesn't support, a false affordance eating header
       space. Dismiss is via "← Back" / backdrop tap.) */
    /* Belt-and-braces against any rogue child element (autocomplete
       dropdown, long button row, inline-styled content) pushing the
       modal-body past the viewport edge. overflow-x:clip clips without
       creating a scroll container, so sticky tabs / footer keep working. */
    .modal-body { padding: 0 18px 16px; overflow-x: clip; }
    /* Compact the sticky footer: tighter padding/gap + smaller action buttons
       so the (wrapping) button set claims fewer rows on a phone. Save/Cancel
       stay the rightmost, always-visible primary pair. */
    /* Keep the pinned footer above the photo panel's z-index:1001 toggle
       buttons so they can't bleed over it while scrolling. */
    .modal-footer { padding: 8px 14px; gap: 6px; overflow-x: clip; z-index: 1100; }
    .modal-footer .btn { padding: 6px 11px; font-size: 11.5px; }
    /* "More" on mobile: footer is one [⋯ More] [Cancel] [Save] row; tapping
       More expands the secondary actions IN-FLOW as a row directly above it.
       Deliberately NOT an absolute dropdown — an absolutely-positioned child of
       a position:sticky footer inside an overflow:auto scroller renders clipped
       on iOS Safari (the menu opened but was invisible). In-flow expansion has
       no sticky/overflow/abs interaction, so it's reliable. The wrap stays
       display:contents so More + actions are direct flex children of the
       footer and can reorder with `order`. */
    .footer-more-wrap { display: contents; }
    .footer-more-btn { display: inline-flex; order: 0; margin-right: auto; }
    .footer-actions {
      display: none;
      order: -1; flex: 0 0 100%;
      flex-wrap: wrap; gap: 6px;
      padding-bottom: 8px; margin-bottom: 2px;
      border-bottom: 1px solid var(--border);
    }
    .footer-more-wrap.open .footer-actions { display: flex; }
    .footer-actions .btn { font-size: 11.5px; padding: 6px 11px; }

    /* Contact actions — thumb-friendly */
    .contact-action-btn {
      min-height: 36px;
      font-size: 12px;
    }

    /* Toast */
    .toast { right: 12px; bottom: 12px; left: 12px; max-width: none; }

    /* Forms */
    .form-grid { grid-template-columns: 1fr; }
    .research-grid { grid-template-columns: 1fr; }
  }

  /* VIEW TOGGLE */
  .view-toggle {
    display: flex;
    background: var(--surface2);
    border: 1px solid var(--border);
    border-radius: 6px;
    padding: 3px;
    gap: 3px;
  }
  .view-btn {
    padding: 5px 12px;
    border-radius: 4px;
    font-size: 12px;
    font-weight: 500;
    cursor: pointer;
    color: var(--text3);
    border: none;
    background: none;
    font-family: var(--font-main);
    transition: all 0.15s;
    display: flex; align-items: center; gap: 5px;
  }
  .view-btn.active { background: var(--accent-dim2); color: var(--accent); border: 1px solid rgba(240, 75, 36,0.3); }
  .view-btn:hover:not(.active) { color: var(--text2); }
  .view-ico { width: 15px; height: 15px; flex-shrink: 0; }

  /* MAP */
  .map-wrap { display: none; padding: 0 24px 24px; position: relative; }
  .map-wrap.visible { display: block; }
  #map {
    /* Fill the desktop viewport instead of a fixed 580px (which left a white
       band below the map on tall screens). Offset ≈ header 56 + toolbar 56 +
       stats 52 + map margin-top 16 + legend/bottom-padding ~50 = 230. Driven by
       --map-chrome so the slim-map-header collapse can reclaim the stats + deal
       bands (~80px) without a second calc. min-height keeps it usable on short
       windows. Leaflet reflows on resize/collapse (invalidateSize in js/app.js). */
    height: calc(100vh - var(--map-chrome, 230px));
    min-height: 480px;
    border-radius: 8px;
    border: 1px solid var(--border);
    margin-top: 16px;
  }
  /* Slim map header (desktop): collapse the context bands (stats strip + deal
     filter) so the map gets the room — toggled from the toolbar, and auto-collapsed
     when you start panning/zooming (js). Mobile already hides these on .view-map
     (see the 768px block) and uses its own #map height, so --map-chrome only bites
     on desktop; the toggle is added to the mobile hide-list so it never shows there. */
  .chrome-toggle {
    display: none; align-items: center; gap: 4px; height: 30px; padding: 0 10px;
    border: 1px solid var(--border); border-radius: 6px; background: var(--surface2);
    color: var(--text3); font-family: var(--font-main); font-size: 11px; font-weight: 600;
    cursor: pointer; white-space: nowrap;
  }
  .chrome-toggle:hover { color: var(--text); border-color: var(--text3); }
  body.view-map #mapChromeToggle { display: inline-flex; }
  body.view-map.map-chrome-collapsed #statusBar,
  body.view-map.map-chrome-collapsed #dealFilterRow { display: none; }
  body.view-map.map-chrome-collapsed { --map-chrome: 150px; }
  /* Account cluster marker — a campus/account (all one owner) reads distinctly from
     a generic crowd-of-buildings cluster. Map-layer literal colors (like the other
     pins); purple matches the portfolio/account accent used in the table. */
  .acct-cluster { background: none; border: none; }
  /* Active-signal presence dot on a cluster bubble — just flags "there's a signal in
     here"; the count reveals itself as you zoom in. Positioned relative to the inner
     circle (the proven pattern; never touch the leaflet-positioned outer icon). */
  .marker-cluster > div { position: relative; }
  .cluster-sig {
    position: absolute; top: -2px; left: -2px; z-index: 2;
    width: 10px; height: 10px; border-radius: 50%;
    background: var(--accent); border: 1.5px solid var(--surface);
    box-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
  }
  .acct-cluster-inner {
    width: 44px; height: 44px; border-radius: 50%;
    background: rgba(124, 58, 237, 0.92); color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-size: 18px; position: relative;
    box-shadow: 0 0 0 4px rgba(124, 58, 237, 0.25);
  }
  .acct-cluster-n {
    position: absolute; top: -5px; right: -5px;
    background: #fff; color: #7c3aed; border: 1px solid rgba(124, 58, 237, 0.5);
    border-radius: 10px; min-width: 18px; padding: 0 5px;
    font-size: 11px; font-weight: 700; line-height: 16px; text-align: center;
    font-family: var(--font-mono);
  }
  /* Curated-campus cluster (🏛) — a real SITE, visually distinct from the co-located
     same-owner cluster (🏢): a white double-ring reads as "curated / pinned site".
     Same purple family so it stays on-brand in both themes. See iconCreateFunction. */
  .campus-cluster { background: none; border: none; }
  .campus-cluster-inner {
    width: 46px; height: 46px; border-radius: 50%;
    background: rgba(124, 58, 237, 0.95); color: #fff;
    display: flex; align-items: center; justify-content: center;
    font-size: 19px; position: relative;
    box-shadow: 0 0 0 2.5px #fff, 0 0 0 6px rgba(124, 58, 237, 0.35);
  }
  /* Metro coverage pills (map view) — one per covered metro at zoomed-out levels,
     badged with the tracked-building count: a spatial "where's our footprint?".
     Token-driven so they read in both themes. See _renderMetroPins() in js/app.js. */
  .metro-pin-wrap { background: none; border: none; }
  .metro-pin {
    display: inline-flex; align-items: center; gap: 6px; white-space: nowrap;
    transform: translate(-50%, -50%);
    /* Translucent "glass" so the map breathes through; color-mix keeps it
       theme-aware off --surface (defined for both themes), and the backdrop
       blur keeps the label legible over busy map areas. */
    background: color-mix(in srgb, var(--surface) 70%, transparent); color: var(--text);
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
    border: 1px solid var(--border2); border-radius: 999px;
    padding: 3px 4px 3px 10px; font-size: 11px; font-weight: 600; line-height: 1;
    cursor: pointer; box-shadow: 0 2px 8px rgba(0, 0, 0, 0.28);
    transition: transform .1s ease, border-color .1s ease;
  }
  .metro-pin:hover { border-color: var(--accent); transform: translate(-50%, -50%) scale(1.06); }
  .metro-pin-count {   /* tracked buildings — neutral context */
    background: var(--surface2); color: var(--text2); border-radius: 999px;
    min-width: 18px; padding: 0 6px; font-size: 11px; font-weight: 700;
    line-height: 18px; text-align: center; font-family: var(--font-mono);
  }
  .metro-pin-sig {     /* active signals — the BD value, accent + broadcast glyph */
    display: inline-flex; align-items: center; gap: 1px;
    background: var(--accent); color: #fff; border-radius: 999px;
    padding: 0 7px 0 5px; font-size: 11px; font-weight: 700;
    line-height: 18px; font-family: var(--font-mono);
  }
  .metro-pin-empty { opacity: .6; }
  .metro-pin-empty .metro-pin-count { color: var(--text3); }
  .metro-pin-custom { border-style: dashed; }   /* operator-added metro */

  /* Contextual "back to metro" chip — appears bottom-center of the map only when
     you've zoomed in past the metro's home view; one tap pops back out. */
  .map-zoomout-chip {
    position: absolute; left: 50%; bottom: 18px; transform: translateX(-50%);
    z-index: 1100; display: inline-flex; align-items: center; gap: 6px;
    background: var(--surface); color: var(--text);
    border: 1px solid var(--border2); border-radius: 999px;
    padding: 7px 14px; font-size: 12px; font-weight: 600; font-family: inherit;
    cursor: pointer; box-shadow: 0 2px 10px rgba(0, 0, 0, 0.28);
  }
  .map-zoomout-chip:hover { border-color: var(--accent); }
  .map-zoomout-chip svg { color: var(--accent); flex-shrink: 0; }

  /* Account picker (issue #113) — assign / create / move a building's account
     (a HubSpot company). Theme-safe via tokens; sits above the BD rail + modals. */
  .acct-picker-overlay {
    position: fixed; inset: 0; z-index: 4000; background: rgba(0,0,0,0.45);
    display: none; align-items: flex-start; justify-content: center; padding: 12vh 16px;
    /* Own the scroll so a wheel/touch gesture over the dim area doesn't chain
       through to the page behind the modal. */
    overflow: auto; overscroll-behavior: contain;
  }
  .acct-picker-overlay.open { display: flex; }
  .acct-picker {
    width: 100%; max-width: 420px; max-height: 70vh; overflow: auto;
    background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
    box-shadow: 0 12px 40px rgba(0,0,0,0.35); padding: 14px 16px;
  }
  .acct-picker-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
  .acct-picker-title { font-size: 14px; font-weight: 700; color: var(--text); }
  .acct-picker-sub { font-size: 12px; color: var(--text3); margin-top: 2px; }
  .acct-picker-x { background: none; border: none; color: var(--text3); font-size: 15px; cursor: pointer; padding: 2px 6px; }
  .acct-picker-cur { display: flex; align-items: center; justify-content: space-between; gap: 8px; font-size: 12px; color: var(--text2); margin: 10px 0; }
  .acct-picker-muted { color: var(--text3); }
  .acct-picker-remove { background: none; border: 1px solid var(--border); color: var(--text3); border-radius: 8px; padding: 2px 8px; font-size: 11px; cursor: pointer; font-family: var(--font-main); }
  .acct-picker-remove:hover { color: #f87171; border-color: #f87171; }
  .acct-picker-input { width: 100%; box-sizing: border-box; background: var(--surface2); border: 1px solid var(--border); border-radius: 8px; padding: 9px 11px; color: var(--text); font-size: 13px; font-family: var(--font-main); }
  .acct-picker-results { margin-top: 8px; display: flex; flex-direction: column; gap: 4px; }
  .acct-picker-row { display: flex; align-items: center; justify-content: space-between; gap: 10px; width: 100%; text-align: left; background: var(--surface2); border: 1px solid var(--border); border-radius: 8px; padding: 8px 10px; color: var(--text); font-size: 13px; cursor: pointer; font-family: var(--font-main); }
  .acct-picker-row:hover { border-color: var(--accent); }
  .acct-picker-dom { font-size: 11px; color: var(--text3); flex: 0 0 auto; }
  .acct-picker-create { color: var(--accent); font-weight: 600; }
  /* ── Compare buildings (duplicate triage) ── tight side-by-side grid. Built
     from divs (not a table) so it never inherits the uppercase/letter-spaced
     th styling, and scrolls inside itself (background stays put). */
  .cmp-panel { max-height: 74vh; overflow-y: auto; overscroll-behavior: contain; }
  .cmp-grid { display: grid; gap: 0; margin-top: 10px; font-family: var(--font-main); }
  .cmp-head { padding: 0 6px 8px; }
  .cmp-card { border: 1px solid var(--border); border-radius: 9px; overflow: hidden; background: var(--surface2); }
  .cmp-card.cmp-best { border-color: var(--accent); }
  .cmp-img { width: 100%; height: 84px; object-fit: cover; display: block; background: var(--surface); }
  .cmp-img-none { height: 84px; display: flex; align-items: center; justify-content: center; color: var(--text3); font-size: 10px; background: var(--surface); }
  .cmp-card-body { padding: 6px 7px 7px; }
  .cmp-name { font-size: 11.5px; font-weight: 700; color: var(--text1); line-height: 1.25; text-transform: none; letter-spacing: 0; }
  .cmp-keep { font-size: 9.5px; font-weight: 700; color: var(--accent); margin-top: 1px; }
  .cmp-badges { display: flex; flex-wrap: wrap; gap: 3px; margin-top: 5px; min-height: 14px; }
  .cmp-badge { font-size: 9px; background: var(--surface); border: 1px solid var(--border); border-radius: 4px; padding: 1px 4px; color: var(--text2); text-transform: none; letter-spacing: 0; }
  .cmp-badge-none { font-size: 9.5px; color: var(--text3); }
  .cmp-del { margin-top: 6px; width: 100%; background: none; border: 1px solid var(--border); color: #e0584f; border-radius: 6px; padding: 4px 6px; font-size: 11px; font-weight: 600; cursor: pointer; font-family: var(--font-main); text-transform: none; letter-spacing: 0; }
  .cmp-del:hover { border-color: #e0584f; background: rgba(224,88,79,0.08); }
  .cmp-lbl { font-size: 10px; color: var(--text3); text-align: right; padding: 5px 8px; white-space: nowrap; border-top: 1px solid var(--border); display: flex; align-items: center; justify-content: flex-end; }
  .cmp-val { font-size: 11.5px; color: var(--text2); padding: 5px 8px; border-top: 1px solid var(--border); overflow-wrap: anywhere; }
  .cmp-val.cmp-diff { color: var(--text1); font-weight: 600; }
  .cmp-note { font-size: 10.5px; color: var(--text3); line-height: 1.5; padding: 9px 4px 2px; }
  /* Theme the Leaflet container background so the gutters around the world map
     (visible at low zoom, where tiles don't fill the box) match the app surface
     instead of Leaflet's default light #ddd — which read as a broken light band
     in dark mode. Covers the main map + the mini map. Our app.css loads after
     leaflet.css, so this wins. */
  .leaflet-container {
    background: var(--surface2);
    /* Suppress iOS Safari's long-press text-selection callout ("Copy / Find
       Selection") so a long-press to quick-add a building reads as a clean
       gesture, not a text selection. Our contextmenu handler still fires. */
    -webkit-touch-callout: none;
    -webkit-user-select: none;
    user-select: none;
  }
  /* "Fit to results" reset control — centers the icon in the Leaflet bar button
     (inherits the +/- button chrome via the leaflet-bar class). */
  .map-reset-ctl a { display: flex; align-items: center; justify-content: center; }
  /* Keep the control stack off the screen edge on notched / rounded devices
     (in portrait these insets are 0, so no change there). */
  .leaflet-top .leaflet-left .leaflet-control,
  .leaflet-left .leaflet-control { margin-left: calc(10px + env(safe-area-inset-left, 0px)); }
  .leaflet-top .leaflet-control { margin-top: calc(10px + env(safe-area-inset-top, 0px)); }
  .map-legend {
    display: flex; gap: 16px; align-items: center;
    margin-top: 10px;
    font-size: 11px; color: var(--text3); font-family: var(--font-mono);
    flex-wrap: wrap;
  }

  /* Mobile map — half-screen so list is still reachable below */
  @media (max-width: 768px) {
    .map-wrap.visible { padding: 0; display: block; }
    #map {
      height: min(45vh, 360px);
      min-height: 240px;
      border-radius: 0;
      border-left: none; border-right: none; border-top: none;
      margin-top: 0;
    }
    .map-legend { padding: 8px 14px; gap: 10px; font-size: 10px; margin-top: 0; }
    /* Map view reclaims screen — collapse the chrome that isn't useful while
       working the map (KPI + deal rows, breadcrumb, market picker, search, and
       the redundant "within X" radius chip), leaving just the view tabs, and
       grow the map toward the full viewport. Height adapts to whether Near Me's
       radius controls are showing (body.near-me-on). */
    body.view-map #statusBar,
    body.view-map #dealFilterRow,
    body.view-map #mapChromeToggle,
    body.view-map .scout-trail--bar,
    body.view-map #filterChips,
    body.view-map .toolbar > .metro-select-wrap,
    body.view-map .omnibox-wrap { display: none; }
    body.view-map .toolbar { padding-top: 4px; padding-bottom: 6px; }
    body.view-map #map { height: calc(100dvh - 120px); min-height: 340px; }
    body.view-map.near-me-on #map { height: calc(100dvh - 150px); }
    /* Floating Table button pinned bottom-left over map on mobile */
    #mapTableBtn {
      display: flex;
      position: fixed;
      bottom: 80px; left: 16px; z-index: 500;
      background: var(--surface);
      border: 1px solid var(--border2);
      border-radius: 20px;
      padding: 8px 16px;
      font-size: 13px; font-weight: 600;
      color: var(--text2);
      cursor: pointer;
      box-shadow: 0 2px 12px rgba(0,0,0,0.25);
      align-items: center; gap: 6px;
    }
  }
  #mapTableBtn { display: none; } /* hidden on desktop */
  .legend-dot {
    width: 8px; height: 8px; border-radius: 50%; display: inline-block; margin-right: 5px;
  }

  /* ── Fullscreen map ────────────────────────────────────
     Toggle button sits top-right of the map. */
  .map-fs-btn {
    position: absolute; z-index: 600;
    top: calc(10px + env(safe-area-inset-top, 0px));
    right: calc(10px + env(safe-area-inset-right, 0px));
    width: 38px; height: 38px; border-radius: 8px;
    background: var(--surface); border: 1px solid var(--border2); color: var(--text);
    display: flex; align-items: center; justify-content: center; cursor: pointer;
    box-shadow: 0 2px 10px rgba(0,0,0,0.25); font-size: 16px; line-height: 1; padding: 0;
  }
  .map-fs-btn svg { width: 18px; height: 18px; }
  .map-fs-btn:hover { border-color: var(--accent); color: var(--accent); }

  /* Persistent property-address bar (shown while the BD rail is open). Centered
     pill at the top, clearing the zoom (left) and fullscreen (right) controls,
     and click-through so it never blocks the map. */
  .bd-addr-bar {
    position: absolute; z-index: 600; display: none; text-align: center;
    pointer-events: none;
    top: calc(10px + env(safe-area-inset-top, 0px));
    left: calc(56px + env(safe-area-inset-left, 0px));
    right: calc(58px + env(safe-area-inset-right, 0px));
  }
  .bd-addr-bar.show { display: block; }
  .bd-addr-bar-inner {
    display: inline-flex; max-width: 100%; align-items: center; gap: 6px;
    background: var(--surface); border: 1px solid var(--border2);
    border-radius: 10px; padding: 7px 12px; box-shadow: 0 4px 16px rgba(0,0,0,0.3);
  }
  .bd-addr-bar .pin { color: var(--accent); flex: 0 0 auto; font-size: 12px; }
  .bd-addr-bar-text {
    font-size: 13px; font-weight: 600; color: var(--text);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .bd-addr-bar-name { font-weight: 400; color: var(--text3); }

  /* ── Collapsing map search ─────────────────────────────
     The 🔍 trigger is a Leaflet control in the top-left stack (themed by the
     app's leaflet-bar rules, like the zoom/reset buttons). The field below is
     revealed on demand, clearing the zoom column (left) and fullscreen (right). */
  .map-search-ctl a { font-weight: normal; }
  .map-search {
    position: absolute; z-index: 700; display: none;
    /* Clear the left control column (zoom/reset/search ≈ 44px) and the
       right fullscreen button (≈ 48px) with comfortable gaps, and honour
       device safe-areas so nothing hugs / gets clipped at the screen edge. */
    top: calc(10px + env(safe-area-inset-top, 0px));
    left: calc(56px + env(safe-area-inset-left, 0px));
    right: calc(58px + env(safe-area-inset-right, 0px));
  }
  .map-search.open { display: block; }
  .map-search-bar {
    display: flex; align-items: center; gap: 8px;
    background: var(--surface); border: 1px solid var(--border2);
    border-radius: 10px; padding: 8px 10px; box-shadow: 0 4px 16px rgba(0,0,0,0.3);
  }
  .map-search-bar > svg { width: 16px; height: 16px; color: var(--text3); flex: 0 0 auto; }
  .map-search-input {
    flex: 1; min-width: 0; border: none; background: transparent; outline: none;
    color: var(--text); font-family: var(--font-main); font-size: 14px; padding: 2px 0;
  }
  .map-search-input::placeholder { color: var(--text3); }
  .map-search-x {
    border: none; background: transparent; color: var(--text3); cursor: pointer;
    font-size: 14px; line-height: 1; padding: 4px 6px; flex: 0 0 auto;
  }
  /* Results reuse the omnibox markup (.omnibox-section / .omnibox-row), so the
     map search list looks identical to the toolbar omnibox. This is just the
     panel chrome around it; inner padding mirrors .omnibox-panel. */
  .map-search-results {
    margin-top: 6px; background: var(--surface); border: 1px solid var(--border2);
    border-radius: 10px; box-shadow: 0 8px 24px rgba(0,0,0,0.4); padding: 4px;
    max-height: 50dvh; overflow-y: auto; -webkit-overflow-scrolling: touch;
  }

  /* Fullscreen ON: hide the page chrome and let the map fill the viewport. The
     map stays in normal flow (z auto), so the building modal / BD panel (z 3000)
     still open above it from a pin tap. Cleared on leaving Map view. */
  body.map-fullscreen { overflow: hidden; }
  body.map-fullscreen header,
  body.map-fullscreen .scout-trail--bar,
  body.map-fullscreen .toolbar,
  body.map-fullscreen #statusBar,
  body.map-fullscreen #dealFilterRow,
  body.map-fullscreen #filterChips,
  body.map-fullscreen #radiusBar,
  body.map-fullscreen .near-me-fallback { display: none !important; }
  body.map-fullscreen .map-wrap.visible { padding: 0 !important; }
  body.map-fullscreen #map {
    height: 100dvh !important; min-height: 0 !important;
    border: none !important; border-radius: 0 !important; margin: 0 !important;
  }
  body.map-fullscreen .map-legend { display: none; }
  body.map-fullscreen .map-fs-btn { z-index: 1600; top: calc(env(safe-area-inset-top, 0px) + 10px); }
  body.map-fullscreen #mapTableBtn { z-index: 1600; }

  /* THIS WEEK — Ring 1 prioritization queue (issue #29) */
  .this-week-wrap { padding: 12px 24px 32px; max-width: 1080px; margin: 0 auto; }
  .tw-header {
    margin-bottom: 6px;
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 16px;
    flex-wrap: wrap;
  }
  .tw-titlerow { display: flex; align-items: center; gap: 6px; }
  .tw-title { font-size: 18px; font-weight: 700; color: var(--text1); letter-spacing: 0.01em; }
  /* The pipeline explainer used to be an always-on subtitle row; it's reference,
     not glance-state, so it folds into this ⓘ (hover/long-press tooltip) and the
     board's column headers carry the Signals→…→Proposal story. */
  .tw-sub-info {
    flex: 0 0 auto; width: 20px; height: 20px; padding: 0;
    display: inline-flex; align-items: center; justify-content: center;
    background: none; border: none; cursor: help;
    color: var(--text3); font-size: 13px; line-height: 1;
    border-radius: 50%; transition: color 0.12s ease, background 0.12s ease;
  }
  .tw-sub-info:hover { color: var(--text1); background: var(--surface2); }
  .tw-sub { font-size: 12px; color: var(--text3); margin-top: 4px; }
  .tw-refresh-btn {
    flex: 0 0 auto;
    display: inline-flex; align-items: center; justify-content: center; gap: 6px;
    min-height: 30px; padding: 0 11px;   /* compact icon at rest; widens for status text */
    border-radius: 7px;
    border: 1px solid var(--border2);
    background: var(--surface2);
    color: var(--text1);
    font-family: var(--font-mono);
    font-size: 14px; line-height: 1;
    font-weight: 600;
    cursor: pointer;
    transition: border-color 0.15s, background 0.15s;
  }
  /* While running it carries a text label (smaller, so the status reads tidily). */
  .tw-refresh-btn.tw-refresh-running { font-size: 12px; }
  .tw-refresh-btn:hover:not(:disabled) { border-color: var(--accent2); color: var(--accent); }
  .tw-refresh-btn:disabled { cursor: progress; opacity: 0.7; }
  .tw-refresh-running { color: var(--accent); border-color: var(--accent2); }

  /* Freshness indicator (issue #48 PR-C UX). Sits below the queue sub-line
     to tell BD how fresh the data is at a glance. Three tiers: fresh
     (green), warm (amber, no action needed but flag the age), stale
     (red, with explicit CTA in the label). The hourly cron sweep should
     keep most days in the green tier. */
  .tw-freshness {
    margin-top: 6px;
    display: inline-block;
    font-size: 11px;
    font-family: var(--font-mono);
    padding: 2px 8px;
    border-radius: 4px;
    letter-spacing: 0.02em;
  }
  .tw-fresh-ok { /* semantic freshness OK — stays green */
    color: #4ade80;
    background: rgba(74, 222, 128, 0.08);
    border: 1px solid rgba(74, 222, 128, 0.25);
  }
  .tw-fresh-warm {
    color: #f59e0b;
    background: rgba(245, 158, 11, 0.10);
    border: 1px solid rgba(245, 158, 11, 0.30);
  }
  .tw-fresh-stale {
    color: #f87171;
    background: rgba(248, 113, 113, 0.10);
    border: 1px solid rgba(248, 113, 113, 0.35);
    font-weight: 600;
  }

  /* Refresh-in-progress banner (issue #48 PR-C). Sits at the top of the
     queue while Refresh queue runs so BD sees the phase + percent + count
     without having to open DevTools. The bar fill uses a linear gradient
     in the brand-accent so it reads as "active work happening." */
  .tw-progress-banner {
    background: var(--surface2);
    border: 1px solid var(--accent2);
    border-radius: 10px;
    padding: 14px 18px;
    margin-bottom: 16px;
    display: flex;
    flex-direction: column;
    gap: 10px;
  }
  .tw-progress-row {
    display: flex;
    justify-content: space-between;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
  }
  .tw-progress-phase {
    color: var(--accent);
    font-weight: 600;
    font-size: 13px;
    letter-spacing: 0.01em;
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .tw-progress-phase::before {
    content: '';
    width: 8px;
    height: 8px;
    border-radius: 50%;
    background: var(--accent);
    animation: tw-progress-pulse 1.4s ease-in-out infinite;
  }
  @keyframes tw-progress-pulse {
    0%, 100% { opacity: 1; }
    50%      { opacity: 0.35; }
  }
  .tw-progress-count {
    color: var(--text2);
    font-family: var(--font-mono);
    font-size: 12px;
  }
  .tw-progress-bar {
    height: 6px;
    background: var(--surface3, rgba(255,255,255,0.06));
    border-radius: 3px;
    overflow: hidden;
  }
  .tw-progress-fill {
    height: 100%;
    background: linear-gradient(90deg, var(--accent), var(--accent2));
    border-radius: 3px;
    transition: width 0.4s ease;
    width: 0%;
  }
  .tw-empty {
    border: 1px dashed var(--border);
    border-radius: 10px;
    padding: 32px 24px;
    text-align: center;
    color: var(--text2);
    background: var(--surface2);
  }
  .tw-empty-title { font-size: 15px; font-weight: 600; color: var(--text1); margin-bottom: 8px; }
  .tw-empty-body { font-size: 13px; color: var(--text3); max-width: 540px; margin: 0 auto; line-height: 1.5; }
  /* Compact v2 card — score is the headline (right), everything else collapses
     to a few tight lines so 5-6 cards fit per mobile viewport. Rich detail
     (portfolio rail, HubSpot relationship + links, score pips) is mounted but
     hidden behind .tw-card-open; expanding toggles one class, no re-render. */
  .tw-card {
    display: flex;
    align-items: stretch;
    gap: 12px;
    padding: 9px 12px;
    border: 1px solid var(--border);
    border-radius: 10px;
    background: var(--surface);
    margin-bottom: 7px;
    cursor: pointer;
    transition: border-color 0.15s, transform 0.05s;
  }
  .tw-card:hover { border-color: var(--accent2); }
  .tw-card:active { transform: scale(0.997); }
  /* Building thumbnail (Street View / satellite) — a visual anchor for the card.
     Top-aligned so it reads as the building's "face" next to the text block. */
  .tw-thumb {
    flex: 0 0 auto;
    width: 52px; height: 52px;
    border-radius: 8px;
    object-fit: cover;
    align-self: flex-start;
    background: var(--surface2);
    border: 1px solid var(--border);
  }
  /* Thumb + city caption stacked, filling the whitespace under the image. */
  .tw-thumb-wrap {
    flex: 0 0 auto; width: 52px;
    display: flex; flex-direction: column; align-items: center; gap: 4px;
    align-self: flex-start;
  }
  .tw-thumb-wrap .tw-thumb { align-self: stretch; }
  .tw-thumb-city {
    max-width: 72px; margin-top: 3px;
    font-size: 9px; font-weight: 500; letter-spacing: .01em;
    color: var(--text3); text-align: center; line-height: 1.3;
    /* The left column is tall (height comes from the text block), so let the
       city use that vertical space — wrap the full name across lines, breaking
       at spaces only (never mid-word). */
    overflow-wrap: break-word; word-break: normal; hyphens: none;
  }
  /* Insertion indicator shown while dragging to reorder within a column. */
  .tw-drop-line {
    height: 3px; margin: 2px;
    border-radius: 2px;
    background: var(--accent, #7c3aed);
    box-shadow: 0 0 0 1px rgba(124,58,237,0.25);
  }
  /* Caveat note under the Signals column header (auto-ranked → order is temporary). */
  .tw-col-note {
    display: flex; align-items: center; gap: 6px;
    font-size: 10.5px; line-height: 1.35; color: var(--text3);
    background: rgba(148,163,184,0.08);
    border: 1px solid var(--border);
    border-radius: 8px; padding: 4px 6px 4px 8px; margin: 0 2px 8px;
    white-space: nowrap; overflow: hidden;
  }
  .tw-col-note > span { flex: 1 1 auto; }
  .tw-col-note-x {
    flex: 0 0 auto; background: none; border: none; cursor: pointer;
    color: var(--text3); font-size: 12px; line-height: 1; padding: 1px 3px;
    border-radius: 4px;
  }
  .tw-col-note-x:hover { color: var(--text1); background: rgba(148,163,184,0.18); }
  .tw-main { flex: 1 1 auto; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
  .tw-addr-row { display: flex; align-items: center; gap: 7px; min-width: 0; }
  .tw-rank {
    flex: 0 0 auto;
    font-family: var(--font-mono);
    font-size: 11px;
    font-weight: 700;
    color: var(--text3);
  }
  .tw-addr {
    flex: 1 1 auto; min-width: 0;
    font-size: 13px; font-weight: 600; color: var(--text1);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .tw-rel-mini { flex: 0 0 auto; font-size: 12px; line-height: 1; }
  /* Inline score badge — "• 91" riding on the property-name line (Signals). */
  .tw-score-badge {
    flex: 0 0 auto; font-family: var(--font-mono);
    font-size: 11px; font-weight: 700; color: var(--text2);
  }
  .tw-score-badge::before { content: "• "; color: var(--text3); font-weight: 400; }
  /* Secondary chips, relocated into the expandable detail (off the card face). */
  /* Chevron is now a passive open/closed *indicator*, not a control — the whole
     card is the tap target (see _twCardClick), so it takes no pointer events and
     just rotates with the card's state. */
  .tw-chevron {
    flex: 0 0 auto;
    width: 22px; height: 22px;
    display: flex; align-items: center; justify-content: center;
    margin-right: -2px;
    color: var(--text3); font-size: 16px; line-height: 1;
    cursor: pointer;
    border-radius: 6px;
    transition: transform 0.15s ease, color 0.12s ease, background 0.12s ease;
  }
  .tw-card:hover .tw-chevron { color: var(--text1); }
  .tw-chevron:hover { color: var(--accent2); background: var(--surface2); }
  .tw-card-open .tw-chevron { transform: rotate(180deg); color: var(--accent2); }
  @media (max-width: 768px) {
    .tw-chevron { width: 24px; height: 24px; font-size: 18px; }
  }
  /* Owner line — tiny, single line, with the tier chip riding along. */
  .tw-owner > span { min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  /* Diagnosing gate chip — the one glance a Diagnosing card needs: how many of
     the three gates are met. Green = all met (can advance), amber = work to do.
     Works in both themes (token-driven, mid-tone status colours). */
  /* Diagnosing gate chip — one tight glanceable line: 🚪 n/3 + three status
     pips (green = met, amber = not). Never wraps. Works in both themes. */
  .tw-gates {
    display: flex; align-items: center; gap: 8px;
    width: 100%; min-height: 30px; margin-top: 4px; padding: 4px 9px;
    border-radius: 8px; border: 1px solid var(--border2);
    background: var(--surface); cursor: pointer;
    font-family: inherit; font-size: 11px; line-height: 1.2; text-align: left;
    white-space: nowrap; overflow: hidden;
  }
  .tw-gates-head { flex: 0 0 auto; font-weight: 700; font-size: 11px; letter-spacing: 0.01em; }
  .tw-gate-pips { display: inline-flex; align-items: center; gap: 7px; }
  .tw-gate-pip { font-size: 12px; line-height: 1; color: var(--text3); }
  .tw-gate-pip.tw-gate-on  { color: var(--green, #4ade80); }
  .tw-gate-pip.tw-gate-off { color: var(--amber, #d97706); }
  .tw-gates.tw-gates-met {
    border-color: var(--green, #4ade80);
    background: color-mix(in srgb, var(--green, #4ade80) 12%, transparent);
  }
  .tw-gates.tw-gates-met .tw-gates-head { color: var(--green, #4ade80); }
  .tw-gates.tw-gates-unmet { border-color: var(--amber, #d97706); }
  .tw-gates.tw-gates-unmet .tw-gates-head { color: var(--amber, #d97706); }
  /* Per-lane accent on the column header — a Pursuing card and a Diagnosing card
     should not read the same. Restrained: a left-border tint, nothing loud. */
  .tw-col[data-stage="diagnosing"]    .tw-col-head { box-shadow: inset 3px 0 0 var(--amber, #d97706); padding-left: 9px; }
  .tw-col[data-stage="business_case"] .tw-col-head { box-shadow: inset 3px 0 0 var(--accent, #38bdf8); padding-left: 9px; }
  .tw-col[data-stage="proposal"]      .tw-col-head { box-shadow: inset 3px 0 0 var(--green, #4ade80); padding-left: 9px; }
  /* Rationale — clamped to one line; expand-on-click reveals it in full. */
  .tw-rationale-line {
    display: none;   /* the "why" is detail, not glance-state — hidden until the card is expanded (⌄) */
    font-size: 11.5px; color: var(--text2); font-style: italic;
    line-height: 1.35; cursor: pointer;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .tw-rationale-line::before { content: "\201C"; }
  .tw-rationale-line::after  { content: "\201D \2304"; font-style: normal; color: var(--text3); }
  .tw-card-open .tw-rationale-line {
    display: block; white-space: normal; overflow: visible; text-overflow: clip;
  }
  .tw-card-open .tw-rationale-line::after { content: "\201D"; }
  .tw-card-actions { margin-top: 5px; display: flex; align-items: center; gap: 6px; flex-wrap: wrap; }
  .tw-card-actions .tw-next-cta { margin-top: 0; }
  /* Action buttons carry an inline line-icon + label (replaces the emoji glyphs). */
  .tw-card-actions .tw-next-cta,
  .tw-card-actions .tw-move-cta,
  .tw-card-actions .tw-pass-cta { display: inline-flex; align-items: center; gap: 5px; }
  .cta-ico { flex: 0 0 auto; display: block; }
  .tw-pass-cta {
    margin-top: 0;
    align-self: flex-start;
    padding: 5px 12px;
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    color: var(--text2, #cbd5e1);
    background: rgba(148,163,184,0.08);
    border: 1px solid var(--border2, #475569);
    border-radius: 6px;
    cursor: pointer;
    line-height: 1.4;
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
  }
  .tw-pass-cta:hover { color: #f87171; border-color: rgba(239,68,68,0.55); background: rgba(239,68,68,0.10); }
  .tw-pass-cta:active { transform: translateY(1px); }
  .tw-add-btn {
    padding: 5px 11px;
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    color: var(--accent);
    background: rgba(124,58,237,0.12);
    border: 1px solid rgba(124,58,237,0.4);
    border-radius: 7px;
    cursor: pointer;
    flex-shrink: 0;
    transition: background 0.12s ease, border-color 0.12s ease;
  }
  .tw-add-btn:hover { background: rgba(124,58,237,0.22); border-color: rgba(124,58,237,0.6); }
  .tw-add-btn:active { transform: translateY(1px); }
  /* Passed panel trigger — neutral counterpart to + Add. Distinct class so
     tests can disambiguate it from + Add. */
  .tw-passed-btn {
    padding: 5px 11px;
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    color: var(--text2, #cbd5e1);
    background: rgba(148,163,184,0.10);
    border: 1px solid var(--border2, #475569);
    border-radius: 7px;
    cursor: pointer;
    flex-shrink: 0;
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
  }
  .tw-passed-btn:hover { background: rgba(148,163,184,0.18); border-color: var(--text3); color: var(--text1); }
  .tw-passed-btn:active { transform: translateY(1px); }
  .tw-rank-pinned { color: var(--accent); }
  .tw-pin-chip {
    font-size: 10px;
    font-weight: 600;
    color: var(--accent);
    background: rgba(124,58,237,0.12);
    border: 1px solid rgba(124,58,237,0.35);
    border-radius: 4px;
    padding: 1px 6px;
    white-space: nowrap;
  }
  .tw-card-pinned { border-left: 2px solid rgba(124,58,237,0.55); }
  /* Pointer-drag: the floating clone that follows the cursor, and the dimmed
     source card left behind. The clone is a real .tw-card so it keeps all card
     styling; these rules only add lift + positioning. */
  .tw-card-ghost {
    position: fixed; z-index: 100002; pointer-events: none; margin: 0 !important;
    opacity: 0.92; box-shadow: 0 14px 34px rgba(0,0,0,0.55); transform: rotate(1.5deg);
    background: var(--surface, #0f1117); border: 1px solid var(--accent, #7c3aed); border-radius: 12px;
  }
  .tw-card-dragging { opacity: 0.35; }
  body.tw-dragging, body.tw-dragging * { cursor: grabbing !important; user-select: none !important; }
  /* Move action — neutral secondary button beside Call/Pass. */
  .tw-move-cta {
    margin-top: 0;
    padding: 5px 12px;
    font-size: 12px;
    font-weight: 600;
    font-family: inherit;
    color: var(--text2);
    background: rgba(148,163,184,0.10);
    border: 1px solid var(--border2, #475569);
    border-radius: 6px;
    cursor: pointer;
    line-height: 1.4;
    transition: background 0.12s ease, border-color 0.12s ease, color 0.12s ease;
  }
  .tw-move-cta:hover { color: var(--accent); border-color: rgba(124,58,237,0.55); background: rgba(124,58,237,0.10); }
  .tw-move-cta:active { transform: translateY(1px); }
  /* Waiting tag on working-column cards (replaces the rank). */
  .tw-rank-wait { color: var(--text3); font-weight: 600; white-space: nowrap; }
  .tw-rank-stale { color: #fbbf24; }

  /* ── Cockpit Kanban board (Phase 2) ───────────────────────────────────── */
  /* Mobile-first: one column at a time, driven by the switcher. */
  .tw-switcher {
    display: flex; gap: 6px; overflow-x: auto; margin-bottom: 14px;
    -webkit-overflow-scrolling: touch; padding-bottom: 2px;
  }
  /* Deck/list view toggle now lives in the ⋯ More menu (a .tw-more-item);
     it's a mobile-only preference, so hide it on desktop where the board is fixed. */
  /* The deck is mobile-only and off unless deck mode is on (see the max-width
     block below); hidden everywhere by default so desktop never paints it. */
  .tw-deck { display: none; }
  .tw-switch-tab {
    flex: 1 0 auto; display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 12px; border-radius: 999px; font-size: 12px; font-weight: 600;
    font-family: inherit; white-space: nowrap; cursor: pointer;
    color: var(--text2, #94a3b8); background: transparent;
    border: 1px solid var(--border, #1e2433); transition: all 0.12s ease;
  }
  .tw-switch-tab.tw-switch-on {
    color: var(--accent); background: rgba(124,58,237,0.14);
    border-color: rgba(124,58,237,0.5);
  }
  .tw-switch-count {
    font-size: 10px; min-width: 16px; height: 16px; padding: 0 4px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 8px; background: rgba(148,163,184,0.18); color: inherit;
  }
  /* Cockpit job toggle (prospect ⇄ expansion). Mirrors the segmented column
     switcher above, but — unlike .tw-switcher — shows on BOTH desktop and mobile
     (it's a job switch, not a one-column-at-a-time mobile affordance). */
  /* One compact control row: title + job toggle + asset-class lens on the left,
     the board's action band pinned to the right — collapsing what used to be a
     title row, a filter row, and an action row into a single line that wraps
     gracefully when narrow. */
  .tw-filters {
    display: flex; flex-wrap: wrap; align-items: center;
    gap: 8px 16px; margin-bottom: 10px;
  }
  .tw-filters-left {
    flex: 1 1 auto; min-width: 0;
    display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px;
  }
  .tw-filters .tw-sub-info { flex: 0 0 auto; margin-right: 2px; }
  .tw-filters .tw-jobtoggle,
  .tw-filters .tw-asset-row { margin: 0; }
  .tw-filters .tw-header-actions { flex: 0 0 auto; margin: 0; }
  /* ⓘ + Prospect/Expansion + the campus lens on one line. */
  .tw-lensrow { display: flex; align-items: center; gap: 8px; min-width: 0; }
  /* Dev-only queue-freshness note — its own thin line under the control row. */
  .tw-freshrow { margin: 0 0 8px; }
  .tw-jobtoggle { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
  .tw-job-tab {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 7px 14px; border-radius: 999px; font-size: 12px; font-weight: 600;
    font-family: inherit; white-space: nowrap; cursor: pointer;
    color: var(--text2, #94a3b8); background: transparent;
    border: 1px solid var(--border, #1e2433); transition: all 0.12s ease;
  }
  .tw-job-tab.tw-job-on {
    color: var(--accent); background: rgba(124,58,237,0.14);
    border-color: rgba(124,58,237,0.5);
  }
  .tw-job-count {
    font-size: 10px; min-width: 16px; height: 16px; padding: 0 4px;
    display: inline-flex; align-items: center; justify-content: center;
    border-radius: 8px; background: rgba(148,163,184,0.18); color: inherit;
  }
  /* When the lane is the toggle's primary content (not the bottom-stacked band),
     drop the top margin — the toggle already provides the gap above it. */
  .tw-expansion--view { margin-top: 0; }
  .tw-board { display: block; }
  .tw-col { display: none; }
  .tw-col.tw-col-active { display: block; }
  .tw-col-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 2px 2px 6px; position: sticky; top: 0;
  }
  .tw-col-title { font-size: 13px; font-weight: 700; color: var(--text1); letter-spacing: 0.01em; }
  .tw-col-count {
    font-size: 11px; font-weight: 600; color: var(--text3);
    background: rgba(148,163,184,0.14); border-radius: 8px; padding: 1px 8px;
  }
  .tw-col-body { display: flex; flex-direction: column; gap: 10px; }
  .tw-col-empty {
    font-size: 12px; color: var(--text3); text-align: center;
    padding: 18px 10px; border: 1px dashed var(--border, #1e2433); border-radius: 10px;
  }

  /* ── Mobile deck view — a compact, vertically-scrolling list of cards ───── */
  /* On phones the deck is the one cockpit view — it replaces the board. */
  @media (max-width: 768px) {
    .this-week-wrap .tw-board { display: none; }
    .this-week-wrap .tw-deck { display: block; }
  }
  .tw-deck-listhead {
    display: flex; align-items: baseline; gap: 8px; flex-wrap: wrap;
    font-size: 13px; font-weight: 700; color: var(--text2);
    padding: 0 2px 10px;
  }
  .tw-deck-listhead b { color: var(--text1); }
  .tw-deck-hint2 { font-size: 11px; font-weight: 500; color: var(--text3); margin-left: auto; }
  /* Each card is a swipeable face over a Move/Pass tray. */
  .tw-deck-card {
    position: relative; overflow: hidden; box-sizing: border-box; margin-bottom: 8px;
    border: 1px solid var(--border); border-radius: 12px; background: var(--surface2);
  }
  .tw-deck-swipe {
    position: relative; z-index: 2; background: var(--surface);
    padding: 10px 12px; cursor: pointer; transition: transform 0.2s ease;
    touch-action: pan-y;
  }
  .tw-deck-card.tw-deck-open .tw-deck-swipe { transform: translateX(-150px); }
  /* The revealed action tray, sitting behind the face on the right. */
  .tw-deck-actions {
    position: absolute; top: 0; right: 0; bottom: 0; width: 150px; z-index: 1;
    display: flex; align-items: stretch; gap: 6px; padding: 8px;
  }
  .tw-deck-act {
    flex: 1 1 0; min-width: 0; border-radius: 10px; border: 1px solid var(--border);
    background: var(--surface); color: var(--text1);
    display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px;
    font-family: inherit; font-size: 18px; cursor: pointer;
  }
  .tw-deck-act span { font-size: 10px; font-weight: 600; }
  .tw-deck-act-pass { color: #f87171; }
  /* Reuse the list card markup, compact; tap opens the full detail, so the card's
     own chevron/inline-detail/secondary buttons are dropped here. Call stays. */
  .tw-deck-card .tw-card {
    border: none; background: transparent; padding: 0; margin: 0; cursor: pointer;
  }
  .tw-deck-card .tw-card .tw-thumb {
    width: 64px; height: 64px; border-radius: 9px; margin: 0;
  }
  /* Aggressive, app-like dense deck card: thumb + a tight address/score line,
     owner, and the next-step signal line — with an icon-only action cluster
     (call / move / pass). Owner-ready, rationale, portfolio, and the score
     breakdown move behind the tap (tapping the card opens the full detail). */
  .tw-deck-card .tw-card { gap: 10px; }
  .tw-deck-card .tw-card .tw-thumb { width: 50px; height: 50px; border-radius: 9px; }
  .tw-deck-card .tw-thumb-wrap { width: 50px; }
  .tw-deck-card .tw-main { gap: 1px; }
  .tw-deck-card .tw-addr { font-size: 14px; white-space: nowrap; }
  .tw-deck-card .tw-owner { font-size: 12px; }
  .tw-deck-card .tw-score, .tw-deck-card .tw-chevron, .tw-deck-card .tw-detail,
  .tw-deck-card .tw-rationale-line, .tw-deck-card .tw-ready-chip { display: none !important; }
  /* Icon-only action cluster — square buttons, labels hidden (titles remain). */
  .tw-deck-card .tw-card-actions { margin-top: 6px; gap: 7px; flex-wrap: nowrap; }
  .tw-deck-card .tw-card-actions .tw-move-cta,
  .tw-deck-card .tw-card-actions .tw-pass-cta { display: inline-flex !important; }
  .tw-deck-card .tw-card-actions > button {
    width: 40px; height: 36px; padding: 0; justify-content: center; border-radius: 9px;
  }
  .tw-deck-card .tw-card-actions > button > span { display: none; }
  .tw-deck-card .tw-card-actions .cta-ico { width: 16px; height: 16px; }
  .tw-deck-card .tw-next-cta { align-self: auto; }
  .tw-deck-empty {
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    text-align: center; gap: 8px; padding: 48px 18px; min-height: 50dvh;
  }
  .tw-deck-empty-emoji { font-size: 40px; }
  .tw-deck-empty-title { font-size: 17px; font-weight: 700; color: var(--text1); }
  .tw-deck-empty-body { font-size: 13px; color: var(--text3); max-width: 320px; line-height: 1.5; }
  .tw-deck-refresh {
    margin-top: 8px; padding: 11px 20px; border-radius: 12px; cursor: pointer;
    font-family: inherit; font-size: 14px; font-weight: 700;
    color: #fff; background: var(--accent, #7c3aed); border: none;
  }

  /* Desktop: all columns side by side; hide the switcher. */
  @media (min-width: 769px) {
    .tw-deck { display: none !important; }
    .this-week-wrap { max-width: 1400px; }
    /* The action band sits on the same line as the title + job toggle (pinned
       right); the asset lens drops to its own full-width line just below, so every
       pill stays visible. This collapses what used to be separate title, toggle,
       and action rows into two tight lines. */
    .tw-filters { flex-wrap: nowrap; align-items: flex-start; }
    .tw-filters-left { flex-wrap: wrap; }
    .tw-filters-left .tw-jobtoggle { flex: 0 0 auto; flex-wrap: nowrap; }
    .tw-filters-left .tw-asset-row { flex: 1 1 100%; min-width: 0; }
    .tw-filters .tw-header-actions { align-self: flex-start; }
    .tw-switcher { display: none; }
    .tw-board {
      display: flex; gap: 14px; align-items: stretch;
      overflow-x: auto; -webkit-overflow-scrolling: touch; padding-bottom: 8px;
    }
    .tw-col {
      display: flex !important; flex-direction: column;
      flex: 1 1 0; min-width: 260px;
      background: rgba(148,163,184,0.04);
      border: 1px solid var(--border, #1e2433); border-radius: 12px;
      /* --tw-col-max is measured in JS (_sizeCockpitBoard) to fill the real space
         left below the board; the calc() is a safe fallback before that runs. */
      padding: 10px; max-height: var(--tw-col-max, calc(100dvh - 220px));
    }
    .tw-col-head { background: transparent; padding: 2px 4px 6px; }
    /* flex:1 makes the body fill the column even when sparse, so a drop
       anywhere in the visible column box (not just over the cards) hits the
       column instead of falling through to the board container. */
    .tw-col-body { flex: 1 1 auto; overflow-y: auto; min-height: 80px; }
    .tw-col-dragover { border-color: rgba(124,58,237,0.6); background: rgba(124,58,237,0.07); }
    /* Drag affordance: grab cursor + suppress text selection so a press-drag
       starts a drag instead of selecting the card's text. */
    .tw-col .tw-card { cursor: grab; user-select: none; -webkit-user-select: none; }
    .tw-col .tw-card:active { cursor: grabbing; }
    /* App-like compaction: tight cards, one glance line each. */
    .tw-col .tw-card { position: relative; padding: 7px 9px; gap: 9px; margin-bottom: 6px; }
    .tw-col .tw-main { gap: 1px; }
    .tw-col .tw-thumb { width: 40px; height: 40px; }
    /* Actions fade in on hover as a single compact row docked to the card's
       bottom edge — a solid (not see-through) footer so nothing bleeds through,
       covering only the rationale line while address / owner / next stay visible.
       Pinned in place: the card never grows and neighbours never shift. "Call from
       HubSpot" shortens to "Call" here; click the card body for the full detail. */
    .tw-col .tw-card-actions {
      position: absolute; left: 1px; right: 1px; bottom: 1px; z-index: 3;
      margin: 0; padding: 6px 8px 7px;
      display: flex; flex-wrap: nowrap; align-items: center; gap: 4px;
      border-radius: 0 0 9px 9px;
      background: var(--surface);
      opacity: 0;
      transition: opacity 0.12s ease;
    }
    /* Soft fade so the content above melts into the footer instead of a hard cut. */
    .tw-col .tw-card-actions::before {
      content: ''; position: absolute; left: 0; right: 0; top: -14px; height: 14px;
      background: linear-gradient(to top, var(--surface), transparent);
      pointer-events: none;
    }
    /* One cohesive, compact toolbar — uniform tight pills, Call as the accent
       primary, Move/Pass neutral (hover-tinted). Overrides the chunkier default
       card-button styling. */
    .tw-col .tw-card-actions > button {
      flex: 0 0 auto; margin-top: 0 !important;
      display: inline-flex; align-items: center; gap: 4px;
      font-size: 11px !important; font-weight: 600;
      padding: 3px 9px !important; border-radius: 7px !important;
      border: 1px solid var(--border) !important;
      background: var(--surface2) !important; color: var(--text2) !important;
    }
    .tw-col .tw-card-actions > button:hover { border-color: var(--border2) !important; color: var(--text) !important; }
    .tw-col .tw-card-actions .tw-next-cta { color: var(--accent) !important; border-color: var(--accent2) !important; }
    .tw-col .tw-card-actions .tw-pass-cta:hover { color: #f87171 !important; border-color: rgba(248,113,113,0.4) !important; }
    .tw-col .tw-cta-extra { display: none; }
    /* Hover (or keyboard focus) reveals the footer. Hidden state keeps
       pointer-events so the buttons stay hit-testable — on desktop the mouse must
       be over the card to reach them anyway, so they're always visible-on-reach. */
    .tw-col .tw-card:hover .tw-card-actions,
    .tw-col .tw-card:focus-within .tw-card-actions {
      opacity: 1;
    }
    /* When the card is expanded inline (chevron / score breakdown open), drop the
       actions back into flow so the footer sits below the detail instead of
       floating over its bottom edge. */
    .tw-col .tw-card-open .tw-card-actions {
      position: static; opacity: 1; padding: 8px 0 2px; margin-top: 2px;
    }
    .tw-col .tw-card-open .tw-card-actions::before { display: none; }
  }
  /* Detail block — collapsed by default, revealed by .tw-card-open. */
  .tw-detail { display: none; margin-top: 6px; flex-direction: column; gap: 6px; }
  .tw-card-open .tw-detail { display: flex; }
  /* The deep dive lives behind one explicit step, at the foot of the expanded
     detail — replacing the old "tap anywhere → full-screen modal" that made the
     card feel twitchy. Quiet link-button so it doesn't compete with Call. */
  .tw-open-record {
    align-self: flex-start; margin-top: 2px;
    padding: 4px 10px; border-radius: 7px;
    background: var(--surface2); border: 1px solid var(--border);
    color: var(--text2); font-family: inherit; font-size: 11px; font-weight: 600;
    cursor: pointer; line-height: 1.4;
    transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
  }
  .tw-open-record:hover { color: var(--accent); border-color: var(--accent2); background: var(--surface); }
  .tw-open-record:active { transform: translateY(1px); }
  /* ── Campus rollup (Signals lane) ───────────────────────────────────────── */
  /* The card is a NORMAL building card; this chip just flags it's the strongest of
     N signaling buildings on one site, and links to the whole-site report. */
  .tw-campus-line { margin-top: 1px; }
  .tw-campus-chip {
    display: inline-flex; align-items: center; gap: 4px; max-width: 100%;
    padding: 1px 8px; border-radius: 999px; cursor: pointer;
    font-family: inherit; font-size: 11px; font-weight: 600; line-height: 1.5;
    color: var(--accent); background: rgba(124,58,237,0.12);
    border: 1px solid rgba(124,58,237,0.35);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    transition: background 0.12s ease, border-color 0.12s ease;
  }
  .tw-campus-chip:hover { background: rgba(124,58,237,0.2); border-color: rgba(124,58,237,0.6); }
  /* "Choose your target" list in the expanded detail: the other signaling buildings
     on the site (click to open one), plus a separate link to the whole-site report. */
  .tw-campus-sibs { display: flex; flex-direction: column; gap: 6px; font-size: 11px; }
  .tw-campus-sibs-h { color: var(--text3); font-weight: 600; line-height: 1.4; }
  .tw-campus-sibwrap { display: flex; flex-wrap: wrap; gap: 5px; }
  .tw-campus-links { display: flex; flex-wrap: wrap; gap: 6px 14px; }
  .tw-campus-link {
    font-family: inherit; font-size: 11px; font-weight: 600; text-align: left;
    color: var(--accent); background: transparent; border: none; padding: 2px 0; cursor: pointer;
  }
  .tw-campus-link:hover { text-decoration: underline; }
  /* Board-local nav shortcut in the quick-look (owner → Table). Text-link idiom,
     matching .tw-campus-link so the expanded detail reads as one tight set. */
  .tw-detail-nav {
    align-self: flex-start;
    font-family: inherit; font-size: 11px; font-weight: 600; text-align: left;
    color: var(--accent); background: transparent; border: none; padding: 2px 0; cursor: pointer;
  }
  .tw-detail-nav:hover { text-decoration: underline; }
  .tw-campus-sib {
    font-family: inherit; font-size: 11px; cursor: pointer;
    padding: 2px 8px; border-radius: 6px; max-width: 100%;
    color: var(--text2); background: var(--surface2); border: 1px solid var(--border);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
    transition: color 0.12s ease, border-color 0.12s ease;
  }
  .tw-campus-sib:hover { color: var(--accent); border-color: var(--accent2); }
  /* Group-by-campus lens — a LABELED toggle pill (not an icon), so its state reads
     at a glance: outlined = off (every building shown); filled accent = on (grouped),
     matching the active asset-chip idiom. The label text states the mode too. */
  .tw-campus-lens {
    flex: 0 0 auto; display: inline-flex; align-items: center; gap: 5px;
    padding: 5px 11px; border-radius: 999px; cursor: pointer;
    font-family: inherit; font-size: 12px; font-weight: 600; white-space: nowrap;
    color: var(--text2); background: transparent; border: 1px solid var(--border);
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
  }
  .tw-campus-lens:hover { color: var(--text); border-color: var(--accent2); }
  .tw-campus-lens.tw-campus-lens-on {
    color: #fff; background: var(--accent2); border-color: var(--accent2);
  }
  /* Header dev toggle — subtle gear, lights up when active. */
  .tw-header-actions { display: flex; align-items: center; gap: 8px; flex-shrink: 0; }
  /* Compact icon actions (Forward / Add / Passed / Customers) — icon-only with
     a tooltip, to keep the BD toolbar to one tight row. Count badges retain the
     numbers the labels used to carry. Matches the 30px toggle geometry. */
  .tw-iconbtn {
    position: relative; width: 30px; height: 30px; flex-shrink: 0;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--surface2); border: 1px solid var(--border);
    border-radius: 7px; cursor: pointer; font-size: 15px; line-height: 1;
    color: var(--text2); font-family: inherit; padding: 0;
    transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
  }
  .tw-iconbtn:hover { color: var(--text1); border-color: var(--accent2); }
  .tw-iconbtn:active { transform: translateY(1px); }
  .tw-iconbtn .tw-badge {
    position: absolute; top: -5px; right: -5px; min-width: 14px; height: 14px;
    padding: 0 3px; border-radius: 7px; background: var(--text3); color: var(--surface);
    font-size: 9px; font-weight: 800; line-height: 14px; text-align: center;
    border: 1.5px solid var(--surface); box-sizing: content-box;
  }
  .tw-iconbtn .tw-badge--cust { background: #34d399; color: #06281d; }
  .tw-dev-toggle {
    width: 30px; height: 30px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    background: var(--surface2); border: 1px solid var(--border);
    border-radius: 7px; cursor: pointer; font-size: 14px;
    color: var(--text3); opacity: 0.5;
    transition: opacity 0.12s ease, color 0.12s ease, border-color 0.12s ease;
  }
  .tw-dev-toggle:hover { opacity: 1; }
  /* Owner engagement-readiness blend toggle (Phase 4) — mirrors the dev toggle. */
  .tw-readiness-toggle {
    width: 30px; height: 30px; flex-shrink: 0;
    display: flex; align-items: center; justify-content: center;
    background: var(--surface2); border: 1px solid var(--border);
    border-radius: 7px; cursor: pointer; font-size: 14px;
    color: var(--text3); opacity: 0.5;
    transition: opacity 0.12s ease, color 0.12s ease, border-color 0.12s ease;
  }
  .tw-readiness-toggle:hover { opacity: 1; }
  /* On-state for the ⚡ readiness lens and ⚙ dev toggles — these are emoji
     glyphs (color: won't recolor them), so make "on" unmistakable with a
     filled accent tint, a solid accent border, and a soft ring. */
  .tw-readiness-toggle.tw-readiness-on,
  .tw-dev-toggle.tw-dev-on {
    opacity: 1; color: var(--accent);
    background: var(--accent-dim2); border-color: var(--accent);
    box-shadow: 0 0 0 2px var(--accent-dim);
  }
  /* Customer-suppression toggle (Ring-3 feed) — only shown when the feed is live. */
  .tw-customer-toggle {
    height: 30px; flex-shrink: 0; padding: 0 10px;
    display: inline-flex; align-items: center; gap: 4px;
    background: var(--surface2); border: 1px solid var(--border);
    border-radius: 7px; cursor: pointer; font-size: 11px; font-weight: 600;
    font-family: inherit; color: var(--text2); opacity: 1;
    transition: opacity 0.12s ease, color 0.12s ease, border-color 0.12s ease;
  }
  .tw-customer-toggle:hover { color: var(--text1); border-color: var(--accent2); }
  .tw-customer-toggle.tw-customer-on { opacity: 1; color: #34d399; border-color: rgba(52,211,153,0.5); }
  /* ⋯ More menu — folds the advanced/dev toggles out of the BD toolbar (native
     <details> disclosure, no JS). Items keep .tw-readiness-toggle/.tw-dev-toggle
     as hooks; these rules (defined after) override the icon-button geometry. */
  .tw-more { position: relative; display: inline-block; flex-shrink: 0; }
  /* ⋯ More — a real, full-strength control (it was muted to opacity 0.6 +
     --text3, so it read as decoration). Solid neutral fill + the same --border2
     its sibling pills use, sized to sit level with them, with prominent dots. */
  .tw-more-btn {
    width: 38px; height: 32px; box-sizing: border-box; list-style: none;
    display: inline-flex; align-items: center; justify-content: center;
    background: var(--surface2); border: 1px solid var(--border2);
    border-radius: 7px; cursor: pointer; font-size: 19px; line-height: 1;
    color: var(--text2); letter-spacing: 1px;
    transition: color 0.12s ease, border-color 0.12s ease, background 0.12s ease;
  }
  .tw-more-btn::-webkit-details-marker { display: none; }
  .tw-more-btn:hover { color: var(--text1); border-color: var(--text3); background: rgba(148,163,184,0.14); }
  .tw-more[open] .tw-more-btn { color: var(--text1); border-color: var(--accent2); background: rgba(124,58,237,0.10); }
  .tw-more-menu {
    position: absolute; right: 0; top: calc(100% + 4px); z-index: 40;
    display: flex; flex-direction: column; gap: 2px; min-width: 170px;
    background: var(--surface2); border: 1px solid var(--border2);
    border-radius: 8px; padding: 4px; box-shadow: 0 10px 28px rgba(0,0,0,0.45);
  }
  /* Override the icon-button base styling for the menu-row presentation. */
  .tw-more-item {
    width: 100%; height: auto; justify-content: flex-start; text-align: left;
    gap: 8px; padding: 8px 10px; border-radius: 6px; border: 1px solid transparent;
    background: transparent; color: var(--text2); opacity: 1;
    font-size: 12px; font-weight: 600; font-family: inherit; white-space: nowrap; cursor: pointer;
  }
  .tw-more-item:hover { background: rgba(148,163,184,0.12); color: var(--text1); border-color: transparent; }
  .tw-more-item.tw-readiness-on, .tw-more-item.tw-dev-on { color: var(--accent); border-color: transparent; }
  /* Expansion / retain lane — the existing-customer job, below the board. */
  .tw-expansion { margin-top: 18px; border: 1px solid rgba(52,211,153,0.28); border-radius: 10px; background: rgba(52,211,153,0.04); overflow: hidden; }
  .tw-exp-head { display: flex; flex-wrap: wrap; align-items: baseline; gap: 8px; padding: 10px 14px; border-bottom: 1px solid rgba(52,211,153,0.18); }
  .tw-exp-title { font-weight: 700; color: #34d399; font-size: 13px; }
  .tw-exp-sub { color: var(--text3); font-size: 11px; }
  .tw-exp-body { display: flex; flex-direction: column; }
  /* owner (flex) | signal (hugs) | meter (fixed). The fixed meter column flush
     right is what makes every row's bar line up vertically. */
  .tw-exp-row { display: grid; grid-template-columns: minmax(0,1fr) auto 116px; align-items: center; gap: 10px; padding: 8px 14px; cursor: pointer; border-bottom: 1px solid var(--border); font-size: 12px; }
  .tw-exp-row:last-child { border-bottom: none; }
  .tw-exp-row:hover { background: var(--surface2); }
  .tw-exp-owner { display: inline-flex; align-items: center; gap: 6px; font-weight: 600; min-width: 0; flex: 1; }
  .tw-exp-owner span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .tw-exp-type { font-size: 9px; font-weight: 700; padding: 1px 5px; border-radius: 3px; background: rgba(52,211,153,0.15); color: #34d399; border: 1px solid rgba(52,211,153,0.4); flex-shrink: 0; }
  .tw-exp-count { color: var(--text3); font-size: 11px; white-space: nowrap; flex-shrink: 0; }
  /* Live (serviced now) vs expansion-opportunity (whitespace) split, per row.
     Colours reuse the ✓ Customer green / ◇ EXP violet used elsewhere. */
  .tw-exp-meter { display: flex; flex-direction: column; gap: 3px; align-items: stretch; width: 116px; }
  .tw-exp-split { font-family: var(--font-mono); font-variant-numeric: tabular-nums; font-size: 10px; color: var(--text3); white-space: nowrap; text-align: right; }
  .tw-exp-split .tw-exp-live { color: #34d399; font-weight: 700; }
  .tw-exp-split .tw-exp-opp { color: #a78bfa; font-weight: 700; }
  .tw-exp-bar { width: 100%; height: 5px; border-radius: 3px; overflow: hidden; display: flex; background: var(--surface2); }
  .tw-exp-bar .tw-exp-bl { background: #34d399; }
  .tw-exp-bar .tw-exp-bo { background: #a78bfa; }
  .tw-exp-ready { font-family: var(--font-mono); font-weight: 600; font-size: 10px; padding: 2px 8px; border-radius: 4px; border: 1px solid rgba(167,139,250,0.45); background: rgba(167,139,250,0.10); color: #a78bfa; white-space: nowrap; max-width: 168px; overflow: hidden; text-overflow: ellipsis; }
  .tw-exp-quiet { color: var(--text3); font-size: 13px; line-height: 1; opacity: 0.4; white-space: nowrap; }
  .tw-exp-ready[role="button"] { cursor: pointer; }
  .tw-exp-ready[role="button"]:hover { filter: brightness(1.12); }
  /* Signal drill-down panel — sources behind an owner's readiness + Ask Birdie. */
  .tw-sig-modal { position: fixed; inset: 0; z-index: 100001; background: rgba(0,0,0,0.55); display: flex; align-items: center; justify-content: center; padding: 16px; }
  .tw-sig-panel { background: var(--surface, #0f1117); border: 1px solid var(--border, #1e2433); border-radius: 14px; padding: 16px; max-width: 460px; width: 100%; box-shadow: 0 14px 44px rgba(0,0,0,0.5); max-height: 85vh; overflow: auto; display: flex; flex-direction: column; gap: 12px; }
  .tw-sig-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 10px; }
  .tw-sig-owner { display: flex; align-items: center; gap: 8px; font-size: 15px; font-weight: 700; color: var(--text1); }
  .tw-sig-score { font-family: var(--font-mono); font-size: 11px; margin-top: 3px; color: var(--text2); }
  .tw-sig-x { background: transparent; border: 1px solid var(--border); color: var(--text3); width: 28px; height: 28px; border-radius: 8px; cursor: pointer; flex-shrink: 0; font-size: 13px; }
  .tw-sig-x:hover { color: var(--text1); }
  .tw-sig-birdie { display: inline-flex; align-items: center; justify-content: center; gap: 6px; padding: 9px 12px; border-radius: 9px; border: 1px solid var(--accent); background: var(--accent-dim); color: var(--accent); font-size: 13px; font-weight: 600; font-family: inherit; cursor: pointer; }
  .tw-sig-birdie:hover { filter: brightness(1.06); }
  .tw-sig-srctitle { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: 0.04em; color: var(--text3); }
  .tw-sig-srclist { display: flex; flex-direction: column; }
  .tw-sig-src { display: flex; align-items: flex-start; gap: 10px; padding: 9px 2px; border-top: 1px solid var(--border); }
  .tw-sig-src:first-child { border-top: none; }
  .tw-sig-srcmain { flex: 1; min-width: 0; }
  .tw-sig-why { font-size: 13px; color: var(--text1); line-height: 1.35; }
  .tw-sig-title { font-size: 11px; color: var(--text2); margin-top: 2px; }
  .tw-sig-meta { font-size: 10px; color: var(--text3); margin-top: 3px; font-family: var(--font-mono); }
  .tw-sig-link { font-size: 11px; color: #a78bfa; white-space: nowrap; flex-shrink: 0; text-decoration: none; }
  .tw-sig-link:hover { text-decoration: underline; }
  .tw-sig-empty { font-size: 12px; color: var(--text3); padding: 8px 2px; line-height: 1.4; }
  /* Cockpit asset-class lens — chip row under the header. Focuses the Signals
     queue on one building type; "All" clears it. */
  .tw-asset-row { display: flex; flex-wrap: wrap; gap: 6px; margin: 2px 0 12px; }
  .tw-asset-chip {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 11px; font-weight: 600; font-family: inherit;
    padding: 3px 9px; border-radius: 13px; cursor: pointer;
    background: var(--surface2); border: 1px solid var(--border); color: var(--text2);
    transition: background 0.12s ease, color 0.12s ease, border-color 0.12s ease;
  }
  .tw-asset-chip:hover { color: var(--text); border-color: var(--accent2); }
  .tw-asset-chip.tw-asset-on { background: var(--accent2); border-color: var(--accent2); color: #fff; }
  .tw-asset-n {
    font-size: 9px; font-variant-numeric: tabular-nums; font-weight: 700;
    background: rgba(148,163,184,0.18); color: var(--text3);
    padding: 0 5px; border-radius: 8px;
  }
  .tw-asset-chip.tw-asset-on .tw-asset-n { background: rgba(255,255,255,0.25); color: #fff; }

  /* Mobile density (Rob, 2026-06-07): on phones the Cockpit's header/controls
     stack ate most of the first screen before any card. Compact it — drop the
     redundant subtitle (the switcher tabs already name the columns), single-line
     the asset chips (scroll, don't wrap into a second row), and tighten margins. */
  @media (max-width: 768px) {
    /* Stack header into a column so the actions row gets full viewport width and
       can wrap — otherwise it shrink-wraps wider than the screen and gets cut off. */
    .tw-header { margin-bottom: 8px; gap: 6px; flex-direction: column; align-items: stretch; }
    .tw-title { font-size: 16px; }
    .tw-sub { display: none; }
    .tw-freshness { margin-top: 3px; }
    .tw-header-actions { gap: 6px; flex-wrap: wrap; width: 100%; }
    .tw-asset-row { flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; margin: 0 0 8px; padding-bottom: 2px; }
    .tw-asset-row::-webkit-scrollbar { display: none; }
    .tw-asset-chip { flex: 0 0 auto; }
    /* Keep ⓘ + Prospect/Expansion + the campus lens on ONE line — scroll sideways
       rather than wrapping the campus lens to its own row. */
    .tw-lensrow { width: 100%; flex-wrap: nowrap; overflow-x: auto; -webkit-overflow-scrolling: touch; margin-bottom: 8px; padding-bottom: 2px; }
    .tw-lensrow::-webkit-scrollbar { display: none; }
    .tw-lensrow > * { flex: 0 0 auto; }
    .tw-switcher { margin-bottom: 8px; }
  }

  .tw-dev-meta { font-size: 10px; font-family: var(--font-mono); color: var(--text3); }
  .tw-owner {
    font-size: 11px;
    color: var(--text2);
    display: flex;
    align-items: center;
    gap: 6px;
  }
  .tw-owner img {
    flex-shrink: 0;
    object-fit: contain;
    background: #f8f9fa; /* light bg so dark-on-transparent logos stay readable on the card surface */
  }
  /* Signal badge on its own line below the owner — moved off the owner line so a
     long owner name no longer collides with the chip. Left-aligned, no wrap. */
  .tw-signal-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 6px;
    margin-top: 3px;
  }
  /* Portfolio rail (Option A) — purple matches the existing portfolio-report
     affordance in the table view so the "owner footprint" cue is consistent. */
  .tw-kind { font-size: 11px; color: var(--accent); font-family: var(--font-mono); margin-top: 4px; letter-spacing: 0.02em; }
  .tw-reasoning { font-size: 12px; color: var(--text2); font-style: italic; margin-top: 2px; }
  .tw-meta { margin-top: 6px; display: flex; gap: 6px; flex-wrap: wrap; }
  /* Ring 2 news CTA on queue cards (issue #48 PR-C). A two-line callout so a rep
     can judge relevance before clicking: a "📰 event · $ · recency · Tier-1" meta
     line over the actual headline. Accent left-border marks it actionable. */
  .tw-news-link {
    display: flex;
    flex-direction: column;
    gap: 3px;
    text-decoration: none;
    background: var(--accent-dim);
    border: 1px solid var(--accent-dim2);
    border-left: 3px solid var(--accent);
    border-radius: 6px;
    padding: 6px 9px;
  }
  .tw-news-link:hover { background: var(--accent-dim2); }
  .tw-news-meta {
    display: flex;
    align-items: baseline;
    gap: 8px;
    font-size: 10px;
    font-family: var(--font-mono);
    color: var(--accent);
    letter-spacing: 0.02em;
  }
  .tw-news-arrow { margin-left: auto; flex: 0 0 auto; font-weight: 700; }
  .tw-news-title {
    font-size: 12px;
    font-weight: 600;
    color: var(--text);
    line-height: 1.3;
  }

  /* "Next" CTA block — the answer to "what should BD do with this card?".
     Sits at the top of the card body, just under the address/owner, so
     it's the first thing the eye lands on. The tone variant (set by
     _nextAction in js/app.js) controls the left border + text color:
     SQL gets gold to flag the funnel-handoff moment; won/lost are
     dimmed; everything else is the default accent. */
  .tw-next {
    margin-top: 6px;
    margin-bottom: 2px;
    padding: 7px 10px;
    background: var(--surface2);
    border-left: 3px solid var(--accent);
    border-radius: 4px;
    display: flex;
    flex-direction: column;
    gap: 1px;
  }
  .tw-next-label {
    font-size: 9px;
    font-family: var(--font-mono);
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--text3);
    font-weight: 700;
  }
  .tw-next-action {
    font-size: 13px;
    font-weight: 600;
    color: var(--text1);
    line-height: 1.3;
  }
  .tw-next-cta {
    align-self: flex-start;
    margin-top: 6px;
    padding: 4px 10px;
    font-size: 11px;
    font-weight: 600;
    font-family: inherit;
    color: var(--accent);
    background: rgba(124,58,237,0.12);
    border: 1px solid rgba(124,58,237,0.35);
    border-radius: 5px;
    cursor: pointer;
    line-height: 1.4;
    transition: background 0.12s ease, border-color 0.12s ease;
  }
  .tw-next-cta:hover { background: rgba(124,58,237,0.22); border-color: rgba(124,58,237,0.6); }
  .tw-next-cta:active { transform: translateY(1px); }
  .tw-next.tw-tone-sql      { border-left-color: #fbbf24; background: rgba(251,191,36,0.06); }
  .tw-next.tw-tone-sql      .tw-next-action  { color: #fcd34d; }
  .tw-next.tw-tone-sql      .tw-next-label   { color: #fbbf24; }
  .tw-next.tw-tone-won      { border-left-color: #22c55e; background: rgba(34,197,94,0.06); }
  .tw-next.tw-tone-won      .tw-next-action  { color: #4ade80; }
  .tw-next.tw-tone-lost     { border-left-color: #6b7280; opacity: 0.7; }
  .tw-next.tw-tone-close    { border-left-color: #f87171; }
  .tw-next.tw-tone-proposal { border-left-color: #38bdf8; }
  .tw-next.tw-tone-qualify  { border-left-color: #a78bfa; }
  .tw-next.tw-tone-followup { border-left-color: #fb923c; }
  .tw-next.tw-tone-research { border-left-color: #60a5fa; }
  /* Default (outreach) keeps the accent green */

  /* Disclosure that hides the technical Ring-2 label + score pips.
     Native <details>/<summary> for zero-JS interaction. The summary line
     is mono-styled to read as a system / diagnostic footer; the actual
     pips render in their full layout when expanded. stopPropagation on
     the <details> element keeps the card-click → openModal handler from
     firing when BD clicks the disclosure caret. */
  .tw-details {
    margin-top: 8px;
  }
  .tw-details > summary {
    cursor: pointer;
    list-style: none;
    color: var(--text3);
    font-family: var(--font-mono);
    font-size: 10px;
    letter-spacing: 0.04em;
    padding: 2px 0;
    user-select: none;
    display: inline-flex;
    align-items: center;
    gap: 4px;
  }
  .tw-details > summary::-webkit-details-marker { display: none; }
  .tw-details > summary::before {
    content: '▸';
    display: inline-block;
    font-size: 9px;
    transition: transform 0.15s;
    color: var(--text3);
  }
  .tw-details[open] > summary::before {
    transform: rotate(90deg);
  }
  .tw-details > summary:hover { color: var(--text2); }

  .tw-score {
    flex: 0 0 auto;
    width: 58px;
    border-radius: 8px;
    background: var(--accent-dim2);
    color: var(--accent);
    font-family: var(--font-mono);
    font-size: 30px;
    font-weight: 700;
    display: flex; align-items: center; justify-content: center;
    border: 1px solid rgba(240, 75, 36,0.3);
  }

  /* Score-breakdown pips — six small bars under each card showing each
     component's contribution. Each pip is its own segment with a fill
     bar (proportional to score/max), an UPPERCASE label, and the raw
     score number on the right. High-contrast white labels with a dark
     glow read cleanly over both the empty surface AND the green fill. */
  /* Rank breakdown — a single stacked bar (part-to-whole of the composite
     score) over a colour-keyed callout legend. Header names the total. */
  .tw-scorebd { margin-top: 10px; display: flex; flex-direction: column; gap: 7px; }
  .tw-scorebd-h {
    font-size: 11px; font-weight: 600; letter-spacing: 0.02em; color: var(--text3);
  }
  .tw-scorebd-h b { color: var(--text); font-weight: 700; }
  /* The bar: proportional flex segments, a 2px surface gap between fills, and
     4px rounded ends via the clipped track. */
  .tw-scorebar {
    display: flex;
    gap: 2px;
    height: 14px;
    border-radius: 4px;
    overflow: hidden;
    background: var(--surface2);
    border: 1px solid var(--border);
  }
  .tw-scoreseg {
    flex: 1 1 0;
    min-width: 5px;
    cursor: help;
    transition: filter 0.12s ease;
  }
  .tw-scoreseg:hover { filter: brightness(1.15); }
  /* Callout legend — colour dot + family name + points. Wraps tight. */
  .tw-scorelegend { display: flex; flex-wrap: wrap; gap: 4px 12px; }
  .tw-scorekey {
    display: inline-flex; align-items: center; gap: 5px;
    font-size: 11px; color: var(--text2); cursor: help;
  }
  .tw-scoredot { width: 9px; height: 9px; border-radius: 2px; flex: 0 0 auto; }
  .tw-scorekey-label { white-space: nowrap; }
  .tw-scorekey-val { font-family: var(--font-mono); font-weight: 700; color: var(--text); }
  /* One fixed hue per family — segment fill + legend dot share the token. */
  .tw-scoreseg[data-fam="buildingtech"], .tw-scoredot[data-fam="buildingtech"] { background: var(--score-buildingtech); }
  .tw-scoreseg[data-fam="portfolio"],    .tw-scoredot[data-fam="portfolio"]    { background: var(--score-portfolio); }
  .tw-scoreseg[data-fam="bd"],           .tw-scoredot[data-fam="bd"]           { background: var(--score-bd); }
  .tw-scoreseg[data-fam="signal"],       .tw-scoredot[data-fam="signal"]       { background: var(--score-signal); }
  .tw-scoreseg[data-fam="code"],         .tw-scoredot[data-fam="code"]         { background: var(--score-code); }
  .tw-scoreseg[data-fam="readiness"],    .tw-scoredot[data-fam="readiness"]    { background: var(--score-readiness); }

  /* ── Compact reason chips (.tw-rchip) ─────────────────────────────────────
     One shape for every reason flag (code-enforcement, building-tech permit,
     customer / expand / churn, owner-readiness): a monochrome icon + a 3–4 char
     token. flex-shrink:0 sizes each to its content so it NEVER clips on the
     owner line — the owner name (min-width:0 + ellipsis, above) yields instead.
     Colour carries severity; the icon inherits it (currentColor). Full phrase in
     the tooltip + the ⌄ expand. */
  .tw-rchip {
    display: inline-flex; align-items: center; gap: 4px; flex-shrink: 0;
    font-family: var(--font-mono); font-size: 10.5px; font-weight: 700;
    letter-spacing: 0.02em; line-height: 1.2;
    padding: 2px 7px; border-radius: 4px;
    border: 1px solid var(--border); background: var(--surface2); color: var(--text2);
    white-space: nowrap;
  }
  /* Symbol-friendly family for the glyph (mono fonts often lack these); the
     U+FE0E in the markup keeps it a flat single-colour glyph that takes the
     chip's colour rather than painting its own emoji hue. */
  .tw-rchip-ico { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI Symbol", sans-serif; font-weight: 400; font-size: 11px; }
  .tw-rchip-tok { font-variant-numeric: tabular-nums; }
  .tw-rchip.r-red    { color: #fca5a5; border-color: color-mix(in srgb, var(--red) 55%, transparent);   background: color-mix(in srgb, var(--red) 16%, transparent); }
  .tw-rchip.r-amber  { color: #fcd34d; border-color: color-mix(in srgb, var(--amber) 55%, transparent); background: color-mix(in srgb, var(--amber) 16%, transparent); }
  .tw-rchip.r-green  { color: #86efac; border-color: color-mix(in srgb, var(--green) 55%, transparent); background: color-mix(in srgb, var(--green) 16%, transparent); }
  .tw-rchip.r-violet { color: #c4b5fd; border-color: rgba(167,139,250,0.55); background: rgba(167,139,250,0.16); }
  .tw-rchip.r-muted  { color: var(--text2); border-color: var(--border2); background: var(--surface2); }
  body.light-mode .tw-rchip.r-red    { color: #b91c1c; }
  body.light-mode .tw-rchip.r-amber  { color: #92400e; }
  body.light-mode .tw-rchip.r-green  { color: #15803d; }
  body.light-mode .tw-rchip.r-violet { color: #6d28d9; }

  /* Weekly "N moved to SQL" momentum banner (#54) — the funnel-goal streak. */
  .tw-sql-progress {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-bottom: 12px;
    padding: 8px 12px;
    border-radius: 6px;
    border: 1px solid rgba(251,191,36,0.45);
    background: linear-gradient(90deg, rgba(251,191,36,0.14), rgba(251,191,36,0.04));
    color: #fcd34d;
    font-size: 13px;
  }
  .tw-sql-progress-flame { font-size: 15px; line-height: 1; }
  .tw-sql-progress-text strong { font-size: 15px; font-weight: 800; }

  /* Demoted card styling for Active Lead / MQL tier — visually dimmed +
     orange left border so the rep reads it as "enrichment context, not
     fresh prospecting." */
  .tw-card-demoted {
    background: var(--surface2);
    opacity: 0.78;
    border-left: 3px solid #fb923c;
  }
  .tw-card-demoted .tw-score {
    background: var(--surface);
    color: var(--text3);
    border-color: var(--border);
  }
  .tw-card-demoted .tw-addr { color: var(--text2); }

  /* HubSpot relationship indicator row — sits between reasoning and meta. */
  .tw-rel {
    margin-top: 8px;
    padding: 6px 10px;
    border-radius: 6px;
    background: var(--surface2);
    border: 1px solid var(--border);
    font-size: 12px;
    display: flex;
    align-items: flex-start;
    gap: 8px;
  }
  .tw-rel-dot { flex: 0 0 auto; font-size: 14px; line-height: 1.2; }
  .tw-rel-text { color: var(--text2); line-height: 1.4; }
  .tw-rel-known        { border-color: rgba(74,222,128,0.3); background: rgba(74,222,128,0.05); } /* semantic "known" — stays green */
  .tw-rel-known .tw-rel-text  { color: var(--text1); }
  .tw-rel-company      { border-color: rgba(251,191,36,0.3); background: rgba(251,191,36,0.05); }
  .tw-rel-needs-research { border-color: rgba(96,165,250,0.3); background: rgba(96,165,250,0.05); }
  .tw-rel-pending      { opacity: 0.6; }
  .tw-rel-research { flex: 1 1 auto; }
  .tw-rel-cta-row {
    margin-top: 6px;
    display: flex; gap: 6px; flex-wrap: wrap;
  }
  .tw-rel-cta {
    font-family: var(--font-mono);
    font-size: 11px;
    padding: 3px 8px;
    border-radius: 4px;
    border: 1px solid var(--border);
    background: var(--surface);
    color: var(--text2);
    text-decoration: none;
  }
  .tw-rel-cta:hover { color: var(--accent); border-color: var(--accent2); }
  .tw-rel-cta-primary { color: var(--accent); border-color: rgba(240, 75, 36,0.4); }

  @media (max-width: 720px) {
    .this-week-wrap { padding: 12px 14px 28px; }
    .tw-score { width: 52px; font-size: 26px; }
    .tw-card { padding: 9px 11px; gap: 10px; }
    .tw-rel-cta-row { gap: 4px; }
    .tw-rel-cta { font-size: 10px; padding: 3px 6px; }
  }

  /* MINI MAP + STREET VIEW + SATELLITE in modal */
  /* Compact hero so ownership + signals are visible on load; tap for fullscreen. */
  #buildingPhotoWrap, #buildingSatWrap {
    height: 104px; border-bottom: 1px solid var(--border);
    cursor: pointer;
  }
  #miniMapWrap {
    height: 104px;
    border-bottom: 1px solid var(--border);
    position: relative;
    overflow: hidden;
    background: var(--surface2);
    flex-shrink: 0;
    cursor: pointer;
  }
  /* Mobile: the photo is "nice to see", not the BD substance — collapse it to a
     slim peek by default so the Signal / Owner / Contact / Next-action gates rise
     to the top. Tap the strip to open the fullscreen (pinch/pan) view. */
  @media (max-width: 768px) {
    #buildingPhotoWrap, #buildingSatWrap, #miniMapWrap { height: 60px; }
    /* On the slim strip the stacked Sat/Map toggles would swamp it — switching
       views now happens in the fullscreen overlay, so hide them inline on mobile. */
    #svToggleBtns, #satToggleBtns, #mapToggleBtns { display: none !important; }
    /* Tapping is the natural gesture on a phone, so the "Expand" hint is just
       clutter here — hide it on mobile, keep it on desktop where there's no
       obvious tap affordance. */
    .hero-expand-hint { display: none; }
  }
  #miniMapWrap.no-coords {
    display: flex;
    align-items: center;
    justify-content: center;
    height: 48px;
  }
  /* Hide the expand hint when there's no location to open fullscreen on. */
  #miniMapWrap.no-coords .hero-expand-hint { display: none; }
  /* Tap-for-fullscreen hint (bottom-right of the photo/satellite/map hero). */
  .hero-expand-hint {
    position: absolute; bottom: 8px; right: 8px; z-index: 1002;
    display: inline-flex; align-items: center; gap: 5px;
    background: rgba(0,0,0,0.55); color: #fff;
    border-radius: 8px; padding: 5px 9px;
    font-size: 12px; font-weight: 600;
    pointer-events: none; backdrop-filter: blur(4px);
  }
  #mini-map { height: 100%; width: 100%; }

  /* ── Fullscreen hero overlay (pinch/pan/zoom map + imagery) ──────────────── */
  #heroFs {
    position: fixed; inset: 0; z-index: 4000;
    display: none; background: var(--bg, #0b0e14);
  }
  #heroFs.open { display: block; }
  #heroFsMap { position: absolute; inset: 0; width: 100%; height: 100%; background: var(--surface2); }
  /* Drop the Leaflet zoom control below the close button (both top-right). */
  #heroFs .leaflet-top.leaflet-right { top: 64px; }
  #heroFs .leaflet-control-zoom a {
    width: 36px; height: 36px; line-height: 36px; font-size: 20px;
    background: rgba(0,0,0,0.62); color: #fff; border: 1px solid rgba(255,255,255,0.18);
  }
  #heroFs .leaflet-control-zoom a:hover { background: rgba(0,0,0,0.8); }
  #heroFs .leaflet-control-attribution {
    background: rgba(0,0,0,0.5); color: rgba(255,255,255,0.7);
  }
  #heroFs .leaflet-control-attribution a { color: rgba(255,255,255,0.85); }
  .hero-fs-close {
    position: absolute; top: max(12px, env(safe-area-inset-top)); right: 12px; z-index: 4002;
    width: 44px; height: 44px; border-radius: 50%;
    display: inline-flex; align-items: center; justify-content: center;
    background: rgba(0,0,0,0.62); color: #fff; border: 1px solid rgba(255,255,255,0.18);
    font-size: 20px; line-height: 1; cursor: pointer; backdrop-filter: blur(4px);
  }
  .hero-fs-close:hover { background: rgba(0,0,0,0.8); }
  .hero-fs-toggle {
    position: absolute; top: max(12px, env(safe-area-inset-top)); left: 12px; z-index: 4002;
    display: inline-flex; gap: 6px;
  }
  .hero-fs-toggle button {
    min-height: 44px; padding: 8px 14px; border-radius: 10px;
    background: rgba(0,0,0,0.55); color: #fff; border: 1px solid rgba(255,255,255,0.14);
    font-size: 13px; font-family: var(--font-mono); cursor: pointer; backdrop-filter: blur(4px);
  }
  .hero-fs-toggle button:hover { background: rgba(0,0,0,0.72); }
  .hero-fs-toggle button.is-active {
    background: var(--accent); color: #fff; border-color: var(--accent);
  }
  .mini-map-label {
    position: absolute;
    bottom: 0; left: 0; right: 0;
    padding: 6px 12px;
    background: linear-gradient(transparent, rgba(0,0,0,0.7));
    font-size: 11px;
    color: rgba(255,255,255,0.7);
    font-family: var(--font-mono);
    pointer-events: none;
    z-index: 1000;
  }
  /* Address autocomplete dropdown */
  #addrSuggestions .addr-suggestion-item:first-child { border-radius: 8px 8px 0 0; }
  #addrSuggestions .addr-suggestion-item:last-child  { border-radius: 0 0 8px 8px; border-bottom: none; }
  #addrSuggestions .addr-suggestion-item:only-child  { border-radius: 8px; }

  /* Company typeahead dropdown (GP + LP fields) */
  .company-suggestions {
    display: none;
    position: absolute;
    top: 100%;
    left: 0; right: 0;
    background: var(--surface);
    border: 1px solid var(--border);
    border-radius: 8px;
    margin-top: 4px;
    z-index: 2100;
    box-shadow: 0 8px 24px rgba(0,0,0,0.3);
    overflow: hidden;
  }
  .company-sug-item {
    padding: 9px 13px;
    cursor: pointer;
    font-size: 13px;
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: 8px;
  }
  .company-sug-item:last-child { border-bottom: none; }
  .company-sug-item:hover { background: var(--surface2); }
  .company-sug-item.not-in-hs {
    color: var(--text3);
    font-style: italic;
    font-size: 12px;
  }
  .hs-badge {
    font-size: 10px;
    font-weight: 700;
    background: rgba(255,122,89,0.15);
    color: #ff7a59;
    border: 1px solid rgba(255,122,89,0.3);
    border-radius: 3px;
    padding: 1px 5px;
    flex-shrink: 0;
    font-family: var(--font-mono);
  }

  .mini-map-no-data {
    font-size: 11px;
    color: var(--text3);
    font-family: var(--font-mono);
  }

  /* ── NEAR ME BUTTON ────────────────────────────────────── */
  /* Lives in the page header as an icon-btn now (location-pin SVG). The
     active state tints with the accent so users can see at a glance that
     the location filter is engaged. While the geolocation API resolves,
     `.is-locating` spins the pin in place — state is class-driven so
     JS never has to touch the SVG. */
  /* Near Me is the field-BD entry point — keep it accent-tinted at rest so it
     stands out from the neutral icon cluster, and a solid accent fill when
     engaged. (No longer hidden behind the ⋯ overflow.) */
  .near-me-btn {
    background: var(--accent-dim2);
    color: var(--accent);
    border-color: rgba(240, 75, 36, 0.4);
  }
  .near-me-btn:hover {
    background: var(--accent-dim);
    color: var(--accent);
    border-color: rgba(240, 75, 36, 0.6);
  }
  .near-me-btn.active {
    background: var(--accent);
    color: #fff;
    border-color: var(--accent);
  }
  .near-me-btn.is-locating svg { animation: near-me-spin 1s linear infinite; }
  @keyframes near-me-spin { to { transform: rotate(360deg); } }

  /* ── NEAR ME FALLBACK ADDRESS INPUT ────────────────────── */
  .near-me-fallback {
    display: flex; flex-direction: column; gap: 7px;
    padding: 10px 24px 12px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    animation: fadeSlideDown 0.18s ease;
  }
  .near-me-fallback-row {
    display: flex; align-items: center; gap: 6px;
  }
  .near-me-fallback-label {
    font-size: 11px; color: var(--text3); font-family: var(--font-mono);
    white-space: nowrap; flex-shrink: 0;
  }
  .near-me-fallback-input {
    flex: 1; background: var(--surface2); border: 1px solid var(--border);
    border-radius: 6px; padding: 7px 10px; font-size: 13px;
    color: var(--text); font-family: var(--font-main); outline: none;
    transition: border-color 0.15s; min-width: 0;
  }
  .near-me-fallback-input:focus { border-color: var(--accent); }
  .near-me-fallback-input::placeholder { color: var(--text3); }

  /* iOS Safari auto-zooms into any focused text input whose computed
     font-size is below 16px, then doesn't auto-zoom back out — the page
     ends up stuck cropped (most visible when tapping the Owner/developer
     filter or the modal form). Bump every user-visible text input to the
     16px threshold on phones. Desktop keeps the existing 13px since
     auto-zoom only applies to touch-zoomable viewports. */
  @media (max-width: 640px) {
    .omnibox-input,
    .search-input,
    .map-search-input,
    .owner-search-input,
    .near-me-fallback-input,
    .form-input,
    .form-select,
    .form-textarea {
      font-size: 16px;
    }
  }
  /* Collapsible tip */
  .near-me-tip { border-top: 1px solid var(--border); padding-top: 7px; margin-top: 2px; }
  .near-me-tip-toggle {
    background: none; border: none; cursor: pointer;
    font-size: 11px; color: var(--text3); font-family: var(--font-mono);
    padding: 0; transition: color 0.15s; text-align: left;
  }
  .near-me-tip-toggle:hover { color: var(--text2); }
  .near-me-tip-body {
    display: none; margin-top: 8px; display: none;
    flex-direction: column; gap: 5px;
  }
  .near-me-tip.open .near-me-tip-toggle { color: var(--text2); }
  .near-me-tip.open .near-me-tip-body { display: flex; }
  .near-me-tip-step {
    font-size: 11px; color: var(--text2); line-height: 1.5;
    padding: 5px 8px; background: var(--surface2); border-radius: 5px;
  }
  .tip-os {
    display: block; font-size: 10px; font-family: var(--font-mono);
    color: var(--text3); margin-bottom: 2px;
  }
  @media (max-width: 768px) {
    .near-me-fallback { padding: 10px 14px 12px; }
  }

  /* ── RADIUS BAR ─────────────────────────────────────── */
  .radius-bar {
    padding: 8px 24px;
    background: var(--surface);
    border-bottom: 1px solid var(--border);
    display: flex;
    align-items: center;
    gap: 10px;
    flex-wrap: wrap;
    animation: fadeSlideDown 0.18s ease;
  }
  @keyframes fadeSlideDown { from { opacity:0; transform:translateY(-6px); } to { opacity:1; transform:translateY(0); } }
  .radius-label {
    font-size: 11px; color: var(--text3);
    font-family: var(--font-mono); text-transform: uppercase;
    letter-spacing: 0.08em; white-space: nowrap;
  }
  .radius-options { display: flex; gap: 4px; }
  .radius-opt {
    padding: 4px 10px; border-radius: 4px; font-size: 12px; font-weight: 500;
    cursor: pointer; color: var(--text3); border: 1px solid var(--border);
    background: var(--surface2); font-family: var(--font-main); transition: all 0.15s;
  }
  .radius-opt:hover { color: var(--text2); border-color: var(--border2); }
  .radius-opt.active { background: var(--accent-dim2); color: var(--accent); border-color: rgba(240, 75, 36,0.35); }
  .radius-info { font-size: 11px; color: var(--text2); font-family: var(--font-mono); }

  /* ── MAP-DRIVEN QUICK ADD (issue #102) ─────────────────
     Field-BD "add building here" button + the candidate-pin popup actions.
     Touch targets ≥44px per docs/ui-standards.md; tokens only (both themes). */
  .quick-add-here-btn {
    font-size: 12px; font-weight: 600; padding: 8px 14px;
    min-height: 40px; white-space: nowrap; border-radius: 6px;
  }
  @media (max-width: 640px) {
    /* One-handed field use: full-width, full 48px target. */
    .radius-bar .quick-add-here-btn { flex: 1 1 100%; min-height: 48px; font-size: 14px; }
  }
  .quick-add-confirm, .quick-add-cancel {
    display: block; width: 100%; box-sizing: border-box;
    border-radius: 6px; font-family: var(--font-main); cursor: pointer;
    font-weight: 600; transition: all 0.15s;
  }
  .quick-add-confirm {
    min-height: 44px; margin-top: 4px; font-size: 14px;
    background: var(--accent); color: #fff; border: 1px solid var(--accent);
  }
  .quick-add-confirm:hover { filter: brightness(1.06); }
  .quick-add-cancel {
    min-height: 36px; margin-top: 6px; font-size: 12px;
    background: var(--surface2); color: var(--text2); border: 1px solid var(--border);
  }
  .quick-add-cancel:hover { color: var(--text); border-color: var(--border2); }
  /* "Open" shortcut on each already-tracked match in the duplicate warning. */
  .quick-add-open {
    flex-shrink: 0; min-height: 32px; padding: 5px 12px; border-radius: 6px;
    font-family: var(--font-main); font-size: 12px; font-weight: 600; cursor: pointer;
    background: var(--accent-dim2); color: var(--accent);
    border: 1px solid rgba(240, 75, 36, 0.35); transition: all 0.15s;
  }
  .quick-add-open:hover { background: var(--accent-dim); }
  /* Pulsing halo ring on a nearby tracked building (duplicate warning). Leaflet
     puts .quick-add-halo on the circleMarker's SVG <path>; animate stroke. */
  @keyframes quickAddHaloPulse {
    0%, 100% { stroke-opacity: 0.95; stroke-width: 3; }
    50%      { stroke-opacity: 0.30; stroke-width: 5; }
  }
  .quick-add-halo { animation: quickAddHaloPulse 1.4s ease-in-out infinite; }
  /* Quick-add content (shared by the desktop popup + the mobile bottom sheet). */
  .qa-title { font-weight: 700; font-size: 14px; color: var(--text); margin-bottom: 6px; }
  .qa-finding { font-size: 13px; color: var(--text3); padding: 2px 0 4px; }
  .qa-address { font-size: 14px; font-weight: 600; line-height: 1.3; color: var(--text); margin: 2px 0 6px; overflow-wrap: anywhere; }
  .qa-address--empty { font-weight: 500; color: var(--text2); }
  /* Owner of record — a tight single line (wraps if long) to keep the capture
     card compact; the parcel lookup fills it in progressively. */
  .qa-owner {
    display: flex; flex-wrap: wrap; align-items: baseline; gap: 3px 6px;
    margin: 0 0 6px; padding: 5px 9px; border-radius: 7px;
    background: var(--accent-dim2); border: 1px solid var(--accent-dim);
    font-size: 12.5px; line-height: 1.3;
  }
  .qa-owner-label { font-size: 10px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase; color: var(--accent); flex: 0 0 auto; }
  .qa-owner-name { font-weight: 600; color: var(--text); overflow-wrap: anywhere; min-width: 0; }
  .qa-owner--none { background: var(--surface2); border-color: var(--border); color: var(--text3); }
  /* Instant size / type / class / energy teasers under the owner line. */
  .qa-insight { font-size: 12.5px; color: var(--text2); margin: 2px 0; line-height: 1.3; overflow-wrap: anywhere; }
  .qa-insight--loading { color: var(--text3); }
  .qa-insight .dr-est { font-size: 10px; color: var(--text3); text-transform: uppercase; letter-spacing: .03em; }
  /* Parcel ID label centered on the outlined boundary (GIS-style). */
  .parcel-id-label.leaflet-tooltip {
    background: transparent; border: none; box-shadow: none;
    color: #15803d; font-weight: 800; font-size: 12px; letter-spacing: .02em;
    text-shadow: 0 1px 2px rgba(255,255,255,0.9), 0 0 2px rgba(255,255,255,0.9);
    padding: 0;
  }
  .parcel-id-label.leaflet-tooltip::before { display: none; }
  /* Lightweight quick-add capture card — content-height, docked above the
     bottom safe area, no dim backdrop so the map (and parcel outline) stay
     visible. Deliberately small: this is a quick capture, not a full form. */
  .qa-card {
    position: fixed; left: 12px; right: 12px;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 12px);
    z-index: 3000; max-width: 460px; margin: 0 auto;
    background: var(--surface); border: 1px solid var(--border2);
    border-radius: 16px; box-shadow: 0 10px 34px rgba(0,0,0,0.4);
    transform: translateY(170%); opacity: 0; pointer-events: none;
    transition: transform 0.24s cubic-bezier(0.32,0.72,0,1), opacity 0.2s;
  }
  .qa-card.open { transform: translateY(0); opacity: 1; pointer-events: auto; }
  .qa-card-inner { padding: 10px 14px 12px; }
  .qa-card-head { display: flex; align-items: center; justify-content: space-between; margin-bottom: 4px; }
  .qa-card-head .qa-title { margin-bottom: 0; }
  .qa-x {
    background: none; border: none; color: var(--text3); font-size: 15px; line-height: 1;
    cursor: pointer; padding: 6px 8px; border-radius: 6px; min-height: 32px;
  }
  .qa-x:hover { color: var(--text); background: var(--surface2); }
  .qa-card .quick-add-confirm { min-height: 46px; font-size: 15px; margin-top: 8px; }
  .quick-add-edit {
    display: block; width: 100%; box-sizing: border-box; margin-top: 6px;
    min-height: 36px; border-radius: 8px; background: transparent; color: var(--text2);
    border: 1px solid var(--border); font-family: var(--font-main); font-size: 12px;
    font-weight: 600; cursor: pointer; transition: all 0.15s;
  }
  .quick-add-edit:hover { color: var(--text); border-color: var(--border2); }
  /* Plain "Add building" (commit on the spot, no research) — the secondary action
     under the accent "Add & research". Mirrors the edit button's quiet style. */
  .quick-add-plain {
    display: block; width: 100%; box-sizing: border-box; margin-top: 6px;
    min-height: 40px; border-radius: 8px; background: transparent; color: var(--text2);
    border: 1px solid var(--border); font-family: var(--font-main); font-size: 12.5px;
    font-weight: 600; cursor: pointer; transition: all 0.15s;
  }
  .quick-add-plain:hover { color: var(--text); border-color: var(--border2); }
  /* Add & research: coverage-preview + address-box additions. Reuse the qa- tokens
     so both themes work with no new hexes (ui-standards.md). */
  .qa-insight--muted { color: var(--text3); }
  .qa-justdoit {
    display: flex; align-items: center; gap: 7px; margin: 8px 0 2px;
    font-size: 12px; color: var(--text2); cursor: pointer; line-height: 1.3;
  }
  .qa-justdoit input { width: 16px; height: 16px; accent-color: var(--accent); cursor: pointer; }
  .qa-addr-input {
    display: block; width: 100%; box-sizing: border-box; margin: 4px 0 6px;
    min-height: 44px; padding: 8px 11px; border-radius: 8px;
    background: var(--surface2); color: var(--text); border: 1px solid var(--border);
    font-family: var(--font-main); font-size: 15px;
  }
  .qa-addr-input:focus { outline: none; border-color: var(--accent); }
  .qa-addr-input::placeholder { color: var(--text3); }
  /* US/Canada segmented control (single-select → segmented, per ui-standards). */
  .qa-country-seg { display: flex; gap: 0; margin: 2px 0 8px; border: 1px solid var(--border); border-radius: 8px; overflow: hidden; }
  .qa-seg {
    flex: 1; min-height: 34px; padding: 6px 10px; font-family: var(--font-main);
    font-size: 12.5px; font-weight: 600; cursor: pointer; background: transparent;
    color: var(--text2); border: none; border-right: 1px solid var(--border); transition: all .15s;
  }
  .qa-seg:last-child { border-right: none; }
  .qa-seg:hover { color: var(--text); background: var(--surface2); }
  .qa-seg.is-active { background: var(--accent); color: #fff; }
  /* Address typeahead dropdown (mirrors the modal's #addrSuggestions). */
  .qa-addr-wrap { position: relative; }
  .qa-suggest {
    position: absolute; left: 0; right: 0; top: calc(100% - 4px); z-index: 20;
    background: var(--surface); border: 1px solid var(--border2); border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,.28); overflow: hidden; max-height: 220px; overflow-y: auto;
  }
  .qa-suggest-row {
    display: block; width: 100%; text-align: left; padding: 9px 11px; cursor: pointer;
    background: transparent; border: none; border-bottom: 1px solid var(--border);
    color: var(--text); font-family: var(--font-main); font-size: 13px; line-height: 1.3; overflow-wrap: anywhere;
  }
  .qa-suggest-row:last-child { border-bottom: none; }
  .qa-suggest-row:hover { background: var(--accent-dim); }
  /* Live intake progress panel (per-sweep status while research runs). */
  .qa-steps { display: flex; flex-direction: column; gap: 5px; margin: 8px 0 4px; }
  .qa-step { display: flex; align-items: baseline; gap: 8px; font-size: 13px; color: var(--text2); line-height: 1.35; }
  .qa-step-ic { flex: 0 0 auto; width: 16px; text-align: center; font-weight: 700; }
  .qa-step.is-done { color: var(--text); }
  .qa-step.is-done .qa-step-ic { color: var(--green); }
  .qa-step.is-running .qa-step-ic { color: var(--accent); display: inline-block; animation: qa-spin 1s linear infinite; }
  .qa-step.is-warn .qa-step-ic { color: var(--amber); }
  .qa-step.is-muted { color: var(--text3); }
  @keyframes qa-spin { to { transform: rotate(360deg); } }
  @media (prefers-reduced-motion: reduce) { .qa-step.is-running .qa-step-ic { animation: none; } }

  /* Modal "Recent news / links" — readable blocks (was a one-line truncated
     row with dark-tuned colors). Title wraps; source + domain on its own line
     so the BD person can see/credit where each item came from. Theme-aware. */
  .mn-head { font-size: 11px; color: var(--text2); font-weight: 700; margin-bottom: 2px; }
  .mn-item { padding: 8px 0; border-top: 1px solid var(--border); }
  .mn-top { display: flex; align-items: center; flex-wrap: wrap; gap: 6px; margin-bottom: 3px; }
  .mn-kind {
    font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em;
    color: var(--accent); background: var(--accent-dim2); border: 1px solid var(--accent-dim);
    border-radius: 4px; padding: 1px 6px;
  }
  .mn-date { font-size: 10px; color: var(--text3); font-family: var(--font-mono); }
  .mn-manual { font-size: 9px; color: #a78bfa; border: 1px solid rgba(124,58,237,0.4); border-radius: 4px; padding: 0 4px; }
  .mn-title { display: block; font-size: 13px; line-height: 1.35; color: var(--blue); text-decoration: none; overflow-wrap: anywhere; }
  .mn-title:hover { text-decoration: underline; }
  .mn-title--plain { color: var(--text); }
  .mn-src { font-size: 11px; color: var(--text3); margin-top: 3px; font-family: var(--font-mono); overflow-wrap: anywhere; }

  /* One-time first-run nudge → the ? Help panel's icon guide. */
  .intro-nudge {
    position: fixed; left: 12px; right: 12px;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 12px);
    z-index: 2500; max-width: 460px; margin: 0 auto;
    display: none; align-items: center; gap: 10px;
    background: var(--surface); border: 1px solid var(--accent2); border-radius: 12px;
    padding: 10px 12px; box-shadow: 0 8px 28px rgba(0,0,0,0.4);
  }
  .intro-nudge.show { display: flex; }
  .intro-nudge-icon { font-size: 18px; flex: 0 0 auto; }
  .intro-nudge-text { flex: 1; min-width: 0; font-size: 12.5px; color: var(--text2); line-height: 1.35; }
  .intro-nudge-go {
    flex: 0 0 auto; background: var(--accent); color: #fff; border: none;
    border-radius: 8px; padding: 8px 12px; font-weight: 700; font-size: 12px;
    min-height: 36px; cursor: pointer; font-family: var(--font-main);
  }
  .intro-nudge-x {
    flex: 0 0 auto; background: none; border: none; color: var(--text3);
    font-size: 14px; line-height: 1; cursor: pointer; padding: 6px 8px; min-height: 32px;
  }

  /* One-time install hint (mobile browser tab only) — nudge to Add to Home
     Screen, which launches the standalone PWA with no browser chrome. */
  .install-hint {
    position: fixed; left: 12px; right: 12px;
    bottom: calc(env(safe-area-inset-bottom, 0px) + 12px);
    z-index: 2500; max-width: 460px; margin: 0 auto;
    display: none; align-items: center; gap: 10px;
    background: var(--surface); border: 1px solid var(--accent2); border-radius: 12px;
    padding: 10px 12px; box-shadow: 0 8px 28px rgba(0,0,0,0.4);
  }
  .install-hint.show { display: flex; }
  .install-hint-icon { font-size: 18px; flex: 0 0 auto; }
  .install-hint-text { flex: 1; min-width: 0; font-size: 12.5px; color: var(--text2); line-height: 1.35; }
  .install-hint-text b { color: var(--text); font-weight: 700; }
  .install-hint-action {
    flex: 0 0 auto; background: var(--accent); color: #fff; border: none;
    border-radius: 8px; padding: 8px 12px; font-weight: 700; font-size: 12px;
    min-height: 36px; cursor: pointer; font-family: var(--font-main);
  }
  .install-hint-x {
    flex: 0 0 auto; background: none; border: none; color: var(--text3);
    font-size: 14px; line-height: 1; cursor: pointer; padding: 6px 8px; min-height: 32px;
  }

  /* ── Discovery readout (the 🔍 Discover result) ───────────────
     A compact "should I pursue this?" card: owner of record + size/type/class,
     the estimated energy spend (size of the prize), value/sale, and a worth-it
     cue. Tokens only, both themes. */
  .discover-readout {
    margin: 0 0 12px; padding: 11px 12px; border-radius: 10px;
    background: var(--surface2); border: 1px solid var(--border);
    font-size: 12px; color: var(--text2); line-height: 1.5;
  }
  .dr-loading { font-size: 12px; color: var(--text3); }
  .dr-head {
    font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    color: var(--text3); margin-bottom: 8px; display: flex; flex-wrap: wrap; gap: 6px; align-items: baseline;
  }
  .dr-src { font-weight: 500; letter-spacing: 0; text-transform: none; color: var(--text3); font-family: var(--font-mono); font-size: 10px; }
  .dr-owner { margin-bottom: 8px; }
  .dr-owner-name { font-size: 15px; font-weight: 700; color: var(--text); line-height: 1.25; overflow-wrap: anywhere; }
  .dr-owner-meta { font-size: 11px; color: var(--text3); }
  .dr-mail { font-size: 11px; color: var(--text2); margin-top: 2px; }
  .dr-prize {
    display: flex; align-items: center; gap: 6px; flex-wrap: wrap;
    margin: 8px 0; padding: 7px 9px; border-radius: 8px;
    background: var(--accent-dim2); border: 1px solid var(--accent-dim); color: var(--text);
    font-size: 13px;
  }
  .dr-prize b { color: var(--accent); }
  .dr-est { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text3); }
  .dr-row { margin: 4px 0; }
  .dr-news { color: var(--accent); text-decoration: none; font-weight: 600; }
  .dr-news:hover { text-decoration: underline; }
  .dr-verdict {
    margin: 8px 0 2px; padding: 6px 9px; border-radius: 8px; font-weight: 600; color: var(--text);
    background: rgba(34,197,94,0.10); border: 1px solid rgba(34,197,94,0.35);
  }
  .dr-actions { margin-top: 9px; display: flex; gap: 8px; flex-wrap: wrap; align-items: center; }
  .dr-research {
    display: inline-flex; align-items: center; padding: 8px 12px; border-radius: 8px; min-height: 38px;
    background: var(--surface); border: 1px solid var(--border2); color: var(--text);
    font-family: var(--font-main); font-size: 12px; font-weight: 600; text-decoration: none; cursor: pointer;
  }
  .dr-research:hover { border-color: var(--accent); color: var(--accent); }
  .dr-research--link { background: transparent; border-color: var(--border); color: var(--text3); font-weight: 500; }
  .dr-note { margin-top: 8px; font-size: 10px; color: var(--text3); line-height: 1.45; }

  /* "Find GP / LP investors (AI)" result */
  .oi-result { margin-top: 10px; padding: 9px 10px; border-radius: 8px; background: var(--surface); border: 1px solid var(--border); }
  .oi-result:empty, .oi-result[hidden] { display: none; }
  .oi-loading { font-size: 12px; color: var(--text3); }
  .oi-err { font-size: 12px; color: var(--red); }
  .oi-head { font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--text3); margin-bottom: 5px; display: flex; align-items: center; gap: 8px; flex-wrap: wrap; }
  .oi-conf { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; padding: 1px 6px; border-radius: 99px; }
  .oi-conf--high { background: rgba(34,197,94,0.15); color: #16a34a; }
  .oi-conf--medium { background: var(--accent-dim2); color: var(--accent); }
  .oi-conf--low { background: var(--surface2); color: var(--text3); }
  .oi-summary { font-size: 12px; color: var(--text2); line-height: 1.5; margin-bottom: 6px; }
  .oi-row { font-size: 12px; color: var(--text); margin: 3px 0; }
  .oi-row .oi-k { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--accent); margin-right: 5px; }
  .oi-sources { margin-top: 7px; display: flex; flex-direction: column; gap: 3px; }
  .oi-sources a { font-size: 11px; color: var(--text2); text-decoration: none; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .oi-sources a:hover { color: var(--accent); }
  .oi-note { margin-top: 7px; font-size: 10px; color: var(--text3); }

  /* Compact county-intel block shown on a pin tap (mobile BD panel + desktop
     map popup): owner of record + energy prize + size/type. Tokens, both themes. */
  .bd-pin-intel:empty { display: none; }
  .bd-pin-intel { margin: 10px 0 6px; }
  .pin-intel {
    padding: 8px 10px; border-radius: 8px;
    background: var(--surface2); border: 1px solid var(--border); font-size: 12px; line-height: 1.5;
  }
  .pi-loading { font-size: 11px; color: var(--text3); font-family: var(--font-mono); padding: 2px 0; }
  .pi-head {
    font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em;
    color: var(--text3); margin-bottom: 4px; font-family: var(--font-mono);
  }
  .pi-owner { font-size: 12px; color: var(--text); margin-bottom: 2px; }
  .pi-owner .pi-label { font-size: 9px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em; color: var(--accent); margin-right: 4px; }
  .pi-warn { color: #f59e0b; font-weight: 600; font-size: 11px; }
  .pi-row { color: var(--text2); margin: 2px 0; }
  /* In the desktop Leaflet popup, give the intel block a little separation. */
  .map-popup-title + .pin-intel-mount, .pin-intel-mount:not(:empty) { margin-top: 6px; }

  /* ── FILTER CHIPS ───────────────────────────────────── */
  /* Breadcrumb trails — the Scout SPA view/owner trail and the building-modal
     trail. Variable-driven so light mode comes for free. */
  .scout-trail {
    display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
    font-family: var(--font-mono); color: var(--text2);
  }
  .scout-trail:empty { display: none; }
  /* In-header (subtitle slot) — the default position on desktop, where the
     "Project Pipeline · Intelligent Buildings" tagline used to be. */
  .scout-trail--header { font-size: 11px; margin-top: 2px; }
  /* Standalone copy below the stat bar — mobile only (the header brand, and
     thus the in-header trail, is hidden on phones). */
  .scout-trail--bar { display: none; font-size: 12px; padding: 8px 24px 0; }
  .modal-bc {
    display: flex; align-items: center; gap: 7px; flex-wrap: wrap;
    font-family: var(--font-mono); font-size: 11px; color: var(--text3); margin-bottom: 4px;
  }
  .scout-trail .bc-root, .scout-trail .bc-link, .modal-bc .bc-link {
    background: none; border: none; padding: 0; font: inherit; color: var(--text2); cursor: pointer;
  }
  /* Home affordance — 🏠 on the root crumb, matching the admin breadcrumbs. */
  .scout-trail .bc-root::before, .modal-bc .bc-link::before { content: "🏠 "; }
  .scout-trail .bc-root:hover, .scout-trail .bc-link:hover, .modal-bc .bc-link:hover {
    color: var(--text); text-decoration: underline;
  }
  .scout-trail .bc-sep, .modal-bc .bc-sep { color: var(--text3); opacity: .7; }
  .scout-trail .bc-current, .modal-bc .bc-current { color: var(--text); font-weight: 600; }
  /* The modal breadcrumb's current crumb just repeats the address that's already
     the big H1 right below it (mobile dropped the whole bar for this reason —
     see the .modal .modal-bc rule). Keep the "🏠 Scout" link as the back-to-board
     affordance, but hide the redundant separator + address crumb on desktop. */
  .modal-bc .bc-sep, #modalBcName { display: none; }

  .filter-chips {
    display: flex; padding: 6px 24px; gap: 6px; flex-wrap: wrap; align-items: center;
    border-bottom: 1px solid var(--border); background: var(--surface);
  }
  .filter-chip {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 3px 4px 3px 10px;
    background: var(--accent-dim); border: 1px solid rgba(240, 75, 36,0.25);
    border-radius: 20px; font-size: 12px; color: var(--accent); font-family: var(--font-mono);
  }
  /* The × button needs a real tap target — the visual is just the glyph but
     the hit area covers a thumb-friendly square. Padding + negative-margin
     hack keeps the chip's visual height unchanged while giving the button
     a 24px × 24px hit zone (32px on mobile via the media-query below). */
  .filter-chip button {
    background: none; border: none; color: var(--accent);
    cursor: pointer; font-size: 14px; line-height: 1; opacity: 0.7;
    width: 24px; height: 24px;
    display: inline-flex; align-items: center; justify-content: center;
    padding: 0; margin: -4px -4px -4px 0;
    border-radius: 50%;
  }
  .filter-chip button:hover { opacity: 1; background: rgba(240, 75, 36,0.18); }
  .filter-chips-clear {
    background: none; border: none; color: var(--text3); cursor: pointer;
    font-size: 11px; font-family: var(--font-mono); text-decoration: underline;
    text-underline-offset: 2px; padding: 0 4px; margin-left: 2px;
  }
  .filter-chips-clear:hover { color: var(--text); }
  /* Explicit return-to-cockpit affordance shown above the owner-filtered table
     (cockpit drill-in). Filled accent so it reads as a primary "back", distinct
     from the outline filter chips beside it. Accent + white work in both themes. */
  .filter-back-cockpit {
    display: inline-flex; align-items: center; gap: 6px;
    padding: 5px 12px; border-radius: 20px; cursor: pointer;
    background: var(--accent); border: 1px solid var(--accent); color: #fff;
    font-size: 12px; font-weight: 600; font-family: inherit; white-space: nowrap;
  }
  .filter-back-cockpit:hover { filter: brightness(1.06); }

  /* ── RECENT DROPDOWN (header) ──────────────────────── */
  .recent-btn {
    display: inline-flex; align-items: center; gap: 4px;
    height: 32px; padding: 0 10px;
    border-radius: 6px;
    border: 1px solid var(--border2);
    background: var(--surface2);
    color: var(--text2);
    cursor: pointer;
    font-family: inherit;
    font-size: 12px;
    font-weight: 500;
    transition: all 0.15s;
  }
  /* Desktop shows "Recent ⌄" — the clock icon is redundant alongside text. */
  .recent-btn .recent-icon { display: none; }
  .recent-btn svg { flex-shrink: 0; }
  /* Mobile: collapse Recent to an icon-only button so it matches the other
     icon-btns in the row instead of standing out as the lone text+chevron.
     The clock SVG takes over from the "Recent ⌄" label+chevron pair. These
     rules sit alongside the base block so they override it in cascade order
     (the page-wide mobile @media at the top of the file runs before this
     section, so its rules would lose to the desktop defaults below). */
  @media (max-width: 768px) {
    .recent-btn { width: 32px; padding: 0; justify-content: center; }
    .recent-btn .recent-label,
    .recent-btn .recent-chevron { display: none; }
    .recent-btn .recent-icon { display: block; }
  }
  .recent-btn:hover { color: var(--text); background: var(--bg); }
  .recent-btn[aria-expanded="true"] { color: var(--accent); border-color: var(--accent2); background: var(--accent-dim); }
  .recent-menu {
    position: absolute;
    top: calc(100% + 6px);
    right: 0;
    min-width: 240px; max-width: 320px;
    background: var(--surface);
    border: 1px solid var(--border2);
    border-radius: 8px;
    box-shadow: 0 8px 24px rgba(0,0,0,0.35);
    padding: 4px;
    /* Above Leaflet's panes/controls (markers 600, popups 700, controls ~1000)
       so the dropdown isn't occluded by the map on the Map view; still below the
       detail modal (2000) and pickers (4000). */
    z-index: 1300;
    max-height: 60vh;
    overflow-y: auto;
  }
  .recent-menu-item {
    display: flex; align-items: center; gap: 8px;
    width: 100%; padding: 8px 10px;
    background: none; border: none; color: var(--text);
    font-family: var(--font-main); font-size: 12px; font-weight: 500;
    text-align: left; cursor: pointer; border-radius: 6px;
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .recent-menu-item:hover { background: var(--surface2); }
  .recent-menu-empty {
    padding: 12px 10px; font-size: 11px; color: var(--text3);
    font-family: var(--font-mono); text-align: center;
  }
  .recent-menu-dot {
    width: 6px; height: 6px; border-radius: 50%;
    background: #4ade80; flex-shrink: 0;
  }

  /* ── OWNER SEARCH ───────────────────────────────────── */
  .owner-search-wrap {
    position: relative; flex: 1; min-width: 140px; max-width: 200px;
  }
  .owner-search-wrap svg {
    position: absolute; left: 10px; top: 50%;
    transform: translateY(-50%); color: var(--text3); pointer-events: none;
  }
  .owner-search-input {
    width: 100%; background: var(--surface2); border: 1px solid var(--border);
    border-radius: 6px; padding: 7px 10px 7px 32px; font-size: 13px;
    color: var(--text); font-family: var(--font-main); outline: none;
    transition: border-color 0.15s;
  }
  .owner-search-input:focus { border-color: var(--accent); }
  .owner-search-input::placeholder { color: var(--text3); }
  /* Owner suggestions — fixed to body so iOS sticky-toolbar clipping can't intercept taps */
  #ownerSuggestions {
    display: none; position: fixed;
    background: var(--surface); border: 1px solid var(--border2);
    border-radius: 6px; overflow: hidden; z-index: 9999;
    box-shadow: 0 8px 24px rgba(0,0,0,0.4); max-height: 220px; overflow-y: auto;
  }
  .owner-suggestion {
    padding: 9px 12px; font-size: 12px; color: var(--text2);
    cursor: pointer; transition: background 0.1s;
    border-bottom: 1px solid var(--border);
  }
  .owner-suggestion:last-child { border-bottom: none; }
  .owner-suggestion:hover { background: var(--surface2); color: var(--text); }

  /* ── DISTANCE BADGE ─────────────────────────────────── */
  .dist-badge {
    font-size: 10px; font-family: var(--font-mono);
    color: var(--accent); background: var(--accent-dim);
    border: 1px solid rgba(240, 75, 36,0.2); border-radius: 4px;
    padding: 1px 5px; white-space: nowrap;
  }

  /* ── FIELD BD RAIL (mobile pin tap — floating icon rail + popover) ──
     A thin monochrome icon rail floats on the right edge so the map stays
     visible; each facet expands in a compact popover. The layer itself is
     click-through (pointer-events:none) — only the rail and popover catch
     taps, so panning/tapping the map behind still works. (issue #102) */
  .bd-rail-layer { position: fixed; inset: 0; z-index: 3000; pointer-events: none; display: none; }
  .bd-rail-layer.open { display: block; }

  .bd-rail {
    position: absolute; right: 10px; top: 50%; transform: translateY(-50%);
    display: flex; flex-direction: column; gap: 4px; padding: 6px;
    max-height: 88dvh; overflow-y: auto; -webkit-overflow-scrolling: touch;
    border-radius: 16px; background: var(--surface); border: 1px solid var(--border2);
    box-shadow: 0 6px 24px rgba(0,0,0,0.35); pointer-events: auto;
  }
  .bd-rail::-webkit-scrollbar { display: none; }
  .bd-rail-btn {
    position: relative; width: 46px; height: 46px; border-radius: 12px; flex: 0 0 auto;
    display: grid; place-items: center; cursor: pointer;
    background: transparent; border: none; color: var(--text2);
    transition: background 0.15s, color 0.15s;
  }
  .bd-rail-btn svg { width: 23px; height: 23px; }
  .bd-rail-btn:active { background: var(--surface2); }
  .bd-rail-btn.active { background: var(--accent-dim2); color: var(--accent); }
  .bd-rail-btn--close { color: var(--text3); margin-top: 2px; padding-top: 4px; border-top: 1px solid var(--border); }

  .bd-rail-dot {
    position: absolute; top: 7px; right: 7px; width: 9px; height: 9px;
    border-radius: 50%; border: 2px solid var(--surface);
  }
  .bd-rail-dot--warn { background: #f59e0b; }
  .bd-rail-dot--deal { background: #ff7a59; }
  .bd-rail-dot--campus { background: #7c3aed; }   /* in a curated campus (matches the 🏛 marker) */
  .bd-rail-count {
    position: absolute; top: 4px; right: 4px; min-width: 16px; height: 16px;
    padding: 0 4px; border-radius: 8px; background: var(--accent); color: #fff;
    font-size: 10px; font-weight: 700; line-height: 16px; text-align: center;
    border: 2px solid var(--surface); box-sizing: content-box;
  }

  /* Docked to the bottom-right (left of the rail) rather than vertically
     centered — that keeps a large *contiguous* band of map clear up top, so
     the selected building/parcel (which we pan up into it) is never hidden
     behind the card. Content-height, capped so a long facet still leaves the
     building visible. */
  .bd-rail-pop {
    position: absolute; right: 66px; top: auto;
    bottom: calc(14px + env(safe-area-inset-bottom, 0px));
    transform: translateY(10px) scale(0.98);
    width: min(78vw, 320px); max-height: 54dvh; overflow-y: auto;
    -webkit-overflow-scrolling: touch;
    background: var(--surface); border: 1px solid var(--border2);
    border-radius: 14px; box-shadow: 0 10px 32px rgba(0,0,0,0.42);
    padding: 14px 14px 16px; opacity: 0; pointer-events: none;
    transition: opacity 0.16s, transform 0.16s;
  }
  .bd-rail-pop.open { opacity: 1; transform: translateY(0) scale(1); pointer-events: auto; }
  .bd-rail-pop-x {
    position: absolute; top: 8px; right: 8px; width: 28px; height: 28px;
    border: none; background: transparent; color: var(--text3);
    font-size: 14px; cursor: pointer; line-height: 1;
  }
  .bd-pop-title {
    display: flex; align-items: center; gap: 7px; margin: 0 22px 10px 0;
    font-size: 11px; font-weight: 700; text-transform: uppercase; letter-spacing: .05em;
    color: var(--text3);
  }
  .bd-pop-title svg { width: 15px; height: 15px; color: var(--accent); flex: 0 0 auto; }

  .bd-pop-addr { font-size: 15px; font-weight: 700; color: var(--text); line-height: 1.25; overflow-wrap: anywhere; }
  .bd-pop-sub { font-size: 11px; color: var(--text3); font-family: var(--font-mono); margin-top: 3px; }
  .bd-pop-dist { font-size: 11px; color: var(--accent); font-family: var(--font-mono); margin-top: 5px; }
  .bd-pop-chips { display: flex; flex-wrap: wrap; gap: 5px; margin-top: 10px; }
  .bd-pop-chip { background: var(--surface2); border: 1px solid var(--border); border-radius: 6px; padding: 3px 8px; font-size: 11px; color: var(--text2); }
  .bd-pop-muted { font-size: 12px; color: var(--text3); margin: 6px 0 0; line-height: 1.4; }

  .bd-pop-row { display: flex; gap: 10px; padding: 7px 0; border-bottom: 1px solid var(--border); }
  .bd-pop-row:last-child { border-bottom: none; }
  .bd-pop-k { flex: 0 0 78px; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text3); padding-top: 2px; }
  .bd-pop-v { flex: 1; min-width: 0; font-size: 13px; color: var(--text); overflow-wrap: anywhere; }
  .bd-pop-stage { font-size: 12px; font-weight: 600; font-family: var(--font-mono); margin-top: 8px; }

  .bd-pop-energy { font-size: 18px; font-weight: 800; color: var(--text); margin: 2px 0 4px; line-height: 1.2; }
  .bd-pop-energy-yr { font-size: 14px; font-weight: 700; color: var(--accent); }

  .bd-pop-contact { padding: 8px 0; border-bottom: 1px solid var(--border); }
  .bd-pop-contact:last-child { border-bottom: none; }
  .bd-pop-cname { font-weight: 600; font-size: 13px; color: var(--text); }
  .bd-pop-ctitle { font-size: 11px; color: var(--text3); }
  .bd-pop-link { font-family: var(--font-mono); font-size: 11px; text-decoration: none; color: var(--accent); }
  .bd-pop-link--mut { color: var(--text2); }

  .bd-pop-deal { display: flex; align-items: center; gap: 8px; font-size: 13px; margin-bottom: 6px; }
  .bd-pop-deal-dot { width: 9px; height: 9px; border-radius: 50%; flex-shrink: 0; }

  .bd-pop-btn {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    width: 100%; margin-top: 10px; padding: 10px; min-height: 44px; box-sizing: border-box;
    border-radius: 9px; cursor: pointer; font-family: inherit; font-size: 13px; font-weight: 700;
    background: var(--surface2); border: 1px solid var(--border); color: var(--text); text-decoration: none;
  }
  .bd-pop-btn--port { background: rgba(124,58,237,0.12); border-color: rgba(124,58,237,0.35); color: #a78bfa; }
  .bd-pop-btn--hs { background: rgba(255,122,89,0.1); border-color: rgba(255,122,89,0.3); color: #ff7a59; }

  /* Split row: a value that stays on one line (ellipsis if long) next to a compact
     inline action — fixes the value collapsing to one-char-per-line on mobile. */
  .bd-pop-split { display: flex; align-items: center; gap: 8px; justify-content: space-between; }
  .bd-pop-fill { flex: 1 1 auto; min-width: 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
  .bd-pop-split .bd-pop-btn { flex: 0 0 auto; width: auto; margin-top: 0; padding: 0 14px; min-height: 44px; }
  /* Named active signal at the top of the Summary facet. */
  .bd-pop-signal {
    display: inline-flex; align-items: center; gap: 5px; margin: 0 0 6px;
    padding: 4px 11px; border-radius: 999px; background: var(--accent); color: #fff;
    font-size: 12px; font-weight: 700; letter-spacing: .01em;
    border: 0; font-family: inherit; cursor: pointer; text-decoration: none;
    transition: filter .1s ease;
  }
  .bd-pop-signal:hover { filter: brightness(1.08); }
  .bd-pop-signal-go { opacity: .75; margin-left: 2px; }
  .bd-pop-why { font-size: 12px; color: var(--text2); line-height: 1.45; margin: 0 0 8px; }
  /* Secondary signals — a building can carry more than one; show the others quietly
     under the headline so nothing's hidden. */
  .bd-pop-sig-more { display: flex; flex-wrap: wrap; gap: 5px; margin: 0 0 10px; }
  .bd-pop-sig-chip {
    display: inline-flex; align-items: center; gap: 4px; font-size: 11px; font-weight: 600;
    color: var(--text2); background: var(--surface2); border: 1px solid var(--border);
    border-radius: 999px; padding: 2px 8px; font-family: inherit; cursor: pointer;
    transition: border-color .1s ease, color .1s ease;
  }
  .bd-pop-sig-chip:hover { color: var(--text); border-color: var(--accent); }
  .bd-pop-sig-go { opacity: .6; }
  .bd-pop-btn--act { background: var(--accent); border-color: var(--accent); color: #fff; }
  /* Secondary "copy link" — quieter than the primary action above it. */
  .bd-pop-copy { background: transparent; border-color: var(--border); color: var(--text2); font-weight: 600; }
  .bd-pop-copy:hover { color: var(--text); border-color: var(--border2); }

  /* ── MAP PEEK SHEET (touch pin tap, tier 1) ────────────────────────────────
     A collapsed, full-width bottom sheet — the touch replacement for the
     desktop hover glance. Slides up from the bottom edge; drag the handle up (or
     tap "Open full detail") to expand to the record (tier 2). Touch-only:
     opened by the marker click handler on coarse-pointer devices, so the
     styling is unconditional (matches the bd-rail pattern above). Centered with
     a max-width so it reads as a sheet on wide touch tablets, not a full band. */
  .map-peek {
    position: fixed; left: 0; right: 0; bottom: 0; z-index: 3200;
    max-width: 560px; margin: 0 auto;
    transform: translateY(110%);
    transition: transform 0.22s cubic-bezier(.22,.61,.36,1);
    background: var(--surface); border: 1px solid var(--border2); border-bottom: none;
    border-radius: 18px 18px 0 0;
    box-shadow: 0 -8px 32px rgba(0,0,0,0.42);
    padding: 4px 16px calc(16px + env(safe-area-inset-bottom, 0px));
    pointer-events: none;
  }
  .map-peek.open { transform: translateY(0); pointer-events: auto; }
  .map-peek.dragging { transition: none; }     /* follow the finger 1:1 while dragging (tier 2) */
  @media (prefers-reduced-motion: reduce) { .map-peek { transition: none; } }

  /* Grab handle — the "drag me up" affordance; a tall, full-width hit area so
     the thin pill is easy to grab. touch-action:none lets the drag own the Y axis. */
  .map-peek-handle {
    display: flex; align-items: center; justify-content: center;
    height: 26px; cursor: grab; touch-action: none;
  }
  .map-peek-handle span { width: 40px; height: 5px; border-radius: 3px; background: var(--border2); }

  .peek-head { display: flex; align-items: flex-start; gap: 12px; }
  .peek-titles { flex: 1; min-width: 0; }
  .peek-name { font-size: 16px; font-weight: 700; color: var(--text); line-height: 1.25; overflow-wrap: anywhere; }
  .peek-sub { font-size: 12px; color: var(--text3); margin-top: 2px; overflow-wrap: anywhere; }

  /* Score puck — neutral by default; greens to match the map's signal beacon
     when the building has an active signal (same green as the parcel outline /
     verified-SF cue elsewhere in this file, kept consistent on purpose). */
  .peek-score {
    flex: 0 0 auto; width: 52px; height: 52px; border-radius: 12px;
    display: flex; flex-direction: column; align-items: center; justify-content: center;
    background: var(--surface2); border: 1px solid var(--border);
  }
  .peek-score-n { font-size: 18px; font-weight: 800; line-height: 1; color: var(--text); font-family: var(--font-mono); }
  .peek-score-l { font-size: 8px; font-weight: 700; text-transform: uppercase; letter-spacing: .06em; color: var(--text3); margin-top: 3px; }
  .peek-score--hot { border-color: rgba(34,197,94,.5); }
  .peek-score--hot .peek-score-n { color: #22c55e; }

  .peek-owner { display: flex; align-items: baseline; gap: 8px; margin-top: 11px; font-size: 13px; color: var(--text); overflow-wrap: anywhere; }
  .peek-owner-k { flex: 0 0 auto; font-size: 10px; font-weight: 700; text-transform: uppercase; letter-spacing: .04em; color: var(--text3); }

  .peek-sigs { display: flex; flex-direction: column; gap: 6px; margin-top: 11px; }
  .peek-sig { display: flex; gap: 7px; align-items: baseline; font-size: 12.5px; color: var(--text); line-height: 1.4; }
  .peek-sig-ico { flex: 0 0 auto; }
  .peek-sig-txt { color: var(--text2); }

  /* Campus row — purple to match the 🏛 site marker / member ring. "Show whole
     site" locks the highlight on; "report →" opens the site report. The member
     highlight itself fires automatically on the pin tap. */
  .peek-campus-row { display: flex; align-items: center; gap: 8px; margin-top: 10px; }
  .peek-campus {
    flex: 1; min-width: 0; display: inline-flex; align-items: center; gap: 5px;
    padding: 8px 12px; border-radius: 999px;
    background: rgba(124,58,237,0.12); border: 1px solid rgba(124,58,237,0.45);
    color: var(--text); font-size: 12px; font-weight: 600; font-family: inherit; cursor: pointer;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .peek-campus:active { background: rgba(124,58,237,0.22); }
  .peek-campus-rep {
    flex: 0 0 auto; background: none; border: none; padding: 8px 4px;
    color: var(--accent); font-size: 12px; font-weight: 600; font-family: inherit;
    cursor: pointer; white-space: nowrap;
  }

  /* Floating lock chip — the locked-campus indicator + clear control, centered
     near the top of the map (the peek is gone, so the map is clear below it). */
  .campus-lock {
    position: fixed; left: 50%; top: calc(64px + env(safe-area-inset-top, 0px));
    transform: translateX(-50%) translateY(-8px); z-index: 3300;
    display: none; align-items: center; gap: 9px; min-height: 40px;
    padding: 7px 12px 7px 15px; border-radius: 999px;
    background: rgba(124,58,237,0.18); border: 1px solid rgba(124,58,237,0.55);
    -webkit-backdrop-filter: blur(6px); backdrop-filter: blur(6px);
    color: var(--text); font-size: 12.5px; font-weight: 700; font-family: inherit;
    box-shadow: 0 6px 20px rgba(0,0,0,0.35); cursor: pointer;
    max-width: 86vw; opacity: 0; pointer-events: none;
    transition: opacity .16s, transform .16s;
  }
  .campus-lock.open { display: flex; opacity: 1; transform: translateX(-50%) translateY(0); pointer-events: auto; }
  .campus-lock-txt { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
  .campus-lock-x {
    display: inline-flex; align-items: center; justify-content: center;
    width: 22px; height: 22px; min-width: 22px; border-radius: 50%;
    background: rgba(124,58,237,0.3); color: var(--text); font-size: 12px;
  }
  @media (prefers-reduced-motion: reduce) { .campus-lock { transition: none; } }

  /* Energy "size of the prize" — the headline reason to engage, at a glance. */
  .peek-energy { margin-top: 11px; font-size: 13px; font-weight: 700; color: var(--text); }
  .peek-energy-est { font-size: 11px; font-weight: 600; color: var(--text3); margin-left: 5px; }

  /* Quick actions (Call / Birdie) — secondary chips above the primary expand. */
  .peek-actions { display: flex; gap: 8px; margin-top: 13px; }
  .peek-act {
    flex: 1; display: flex; align-items: center; justify-content: center; gap: 6px;
    min-height: 44px; padding: 10px; border-radius: 10px;
    background: var(--surface2); border: 1px solid var(--border); color: var(--text);
    font-size: 13px; font-weight: 600; font-family: inherit; cursor: pointer;
    transition: background 0.12s, border-color 0.12s;
  }
  .peek-act:active { background: var(--accent-dim2); border-color: var(--accent); }

  .peek-expand {
    display: flex; align-items: center; justify-content: center; gap: 6px;
    width: 100%; margin-top: 8px; padding: 11px; border-radius: 10px;
    background: var(--accent); border: 1px solid var(--accent); color: #fff;
    font-size: 13px; font-weight: 600; font-family: inherit; cursor: pointer;
    transition: filter 0.12s;
  }
  .peek-expand:active { filter: brightness(0.92); }
  .peek-expand-go { opacity: .7; }

  /* ── PIN CONTEXT MENU (touch long-press, tier 3) ───────────────────────────
     A small action sheet anchored to the long-press point. Touch-only (opened
     by openPinMenu on a coarse-pointer long-press), so styling is unconditional. */
  .pin-menu {
    position: fixed; z-index: 3400; min-width: 196px; max-width: 76vw;
    display: none; flex-direction: column; gap: 2px;
    background: var(--surface); border: 1px solid var(--border2); border-radius: 12px;
    box-shadow: 0 12px 36px rgba(0,0,0,0.46); padding: 6px;
  }
  .pin-menu.open { display: flex; }
  .pin-menu-hd {
    font-size: 11px; font-weight: 700; color: var(--text3);
    text-transform: uppercase; letter-spacing: .04em; padding: 6px 10px 4px;
    overflow: hidden; text-overflow: ellipsis; white-space: nowrap;
  }
  .pin-menu-item {
    display: flex; align-items: center; gap: 11px; width: 100%; min-height: 44px;
    padding: 11px 12px; border: none; background: transparent; border-radius: 8px;
    color: var(--text); font-size: 14px; font-family: inherit; text-align: left; cursor: pointer;
  }
  .pin-menu-item:active { background: var(--surface2); }
  .pin-menu-item-ico { flex: 0 0 auto; width: 20px; text-align: center; font-size: 15px; }

  @media (max-width: 768px) {
    /* Near Me controls collapse to ONE tight, scrollable row: [radius pills]
       [➕ Add here] [✕]. Label, the verbose "N within X" count, and the
       redundant "within X" filter chip are dropped — the pins + selected pill
       already convey them. Saves ~3 rows of map real estate. */
    .radius-bar {
      padding: 6px 12px; gap: 6px; flex-wrap: nowrap;
      overflow-x: auto; -webkit-overflow-scrolling: touch;
    }
    .radius-bar::-webkit-scrollbar { display: none; }
    .radius-bar .radius-label,
    .radius-bar .radius-info { display: none; }
    .radius-bar .radius-options { flex: 0 0 auto; }
    .radius-bar .quick-add-here-btn {
      flex: 0 0 auto; min-height: 38px; font-size: 13px; padding: 8px 12px; margin-left: auto;
    }
    .radius-clear-btn {
      flex: 0 0 auto; min-height: 38px; min-width: 38px; padding: 8px 10px;
      font-size: 14px; line-height: 1;
    }
    /* The active-filter chips (incl. the redundant "within X") add no value on
       the map and updateFilterChips sets an inline display — so !important. */
    body.view-map #filterChips { display: none !important; }
    /* Bigger tap targets on mobile — the ¼ / ½ / 1 / 2 mi buttons used to
       be 12-px text with 4×10 padding (~24px tall), well under iOS's 44pt
       guideline. Bump to 13px text and 7×14 padding so each button is a
       genuine 32px square thumb target. */
    .radius-opt { padding: 7px 14px; font-size: 13px; min-height: 32px; }
    .scout-trail--bar { display: flex; padding: 8px 14px 0; }
    .filter-chips { padding: 8px 14px; gap: 8px; }
    /* The chip × was a 12-px glyph with no padding — almost impossible
       to hit-test on a phone. Grow the chip font-size to 13px and the ×
       hit area to 32×32 (still rendered as the same small glyph). */
    .filter-chip { font-size: 13px; padding: 5px 4px 5px 12px; }
    .filter-chip button {
      font-size: 18px; width: 32px; height: 32px;
      margin: -6px -6px -6px 0;
    }
    /* Owner search owns its own toolbar row on mobile; Near Me rides
       up next to the address search on row 2 (see the earlier block). */
    .owner-search-wrap { max-width: none; flex: 0 0 100%; order: 2; }
  }

  /* ── Address row: input + buttons ── */
  .address-row { display: flex; flex-direction: column; gap: 8px; }
  .address-input-line { display: flex; gap: 8px; align-items: center; }
  .address-input-line .form-input { flex: 1; min-width: 0; }
  .address-btn-line { display: flex; gap: 8px; align-items: center; flex-wrap: wrap; }
  /* Desktop: single row */
  @media (min-width: 769px) {
    .address-row { flex-direction: row; align-items: center; }
    .address-input-line { flex: 1; min-width: 0; }
    .address-btn-line { flex-shrink: 0; }
  }

  /* ── HubSpot sprocket icon ── */
  .hs-sprocket, .li-icon-btn {
    display: inline-flex; align-items: center; justify-content: center;
    width: 28px; height: 28px; border-radius: 6px;
    flex-shrink: 0; text-decoration: none !important;
    transition: opacity 0.15s; cursor: pointer;
  }
  .hs-sprocket { background: rgba(255,122,89,0.12); color: #ff7a59; border: 1px solid rgba(255,122,89,0.35); }
  .li-icon-btn  { background: rgba(10,102,194,0.12); color: #0A66C2;   border: 1px solid rgba(10,102,194,0.35); }
  .hs-sprocket:hover, .li-icon-btn:hover { opacity: 0.75; }

  /* ── Filter count badge ── */
  .filter-count {
    display: inline-block; font-size: 10px; font-weight: 700;
    background: rgba(255,255,255,0.08); border-radius: 3px;
    padding: 0 4px; margin-left: 4px; font-family: var(--font-mono);
    color: var(--text3);
  }
  .filter-tab.active .filter-count { color: var(--accent); background: rgba(240, 75, 36,0.12); }

  /* ── Mobile: compact stat pills — number on top, short label below ── */
  @media (max-width: 768px) {
    .stat-pill {
      flex-direction: column; align-items: center; justify-content: center;
      min-width: 56px; max-width: 72px; padding: 3px 4px; gap: 0; text-align: center;
    }
    .stat-dot { display: none; }
    .stat-num { font-size: 16px; font-weight: 700; min-width: auto; line-height: 1.1; }
    .stat-label {
      font-size: 9px; letter-spacing: 0; text-transform: none; line-height: 1.1;
      white-space: nowrap; overflow: hidden; text-overflow: ellipsis; max-width: 68px;
    }
    /* Mobile-only short labels via CSS attr trick */
    #spill-pipeline .stat-label::after  { content: "Pipeline"; }
    #spill-ib-active .stat-label::after { content: "IB Active"; }
    #spill-pipeline .stat-label,
    #spill-ib-active .stat-label { font-size: 0; } /* hide original, show ::after */
    #spill-pipeline .stat-label::after,
    #spill-ib-active .stat-label::after { font-size: 9px; }

    /* ← Back replaces ✕ on mobile */
    .modal-close {
      font-size: 0 !important;
      padding: 6px 12px;
      border: 1px solid var(--border2);
      border-radius: 6px;
      background: var(--surface2);
    }
    .modal-close::before {
      content: "← Back";
      font-size: 13px;
      font-weight: 500;
      color: var(--text2);
    }
    /* Contact actions wrap nicely on narrow screens */
    .contact-actions { gap: 6px; }
    .contact-action-btn { padding: 5px 8px; font-size: 11px; }
  }

  /* Override Leaflet popup for dark theme */
  .leaflet-popup-content-wrapper {
    background: var(--surface) !important;
    border: 1px solid var(--border2) !important;
    border-radius: 10px !important;
    box-shadow: 0 8px 32px rgba(0,0,0,0.5) !important;
    color: var(--text) !important;
    font-family: var(--font-main) !important;
  }
  .leaflet-popup-tip { background: var(--surface) !important; }
  .leaflet-popup-close-button { color: var(--text3) !important; font-size: 16px !important; }
  .map-popup-title { font-size: 14px; font-weight: 700; color: var(--text); margin-bottom: 2px; line-height: 1.3; }
  .map-popup-sub { font-size: 11px; color: var(--text3); font-family: var(--font-mono); margin-bottom: 7px; }
  .map-popup-row { font-size: 11px; color: var(--text3); margin: 3px 0; font-family: var(--font-mono); }
  /* "View site report" chip on a curated-campus cluster (works in both themes via tokens). */
  .cluster-site-chip { display: flex; flex-direction: column; gap: 7px; align-items: stretch; }
  .cluster-site-chip .csc-name { font-size: 12px; font-weight: 700; color: var(--text1); }
  /* Desktop hover tooltip on a pin — tokenized so it reads in both themes (Leaflet's
     default is a white box that disappears in dark mode). */
  .leaflet-tooltip.pin-hover-tip {
    background: var(--surface); color: var(--text1);
    border: 1px solid var(--border2); border-radius: 6px;
    padding: 4px 8px; font-size: 12px; font-weight: 600;
    box-shadow: 0 3px 10px rgba(0,0,0,0.25); font-family: var(--font-main);
  }
  .leaflet-tooltip.pin-hover-tip::before { display: none; }   /* drop the default arrow */
  /* Campus hover: a sonar-style pulsing/fading ring on each member (cleaner than a
     static disc when a campus is large/spread). The divIcon scales from its centre. */
  .campus-pulse span {
    display: block; width: 16px; height: 16px; border-radius: 50%;
    border: 2px solid #7c3aed; opacity: 0; transform-origin: center center;
    animation: campusPulse 1.8s ease-out infinite;
  }
  @keyframes campusPulse {
    0%   { transform: scale(.4); opacity: .75; }
    70%  { opacity: .15; }
    100% { transform: scale(2.8); opacity: 0; }
  }
  .campus-hover-spoke { transition: opacity .12s ease; }
  /* Pin opacity eases when the rest of the map dims around a hovered campus. */
  .leaflet-marker-icon { transition: opacity .12s ease; }
  @media (prefers-reduced-motion: reduce) { .campus-pulse span { animation: none; opacity: .5; transform: scale(1.4); } }
  .mp-chips { display: flex; flex-wrap: wrap; gap: 4px; margin: 7px 0; }
  .mp-chip { display: inline-flex; align-items: center; gap: 3px; background: rgba(255,255,255,0.06); border: 1px solid rgba(255,255,255,0.09); border-radius: 4px; padding: 2px 6px; font-size: 10px; color: var(--text2); font-family: var(--font-main); white-space: nowrap; }
  .mp-chip.green { background: rgba(34,197,94,0.1); border-color: rgba(34,197,94,0.25); color: #22c55e; }
  .mp-chip.amber { background: rgba(245,158,11,0.1); border-color: rgba(245,158,11,0.25); color: #f59e0b; }
  .map-popup-btn {
    flex: 1; padding: 7px 8px;
    background: var(--accent-dim2); color: var(--accent);
    border: 1px solid rgba(240, 75, 36,0.3); border-radius: 6px;
    font-size: 11px; font-weight: 700; cursor: pointer;
    font-family: var(--font-main); transition: background 0.15s; text-align: center;
  }
  .map-popup-btn:hover { background: rgba(240, 75, 36,0.25); }
  .map-popup-btn.portfolio { background: rgba(124,58,237,0.12); border-color: rgba(124,58,237,0.4); color: #a78bfa; }
  .map-popup-btn.portfolio:hover { background: rgba(124,58,237,0.22); }
  .mp-btn-row { display: flex; gap: 6px; margin-top: 10px; }

  /* ── Birdie — BD-assistant chat surface ─────────────────────────────────
     Right-side drawer on desktop, bottom sheet on phones. Mirrors the modal's
     .open toggle pattern. Tokens only — works in both themes. */
  .birdie-backdrop {
    position: fixed; inset: 0; z-index: 2590;
    background: rgba(0,0,0,0.45);
    opacity: 0; pointer-events: none; transition: opacity 0.2s;
    backdrop-filter: blur(2px);
  }
  .birdie-backdrop.open { opacity: 1; pointer-events: auto; }
  .birdie-drawer {
    position: fixed; top: 0; right: 0; height: 100%;
    width: min(420px, 100%); z-index: 2600;
    display: flex; flex-direction: column;
    background: var(--surface);
    border-left: 1px solid var(--border2);
    box-shadow: -10px 0 30px rgba(0,0,0,0.30);
    transform: translateX(100%);
    transition: transform 0.25s ease;
  }
  .birdie-drawer.open { transform: translateX(0); }
  .birdie-head {
    display: flex; align-items: center; justify-content: space-between;
    padding: 14px 16px; border-bottom: 1px solid var(--border);
    flex-shrink: 0;
  }
  .birdie-title {
    display: flex; align-items: center; gap: 8px;
    font-weight: 700; font-size: 15px; color: var(--text);
  }
  .birdie-title svg { color: var(--accent); }
  /* Birdie disc avatar in the chat header — swaps idle ⇄ responding (the
     responding badge animates: wings flap, signals rise) via _setBusy(). The
     disc carries its own navy background, so it reads on light & dark. */
  .birdie-avatar { display: inline-flex; align-items: center; justify-content: center; width: 26px; height: 26px; flex-shrink: 0; }
  .birdie-avatar svg { display: block; width: 26px; height: 26px; border-radius: 50%; }
  /* In-thread "Birdie is working" bubble: the animated responding badge sits
     beside the status text / dots, so her motion reads right where the user is
     looking. The badge carries its own reduced-motion fallback. */
  .birdie-thinking { display: inline-flex; align-items: center; gap: 8px; }
  .birdie-think-mark { width: 22px; height: 22px; flex: 0 0 auto; }
  .birdie-think-mark svg { display: block; width: 22px; height: 22px; border-radius: 50%; }
  .birdie-scope {
    font-weight: 500; font-size: 12px; color: var(--text2);
    font-family: var(--font-mono);
  }
  .birdie-close {
    background: none; border: none; color: var(--text2);
    font-size: 24px; line-height: 1; cursor: pointer; padding: 0 4px;
  }
  .birdie-close:hover { color: var(--text); }
  .birdie-msgs {
    flex: 1; overflow-y: auto; overscroll-behavior: contain;
    padding: 16px; display: flex; flex-direction: column; gap: 12px;
  }
  .birdie-msg {
    max-width: 88%; padding: 10px 13px; border-radius: 14px;
    font-size: 14px; line-height: 1.5; color: var(--text);
    word-wrap: break-word;
  }
  .birdie-head-actions { display: flex; align-items: center; gap: 4px; }
  .birdie-recent {
    background: none; border: none; cursor: pointer; color: var(--text3);
    display: flex; align-items: center; justify-content: center;
    width: 30px; height: 30px; border-radius: 8px; padding: 0;
  }
  .birdie-recent:hover { color: var(--accent); background: var(--surface2); }
  /* Recent-chats list (the "memory" view) */
  .birdie-history { display: flex; flex-direction: column; gap: 6px; }
  .birdie-history-head {
    font-size: 11px; text-transform: uppercase; letter-spacing: .04em;
    color: var(--text3); margin-bottom: 2px;
  }
  .birdie-history-item {
    display: flex; flex-direction: column; gap: 2px; text-align: left;
    background: var(--surface); border: 1px solid var(--border);
    border-radius: 10px; padding: 8px 10px; cursor: pointer; font: inherit; color: var(--text);
  }
  .birdie-history-item:hover { border-color: var(--accent); }
  .birdie-history-item .bh-q { font-size: 13px; }
  .birdie-history-item .bh-meta { font-size: 11px; color: var(--text3); }
  .birdie-msg p { margin: 0 0 8px; }
  .birdie-msg p:last-child { margin-bottom: 0; }
  .birdie-msg strong { font-weight: 700; color: var(--text); }
  .birdie-msg ul { margin: 4px 0 8px; padding-left: 18px; }
  .birdie-msg ul:last-child { margin-bottom: 0; }
  .birdie-msg li { margin: 2px 0; }
  /* Scoped-building context card — pinned atop the transcript. Tokens only,
     so it reads correctly in both light and dark. */
  .birdie-bldg-card {
    align-self: stretch; display: flex; flex-direction: column;
    background: var(--surface2); border: 1px solid var(--border);
    border-radius: 14px; overflow: hidden;
  }
  .birdie-bldg-img {
    width: 100%; height: 120px; object-fit: cover; display: block;
    background: var(--surface); border-bottom: 1px solid var(--border);
  }
  .birdie-bldg-body { padding: 10px 13px; display: flex; flex-direction: column; gap: 3px; }
  .birdie-bldg-name { font-size: 13px; font-weight: 700; color: var(--text); }
  .birdie-bldg-addr { font-size: 12px; color: var(--text2); }
  .birdie-bldg-links { display: flex; flex-wrap: wrap; gap: 14px; margin-top: 6px; }
  .birdie-bldg-links a { font-size: 12px; color: var(--accent); text-decoration: none; }
  .birdie-bldg-links a:hover { text-decoration: underline; }
  /* "Made to travel" — the prospect-ready one-pager Birdie hands back, with a
     Copy action. Visually distinct from Birdie's own bubbles (it's an artifact,
     not a remark). Tokens only → both themes. */
  .birdie-export {
    margin-top: 8px; background: var(--surface); border: 1px solid var(--border2);
    border-radius: 12px; padding: 12px 13px; display: flex; flex-direction: column; gap: 10px;
  }
  .birdie-export-body { font-size: 13px; line-height: 1.55; color: var(--text); white-space: normal; }
  .birdie-export-body p { margin: 0 0 8px; }
  .birdie-export-body p:last-child { margin-bottom: 0; }
  .birdie-copy {
    align-self: flex-start; cursor: pointer;
    background: var(--accent); color: #fff; border: none;
    border-radius: 8px; padding: 6px 14px; font: inherit; font-size: 12px; font-weight: 600;
  }
  .birdie-copy:hover { background: var(--accent2); }
  /* Read aloud — the quieter, secondary action next to Copy: an outline pill so
     it reads as optional, not the lead. Speaks the reply with the device's own
     speech engine (no tokens). Tokens only → both themes. ≥44px tall for touch. */
  .birdie-speak {
    display: inline-flex; align-items: center; gap: 6px;
    background: transparent; color: var(--accent); border: 1px solid var(--accent);
    min-height: 32px;
  }
  .birdie-speak:hover { background: var(--accent-dim); color: var(--accent); }
  .birdie-speak-ic { font-size: 13px; line-height: 1; }
  /* While speaking, flip to a filled "Stop" so the state is unmistakable. */
  .birdie-speak.is-speaking {
    background: var(--accent); color: #fff; border-color: var(--accent);
  }
  .birdie-speak.is-speaking:hover { background: var(--accent2); color: #fff; }
  /* Read-aloud row hung under a plain chat reply (export/prep use the artifact
     actions row instead). A hair of air above so it doesn't crowd the text. */
  .birdie-msg-actions { display: flex; margin-top: 8px; }
  /* Add-property proposal card — Birdie's front door confirm. Tokens only → both
     themes. The tap is the human action (suggest-never-approve). */
  .birdie-action-card {
    margin-top: 10px; padding: 10px 12px; border-radius: 10px;
    background: var(--surface2); border: 1px solid var(--border);
  }
  .birdie-action-addr { font-size: 13px; font-weight: 600; color: var(--text); margin-bottom: 8px; overflow-wrap: anywhere; }
  .birdie-action-btns { display: flex; gap: 8px; flex-wrap: wrap; }
  .birdie-action-go, .birdie-action-no {
    min-height: 40px; padding: 0 14px; border-radius: 8px; font-family: var(--font-main);
    font-size: 13px; font-weight: 600; cursor: pointer; transition: all .15s;
  }
  .birdie-action-go { background: var(--accent); color: #fff; border: 1px solid var(--accent); }
  .birdie-action-go:hover { filter: brightness(1.06); }
  .birdie-action-go:disabled { opacity: .8; cursor: default; }
  .birdie-action-no { background: transparent; color: var(--text2); border: 1px solid var(--border); }
  .birdie-action-no:hover { color: var(--text); border-color: var(--border2); }
  .birdie-action-done { font-size: 13px; color: var(--text2); }
  /* Draft-email action card: the body reads as a quoted draft the rep can copy or
     log. Tokens only → both themes. */
  .birdie-draft-body {
    font-size: 13px; line-height: 1.5; color: var(--text); margin-bottom: 8px;
    padding: 8px 10px; border-radius: 8px; background: var(--surface);
    border: 1px solid var(--border); white-space: pre-wrap; overflow-wrap: anywhere;
  }
  /* Artifact actions row: Copy (accent-filled) + Log to HubSpot (secondary
     outline, so it reads as the quieter, deliberate "keep this" action). Tokens
     only → both themes. */
  .birdie-artifact-actions { display: flex; flex-wrap: wrap; gap: 8px; align-items: center; }
  .birdie-loghs {
    background: transparent; color: var(--accent); border: 1px solid var(--accent);
  }
  .birdie-loghs:hover { background: var(--accent-dim); color: var(--accent); }
  .birdie-loghs:disabled { opacity: .8; cursor: default; }
  /* Not-yet-linked state: the building has no HubSpot company/deal, so there's
     nothing to attach a Note to. Show a quieter "Link to HubSpot to save" hint
     (opens the account picker) instead of the accent outline, so it never
     masquerades as the real save action — muted dashed border + muted text, both
     themes via tokens. */
  .birdie-loghs-hint {
    color: var(--text2); border: 1px dashed var(--border2);
  }
  .birdie-loghs-hint:hover { background: var(--surface); color: var(--text); border-color: var(--accent); }
  .birdie-loghs-hint:disabled:hover { background: transparent; color: var(--text2); border-color: var(--border2); }
  /* After logging, the button becomes a "View in HubSpot ↗" anchor — keep the
     same pill shape, drop the underline. */
  a.birdie-loghs { display: inline-block; text-decoration: none; }
  a.birdie-loghs:hover { text-decoration: none; }
  .birdie-chip--travel {
    border-color: var(--accent); color: var(--accent); font-weight: 600;
  }
  .birdie-chip--travel:hover { background: var(--accent-dim); }
  /* Call prep is the primary call-day action — a filled accent pill so it reads
     as the lead move, distinct from the outline "make a version to share". */
  .birdie-chip--prep {
    border-color: var(--accent); background: var(--accent); color: #fff; font-weight: 600;
  }
  .birdie-chip--prep:hover { background: var(--accent2); color: #fff; }
  /* The prep sheet leans on bold section headers — give them a little air so the
     sheet stays skimmable inside the chat surface (works in both themes). */
  .birdie-prep .birdie-export-body strong { display: inline-block; margin-top: 6px; }
  .birdie-prep .birdie-export-body > :first-child strong { margin-top: 0; }
  /* Inline source links in the prep sheet + the Sources list at the bottom. */
  .birdie-export-body a { color: var(--accent); text-decoration: underline; text-underline-offset: 2px; word-break: break-word; }
  .birdie-prep-sources {
    margin-top: 12px; padding-top: 10px; border-top: 1px solid var(--border);
    display: flex; flex-direction: column; gap: 5px;
  }
  .birdie-prep-sources-head {
    font-size: 11px; font-weight: 700; letter-spacing: .04em; text-transform: uppercase;
    color: var(--text3, var(--text2)); margin-bottom: 2px;
  }
  .birdie-prep-sources a { font-size: 12px; line-height: 1.4; }
  /* Each source is a row: the link + keep/dismiss for building-scoped prep. The
     web findings are captured as candidate signals automatically (opt-out); these
     let the rep keep a link (→ confirmed signal) or dismiss it. See
     docs/birdie-research-signals-spec.md. */
  .birdie-prep-source-row { display: flex; align-items: center; gap: 8px; }
  .birdie-prep-source-row > a { flex: 1 1 auto; min-width: 0; }
  .birdie-prep-source-row.birdie-src-dismissed > a { opacity: .5; text-decoration: line-through; }
  .birdie-src-actions { flex: 0 0 auto; display: inline-flex; gap: 4px; }
  .birdie-src-keep, .birdie-src-dismiss {
    font-size: 11px; line-height: 1; cursor: pointer; min-height: 28px;
    padding: 5px 9px; border-radius: 6px;
    border: 1px solid var(--border2); background: var(--surface); color: var(--text2);
  }
  .birdie-src-keep:hover { border-color: var(--accent); color: var(--accent); }
  .birdie-src-dismiss:hover { border-color: var(--red); color: var(--red); }
  .birdie-src-keep:disabled, .birdie-src-dismiss:disabled { cursor: default; opacity: .7; }
  /* Staged "what it's doing now" status line while the prep is being built. */
  .birdie-prep-status { color: var(--text2); }
  .birdie-msg.user {
    align-self: flex-end;
    background: var(--accent-dim);
    border: 1px solid var(--border2);
    border-bottom-right-radius: 4px;
  }
  .birdie-msg.bird {
    align-self: flex-start;
    background: var(--surface2);
    border: 1px solid var(--border);
    border-bottom-left-radius: 4px;
  }
  .birdie-typing { display: inline-flex; gap: 4px; align-items: center; }
  .birdie-typing i {
    width: 6px; height: 6px; border-radius: 50%;
    background: var(--text3); display: inline-block;
    animation: birdieBlink 1.2s infinite ease-in-out both;
  }
  .birdie-typing i:nth-child(2) { animation-delay: 0.2s; }
  .birdie-typing i:nth-child(3) { animation-delay: 0.4s; }
  @keyframes birdieBlink { 0%, 80%, 100% { opacity: 0.3; } 40% { opacity: 1; } }
  .birdie-input-row {
    display: flex; gap: 8px; align-items: flex-end;
    padding: 12px; border-top: 1px solid var(--border); flex-shrink: 0;
  }
  .birdie-input {
    flex: 1; resize: none; max-height: 120px;
    background: var(--surface2); color: var(--text);
    border: 1px solid var(--border2); border-radius: 10px;
    padding: 10px 12px; font-family: inherit; font-size: 14px; line-height: 1.4;
  }
  .birdie-input:focus { outline: none; border-color: var(--accent); }
  /* Keep the placeholder to a single line (ellipsis if ever too long) so it can't
     wrap and clip on a narrow phone. Affects only the placeholder, not typed text. */
  .birdie-input::placeholder {
    color: var(--text3);
    white-space: nowrap; overflow: hidden; text-overflow: ellipsis;
  }
  .birdie-send {
    flex-shrink: 0; width: 40px; height: 40px; border-radius: 10px;
    border: none; cursor: pointer;
    background: var(--accent); color: #fff;
    display: flex; align-items: center; justify-content: center;
  }
  .birdie-send:hover { background: var(--accent2); }
  .birdie-send:disabled { opacity: 0.5; cursor: default; }
  .birdie-foot {
    padding: 8px 14px 12px; font-size: 11px; line-height: 1.4;
    color: var(--text3); border-top: 1px solid var(--border);
    flex-shrink: 0;
  }
  @media (max-width: 640px) {
    .birdie-drawer {
      top: auto; bottom: 0; right: 0; width: 100%; height: 85vh;
      border-left: none; border-top: 1px solid var(--border2);
      border-radius: 16px 16px 0 0;
      box-shadow: 0 -10px 30px rgba(0,0,0,0.35);
      transform: translateY(100%);
    }
    .birdie-drawer.open { transform: translateY(0); }
  }

  /* ── Birdie welcome — one-time rollout splash ───────────────────────────
     Celebratory but on-brand: tokens only, works in both themes. Shown once
     per user (localStorage), auto-skips deep-links and automated sessions. */
  .birdie-welcome-overlay {
    position: fixed; inset: 0; z-index: 2700;
    display: flex; align-items: center; justify-content: center; padding: 20px;
    background: rgba(0,0,0,0.6); backdrop-filter: blur(4px);
    opacity: 0; pointer-events: none; transition: opacity 0.25s;
  }
  .birdie-welcome-overlay.open { opacity: 1; pointer-events: auto; }
  .birdie-welcome {
    position: relative; width: 100%; max-width: 460px;
    background: var(--surface); border: 1px solid var(--border2);
    border-radius: 16px; overflow: hidden;
    box-shadow: 0 24px 60px rgba(0,0,0,0.45);
    transform: translateY(14px) scale(0.98); transition: transform 0.25s ease;
  }
  .birdie-welcome-overlay.open .birdie-welcome { transform: translateY(0) scale(1); }
  .birdie-welcome-band {
    height: 6px;
    background: linear-gradient(90deg, var(--accent), var(--link));
  }
  .birdie-welcome-body { padding: 24px 24px 20px; }
  .birdie-welcome-badge {
    width: 52px; height: 52px;
    display: flex; align-items: center; justify-content: center; margin-bottom: 14px;
    /* The Birdie disc avatar carries its own navy background — no tinted tile. */
  }
  .birdie-welcome-badge svg { display: block; }
  .birdie-welcome-eyebrow {
    font-family: var(--font-mono); font-size: 11px; letter-spacing: 0.08em;
    text-transform: uppercase; color: var(--accent); margin-bottom: 6px;
  }
  .birdie-welcome h2 { font-size: 22px; line-height: 1.2; color: var(--text); margin: 0 0 8px; }
  .birdie-welcome-sub { font-size: 14px; line-height: 1.5; color: var(--text2); margin: 0 0 18px; }
  /* Example asks — the compelling, tappable "here's what to do with me" grid.
     Each opens Birdie with the question pre-filled. Tokens → both themes. */
  .birdie-welcome-eg-label { font-family: var(--font-mono); font-size: 10.5px; letter-spacing: .06em; text-transform: uppercase; color: var(--text3); margin: 0 0 9px; }
  .birdie-welcome-egs { display: grid; grid-template-columns: 1fr 1fr; gap: 9px; margin: 0 0 22px; }
  .birdie-eg {
    display: flex; align-items: center; gap: 9px; text-align: left;
    padding: 11px 12px; border-radius: 11px; cursor: pointer;
    background: var(--surface2); border: 1px solid var(--border2); color: var(--text);
    font: inherit; font-size: 13px; line-height: 1.3;
    transition: border-color .12s ease, background .12s ease, transform .12s ease;
  }
  .birdie-eg:hover { border-color: var(--accent); background: var(--accent-dim); transform: translateY(-1px); }
  .birdie-eg-ic { font-size: 17px; flex: 0 0 auto; line-height: 1; }
  .birdie-eg-tx { min-width: 0; font-weight: 600; }
  @media (max-width: 420px) { .birdie-welcome-egs { grid-template-columns: 1fr; } }
  /* One-tap "add a building" on-ramp — full-width, accent-outlined so it reads as
     the distinct action below the ask examples. */
  .birdie-eg--add { width: 100%; box-sizing: border-box; margin: 0 0 22px; border-color: var(--accent); }
  .birdie-eg--add:hover { background: var(--accent-dim); }
  .birdie-welcome-actions { display: flex; gap: 10px; }
  .birdie-welcome-actions .bw-btn {
    flex: 1; padding: 11px 14px; border-radius: 10px; font-size: 14px; font-weight: 600;
    cursor: pointer; border: 1px solid transparent; font-family: inherit;
  }
  .bw-btn.bw-primary { background: var(--accent); color: #fff; }
  .bw-btn.bw-primary:hover { background: var(--accent2); }
  .bw-btn.bw-ghost { background: transparent; color: var(--text2); border-color: var(--border2); }
  .bw-btn.bw-ghost:hover { color: var(--text); background: var(--surface2); }
  .birdie-welcome-foot { margin: 14px 0 0; font-size: 11.5px; color: var(--text3); text-align: center; }
  .birdie-welcome-close {
    position: absolute; top: 12px; right: 12px; z-index: 1;
    background: none; border: none; color: var(--text2);
    font-size: 22px; line-height: 1; cursor: pointer; padding: 2px 6px;
  }
  .birdie-welcome-close:hover { color: var(--text); }

  /* Birdie suggested-prompt chips — tappable starters above the input. */
  .birdie-chips {
    display: flex; flex-wrap: wrap; gap: 6px;
    padding: 0 12px 10px; flex-shrink: 0;
  }
  .birdie-chip {
    border: 1px solid var(--border2); background: var(--surface2); color: var(--text);
    border-radius: 999px; padding: 6px 12px; font-size: 12.5px; line-height: 1.2;
    font-family: inherit; cursor: pointer; text-align: left;
  }
  .birdie-chip:hover { border-color: var(--accent); color: var(--accent); }

/* ── Forward-a-post FAB (mobile) ────────────────────────────────────────────
   Manual signal-fishing is a primary BD action, but the top-bar 📎 is easy to
   miss and out of the one-handed thumb zone on a phone. On mobile we surface it
   as a floating action button in the bottom-right instead; on desktop it stays
   in the header (#forwardPostBtn) and this FAB is hidden. */
.forward-fab { display: none; }
@media (max-width: 768px) {
  /* One entry point per breakpoint: the header paperclip yields to the FAB. */
  #forwardPostBtn { display: none; }
  .forward-fab {
    display: inline-flex; align-items: center; gap: 8px;
    position: fixed;
    right: calc(16px + env(safe-area-inset-right, 0px));
    bottom: calc(20px + env(safe-area-inset-bottom, 0px));
    z-index: 1250;               /* above the board/header (1100), below modals/sheets (3000+) */
    height: 52px; padding: 0 20px;
    border: none; border-radius: 26px;
    background: var(--accent); color: #fff;
    font-size: 15px; font-weight: 700; font-family: inherit; line-height: 1;
    box-shadow: 0 6px 20px rgba(0, 0, 0, 0.28);
    cursor: pointer; -webkit-tap-highlight-color: transparent;
  }
  .forward-fab svg { width: 20px; height: 20px; flex: 0 0 auto; }
  .forward-fab:active { transform: translateY(1px); box-shadow: 0 3px 12px rgba(0, 0, 0, 0.26); }
}
/* Very narrow phones: drop the label to a compact circular FAB so it never
   crowds the board edge. */
@media (max-width: 380px) {
  .forward-fab span { display: none; }
  .forward-fab { padding: 0; width: 52px; justify-content: center; border-radius: 50%; }
}
