/* Blackbird Web HQ -- component library (Campaign 020.0, Section 10). */

/* --- micro-labels / section headers --- */
.bb-label {
  font-family: var(--bb-font-ui);
  font-size: var(--bb-fs-micro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bb-text-tertiary);
}

.bb-heading {
  margin: 0 0 var(--bb-space-1);
  font-size: var(--bb-fs-lg);
  font-weight: 650;
  color: var(--bb-text-primary);
}

.bb-mono {
  font-family: var(--bb-font-mono);
}

.bb-muted {
  color: var(--bb-text-secondary);
}
.bb-subtle {
  color: var(--bb-text-tertiary);
}

/* --- buttons --- */
.bb-btn {
  display: inline-flex;
  align-items: center;
  gap: var(--bb-space-2);
  padding: var(--bb-space-2) var(--bb-space-4);
  border-radius: var(--bb-radius-md);
  border: 1px solid var(--bb-border-strong);
  background: var(--bb-surface-raised);
  color: var(--bb-text-primary);
  font-family: var(--bb-font-ui);
  font-size: var(--bb-fs-sm);
  font-weight: 600;
  cursor: pointer;
  transition: background var(--bb-transition-fast), border-color var(--bb-transition-fast);
}
.bb-btn:hover {
  background: var(--bb-surface-hover);
  text-decoration: none;
}
.bb-btn:disabled {
  opacity: 0.5;
  cursor: not-allowed;
}

.bb-btn--primary {
  background: var(--bb-accent);
  border-color: var(--bb-accent);
  color: var(--bb-text-inverse);
}
.bb-btn--primary:hover {
  filter: brightness(1.08);
  background: var(--bb-accent);
}

.bb-btn--ghost {
  background: transparent;
  border-color: transparent;
  color: var(--bb-text-secondary);
}
.bb-btn--ghost:hover {
  background: var(--bb-surface-hover);
  color: var(--bb-text-primary);
}

.bb-btn--sm {
  padding: var(--bb-space-1) var(--bb-space-2);
  font-size: var(--bb-fs-micro);
}

.bb-btn--icon {
  padding: var(--bb-space-2);
  line-height: 1;
}

/* --- cards --- */
.bb-card {
  background: var(--bb-surface);
  border: 1px solid var(--bb-border);
  border-radius: var(--bb-radius-lg);
  padding: var(--bb-space-4);
}

.bb-card__header {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--bb-space-3);
  margin-bottom: var(--bb-space-3);
}

.bb-grid {
  display: grid;
  gap: var(--bb-space-4);
}
.bb-grid--2 {
  grid-template-columns: repeat(2, 1fr);
}
.bb-grid--3 {
  grid-template-columns: repeat(3, 1fr);
}
.bb-grid--panels {
  grid-template-columns: repeat(auto-fit, minmax(280px, 1fr));
}
@media (max-width: 900px) {
  .bb-grid--2,
  .bb-grid--3,
  .bb-grid--panels {
    grid-template-columns: 1fr;
  }
}
/* PRE-MERGE CORRECTION PACK, P2 -- PLAYER PROFILE INFORMATION ORDER.
   `player.html`'s panels now sit in the DOM in the locked reading order
   (current -> contextual -> valuation -> performance -> history), so the
   positional `:nth-of-type` mobile reorder that used to live here is
   gone: one order, every width, nothing to keep in sync by hand.
   `.bb-panel-group-label` is the full-width caption that names each
   group inside the panel grid. */
.bb-player-panel-grid .bb-panel-group-label {
  grid-column: 1 / -1;
}
.bb-panel-group-label {
  display: flex;
  align-items: baseline;
  gap: var(--bb-space-3);
  flex-wrap: wrap;
  margin-top: var(--bb-space-2);
  padding: 0 var(--bb-space-1) var(--bb-space-1);
  border-bottom: 1px solid var(--bb-border-subtle);
}
.bb-panel-group-label:first-child {
  margin-top: 0;
}
.bb-panel-group-label__title {
  font-family: var(--bb-font-mono);
  font-size: var(--bb-fs-micro);
  font-weight: 600;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--bb-accent);
}
.bb-panel-group-label__note {
  font-size: var(--bb-fs-sm);
  color: var(--bb-text-tertiary);
}

/* --- metric / signal tiles --- */
.bb-tile-row {
  display: grid;
  grid-template-columns: repeat(6, 1fr);
  gap: var(--bb-space-3);
}
@media (max-width: 1100px) {
  .bb-tile-row {
    grid-template-columns: repeat(3, 1fr);
  }
}
@media (max-width: 620px) {
  .bb-tile-row {
    grid-template-columns: repeat(2, 1fr);
  }
}

.bb-tile {
  background: var(--bb-surface);
  border: 1px solid var(--bb-border);
  border-radius: var(--bb-radius-md);
  padding: var(--bb-space-3);
  display: flex;
  flex-direction: column;
  gap: var(--bb-space-1);
}
.bb-tile__label {
  font-size: var(--bb-fs-micro);
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--bb-text-tertiary);
}
.bb-tile__value {
  font-family: var(--bb-font-mono);
  font-size: var(--bb-fs-xl);
  font-weight: 600;
  color: var(--bb-text-primary);
}
.bb-tile__sub {
  font-size: var(--bb-fs-sm);
  color: var(--bb-text-secondary);
}
.bb-tile--unavailable .bb-tile__value {
  color: var(--bb-text-tertiary);
  font-size: var(--bb-fs-md);
}
.bb-tile--positive .bb-tile__value { color: var(--bb-positive); }
.bb-tile--negative .bb-tile__value { color: var(--bb-negative); }
.bb-tile--info .bb-tile__value { color: var(--bb-info); }

/* --- evidence panels --- */
.bb-evidence {
  display: flex;
  flex-direction: column;
  gap: var(--bb-space-2);
}
.bb-evidence__row {
  display: flex;
  justify-content: space-between;
  gap: var(--bb-space-3);
  padding: var(--bb-space-2) 0;
  border-bottom: 1px solid var(--bb-border-subtle);
  font-size: var(--bb-fs-sm);
}
.bb-evidence__row:last-child {
  border-bottom: none;
}
.bb-evidence__key {
  color: var(--bb-text-tertiary);
}
.bb-evidence__value {
  font-family: var(--bb-font-mono);
  color: var(--bb-text-primary);
  text-align: right;
}

.bb-chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--bb-space-2);
}
.bb-chip {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 3px 10px;
  border-radius: var(--bb-radius-pill);
  font-size: var(--bb-fs-micro);
  font-weight: 600;
  letter-spacing: 0.03em;
  border: 1px solid var(--bb-caution-border);
  background: var(--bb-caution-bg);
  color: var(--bb-caution);
}

/* --- people picker (Draft Weekend DW-1) --- the Messages composer's
   searchable "To" field. Options are BUTTONS styled as list rows (the
   `.bb-listrow` shape) so the whole row is one tap target on mobile;
   selection state itself lives in the form's hidden input + chip, both
   driven by `app.js`'s `[data-bb-person-option]` delegate. */
.bb-people-results {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--bb-border-subtle);
  border-radius: var(--bb-radius-lg);
  overflow: hidden;
}
.bb-people-option {
  display: flex;
  align-items: center;
  gap: var(--bb-space-2);
  width: 100%;
  padding: var(--bb-space-2) var(--bb-space-3);
  border: none;
  border-bottom: 1px solid var(--bb-border-subtle);
  background: transparent;
  color: var(--bb-text-primary);
  font: inherit;
  text-align: left;
  cursor: pointer;
}
.bb-people-option:last-child {
  border-bottom: none;
}
.bb-people-option:hover,
.bb-people-option:focus-visible {
  background: var(--bb-surface-hover);
}
.bb-people-option__name {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  align-items: baseline;
  gap: var(--bb-space-2);
  flex-wrap: wrap;
}
/* DW-3.2 Phase 1F: this control had no box of its own. `font: inherit`
   on an 11px chip plus four pixels of LEFT-ONLY padding measured 12x10px
   in the authenticated production audit -- the smallest hit area in the
   product, and a dismissal, which is the one class of control a user
   cannot afford to miss. `.bb-trade-chip__remove` below does the exact
   same job on the Trade Desk and has carried a 32px floor since 021.0;
   the asymmetry was the defect, not the size. A centred flex box is what
   turns the button's own space into hit area on all four sides rather
   than only on the left. */
.bb-people-clear {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  min-width: 24px;
  min-height: 24px;
  border: none;
  background: transparent;
  color: inherit;
  font: inherit;
  cursor: pointer;
  padding: 0;
  line-height: 1;
}

/* --- dense tables --- */
.bb-table-wrap {
  overflow-x: auto;
  border: 1px solid var(--bb-border);
  border-radius: var(--bb-radius-lg);
}

.bb-table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--bb-fs-sm);
  min-width: 720px;
}

.bb-table thead th {
  position: sticky;
  top: 0;
  background: var(--bb-bg-elevated);
  text-align: left;
  padding: var(--bb-space-2) var(--bb-space-3);
  font-size: var(--bb-fs-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bb-text-tertiary);
  border-bottom: 1px solid var(--bb-border);
  white-space: nowrap;
}

.bb-table tbody td {
  padding: var(--bb-space-2) var(--bb-space-3);
  border-bottom: 1px solid var(--bb-border-subtle);
  color: var(--bb-text-primary);
  vertical-align: middle;
}

.bb-table tbody tr {
  transition: background var(--bb-transition-fast);
}
.bb-table tbody tr.is-clickable {
  cursor: pointer;
}
.bb-table tbody tr.is-clickable:hover {
  background: var(--bb-surface-hover);
}

.bb-table .bb-num {
  font-family: var(--bb-font-mono);
  text-align: right;
}

.bb-em-dash {
  color: var(--bb-text-tertiary);
  cursor: help;
}

/* --- rankings: position rank + tier dividers (presentation only) --- */
.bb-table .bb-pos-rank {
  font-family: var(--bb-font-mono);
  color: var(--bb-text-secondary);
  white-space: nowrap;
}

/* DRAFT ROOM V1, Packet 3: a queued row on the Draft Room's available
   board. A left accent rule only -- deliberately the quietest possible
   mark, because a queue is the USER's note, not a Blackbird judgement,
   and it must not read as though Blackbird ranks the row differently.
   Reuses the existing accent token; no new palette, no reordering. */
.bb-table tbody tr.bb-row--queued > td:first-child {
  box-shadow: inset 3px 0 0 var(--bb-accent);
}

/* A full-width band between tier groups. Reuses the existing accent
   tokens -- no new palette. */
.bb-table tbody tr.bb-tier-divider td {
  padding: var(--bb-space-2) var(--bb-space-3);
  background: var(--bb-bg-elevated);
  border-top: 1px solid var(--bb-border-strong);
  border-bottom: 1px solid var(--bb-border-strong);
}
.bb-table tbody tr.bb-tier-divider:hover {
  background: transparent;
}
.bb-tier-divider__label {
  display: inline-block;
  font-size: var(--bb-fs-micro);
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--bb-accent);
  background: var(--bb-accent-bg);
  border: 1px solid var(--bb-accent);
  border-radius: var(--bb-radius-sm, 4px);
  padding: 2px 8px;
  vertical-align: middle;
}
.bb-tier-divider__rule {
  display: inline-block;
  height: 1px;
  width: calc(100% - 100px);
  margin-left: var(--bb-space-2);
  vertical-align: middle;
  background: linear-gradient(to right, var(--bb-accent), transparent);
  opacity: 0.45;
}

/* Sticky header for the long rankings board. `.bb-table-wrap` is already
   an `overflow-x: auto` scroll container, which means a `position:
   sticky` thead sticks to the WRAP, not the viewport -- so on a screen
   tall enough for it, the wrap is given its own bounded height and
   becomes the vertical scroller too, which is what actually pins the
   header while scanning 100+ rows. Small screens keep normal page flow. */
/* PACK 2, Part 10: this used to start at 901px, so a tablet lost the
   bounded scroller AND the sticky header at exactly the width where a
   long board becomes hardest to read. A 621px floor keeps both from the
   phone tier upward -- measured effect on the Rankings board at 834px:
   7083px of page height down to a bounded, sticky-headed board. */
@media (min-width: 621px) and (min-height: 620px) {
  .bb-table-wrap--sticky {
    max-height: calc(100vh - 240px);
    overflow-y: auto;
  }
}

/* The per-row "All readings" disclosure exists for the width at which
   columns genuinely cannot fit -- the phone. Above it the row is a
   table row, not a card, and the second `<tr>` per player was pure
   height. Matching threshold: `layout.css`'s priority-column collapse. */
@media (min-width: 621px) {
  .bb-table--rankings .bb-row-detail {
    display: none;
  }
}

/* PACK 2, Part 10 -- THE THIRD COLUMN TIER.
   `bb-col-secondary` carries the MARKET reading (MAV Rank / MAV %ile),
   which is a decision field and survives to the phone tier.
   `bb-col-tertiary` carries supporting evidence -- the unrounded Raw
   value, Opportunity, Confidence, Trajectory -- each of which is also
   published in full on the player's own page, one click from every row.

   MOBILE MECHANICAL CORRECTION (Fable Preproduction, §2.4 #1): this used
   to drop tertiary at `max-width: 1024px` while the row's own "All
   readings" recovery disclosure above only exists below `min-width:
   621px` -- a genuine breakpoint mismatch, not a density choice. Every
   tablet width from 621px to 1024px hid Opportunity/Confidence/
   Trajectory/Raw with NO recovery path at all: real evidence loss, not
   a space-saving trim. Re-widening the disclosure to cover that band
   would reintroduce the exact vertical-bloat regression Part 10 itself
   measured and fixed (a second `<tr>` per row turned 1182px of board
   into 7083px at 834px), so the fix instead aligns tertiary's own
   threshold DOWN to the same 620px phone tier every other priority
   column already uses -- a tablet now keeps every column inline (with
   `.bb-table-wrap`'s existing horizontal scroll as the fallback, the
   same accepted mechanism every table in this app already relies on),
   and nothing is hidden anywhere without the disclosure right there to
   recover it. This also happens to fix the identical mismatch on any
   other board that reuses this same class (e.g. Around The League's
   standings table), not audited separately. The THIRD selector below
   (`.bb-col-tertiary` with no tag qualifier) additionally fixes a
   related Around The League bug: the class was applied to a `<span>`
   nested inside a `<td>` rather than to the cell itself, so the old
   `td.bb-col-tertiary`/`th.bb-col-tertiary`-only selector never matched
   it and the full team name never hid on any screen size. */
@media (max-width: 620px) {
  .bb-table td.bb-col-tertiary,
  .bb-table th.bb-col-tertiary,
  .bb-table .bb-col-tertiary {
    display: none;
  }
}

/* --- top-bar dropdown menus --- */
/* Menu lists are hidden by class (not inline style) so app.js's
   `is-open` toggle can actually reveal them. */
.bb-menu-list {
  display: none;
  /* POST-DRAFT CORRECTION PACK: on a short desktop/laptop viewport a
     member with many connected leagues could hit the bottom of the
     screen with no way to scroll to the rest -- the menu grew to its
     full content height with no cap outside the phone-width media
     query below. Bounding it here (not just under ``max-width:620px``)
     fixes the desktop case too; the trigger's own inline
     `position:absolute` is untouched, so this only clips+scrolls the
     list's height, it doesn't reposition anything. */
  max-height: calc(100vh - var(--bb-topbar-height) - var(--bb-space-4));
  overflow-y: auto;
}
.bb-menu-list.is-open {
  display: block;
}
/* POST-DEPLOY UX CORRECTION PACK -- P1 COMMAND CENTER MOBILE SELECTOR
   OVERFLOW. The league/format menus (`base_shell.html`'s `#bb-league-
   menu-list`/`#bb-context-menu-list`) carry `position:absolute;
   left:0` or `right:0; min-width:240-280px` as INLINE styles, anchored
   to their own small trigger wrapper (`.bb-contextbar__group`), not the
   viewport. On a phone that wrapper can sit anywhere across the width
   of `.bb-contextbar`'s wrapped layout, so a fixed-min-width box
   anchored to ONE edge of it can run past either edge of a 390px
   screen -- observed running off the LEFT. `position:fixed` at this
   tier escapes that small ancestor's coordinate system entirely and
   anchors to the viewport instead, which is what actually guarantees
   no clipping regardless of where the trigger sits; the external
   `!important` here is required because the template's own inline
   `left:0`/`right:0`/`min-width` would otherwise win by specificity. */
@media (max-width: 620px) {
  /* SHELL CONVERGENCE (2026-09): this full-bleed overlay (fixed,
     edge-to-edge via `left`/`right`, its own `overflow-y: auto` scroller)
     is exactly the shape `tests/web/test_mobile_draft_path.py`'s
     `test_every_phone_tier_full_bleed_overlay_budgets_the_bottom_bar`
     guards -- the same defect class as the search panel above
     (`hq.css` `.bb-search__panel`), just never given the same fix. The
     old `max-height` ran to the viewport floor, so a long menu (many
     connected leagues, the full account menu) could size itself right
     over the fixed bottom nav and, being z-index 60 vs. the bar's 40,
     paint above it -- the bar visible-but-untappable underneath for as
     long as the menu stayed open. Budgeting the bottom nav's height plus
     the safe-area inset, the same two tokens every sibling overlay
     reads, ends the scroller where the bar begins instead. */
  .bb-menu-list {
    position: fixed !important;
    top: var(--bb-topbar-height) !important;
    left: var(--bb-space-3) !important;
    right: var(--bb-space-3) !important;
    min-width: 0 !important;
    max-width: none !important;
    width: auto !important;
    max-height: calc(
      100vh - var(--bb-topbar-height) - var(--bb-bottomnav-height) -
        var(--bb-safe-bottom) - var(--bb-space-4)
    );
    /* iOS Safari: dvh tracks the real viewport while the URL bar moves. */
    max-height: calc(
      100dvh - var(--bb-topbar-height) - var(--bb-bottomnav-height) -
        var(--bb-safe-bottom) - var(--bb-space-4)
    );
    overflow-y: auto;
  }
}

/* --- badges / stamps --- */
.bb-badge {
  display: inline-flex;
  align-items: center;
  gap: 4px;
  padding: 2px 8px;
  border-radius: var(--bb-radius-sm);
  font-family: var(--bb-font-mono);
  font-size: var(--bb-fs-micro);
  font-weight: 700;
  letter-spacing: 0.04em;
  border: 1px solid var(--bb-neutral-border);
  background: var(--bb-neutral-bg);
  color: var(--bb-neutral);
  white-space: nowrap;
}
.bb-badge--positive { border-color: var(--bb-positive-border); background: var(--bb-positive-bg); color: var(--bb-positive); }
.bb-badge--negative { border-color: var(--bb-negative-border); background: var(--bb-negative-bg); color: var(--bb-negative); }
.bb-badge--caution  { border-color: var(--bb-caution-border);  background: var(--bb-caution-bg);  color: var(--bb-caution); }
.bb-badge--info     { border-color: var(--bb-info-border);     background: var(--bb-info-bg);     color: var(--bb-info); }
.bb-badge--muted    { opacity: 0.6; }

.bb-badge--confidence-high   { border-color: var(--bb-positive-border); background: var(--bb-positive-bg); color: var(--bb-positive); }
.bb-badge--confidence-medium { border-color: var(--bb-caution-border);  background: var(--bb-caution-bg);  color: var(--bb-caution); }
.bb-badge--confidence-low    { border-color: var(--bb-negative-border); background: var(--bb-negative-bg); color: var(--bb-negative); }

.bb-stamp {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 4px 12px;
  border-radius: var(--bb-radius-sm);
  font-family: var(--bb-font-mono);
  font-size: var(--bb-fs-sm);
  font-weight: 700;
  letter-spacing: 0.05em;
  border: 1px solid var(--bb-border-strong);
}
.bb-stamp--fresh    { border-color: var(--bb-positive-border); background: var(--bb-positive-bg); color: var(--bb-positive); }
.bb-stamp--stale    { border-color: var(--bb-caution-border);  background: var(--bb-caution-bg);  color: var(--bb-caution); }
.bb-stamp--missing  { border-color: var(--bb-negative-border); background: var(--bb-negative-bg); color: var(--bb-negative); }
.bb-stamp--go            { border-color: var(--bb-positive-border); background: var(--bb-positive-bg); color: var(--bb-positive); }
.bb-stamp--go-limited    { border-color: var(--bb-caution-border);  background: var(--bb-caution-bg);  color: var(--bb-caution); }
.bb-stamp--stop          { border-color: var(--bb-negative-border); background: var(--bb-negative-bg); color: var(--bb-negative); }
.bb-stamp--locked   { border-color: var(--bb-neutral-border); background: var(--bb-neutral-bg); color: var(--bb-neutral); }

/* --- NFL team identity accent ---
   Restrained by design: colors THE ABBREVIATION TEXT alone, never a
   whole row/card, never a logo. Values are Blackbird's own curated
   display palette -- lightened or shifted off a team's literal brand
   hex wherever that hex reads too dark against the graphite surfaces
   above (several teams' navy/black primaries), following the same
   "curate once, by hand" convention as every other accent in this file
   rather than any runtime contrast computation. Blackbird purple
   (--bb-accent-blackbird) stays the ONLY interaction/navigation color
   everywhere else -- these are secondary identity accents only, never
   used on an interactive element. The class name is resolved by
   `team_normalization.py::team_color_class` (registered as the
   `team_color_class` Jinja global in `pages.py`) and rendered through
   the `ui.team_code()` macro -- no template branches on a team code
   itself. */
.bb-team-code {
  font-family: var(--bb-font-mono);
  font-weight: 700;
  letter-spacing: 0.02em;
}
.bb-team-code--muted { color: var(--bb-neutral); }
.bb-team-code--ari { color: #d1495a; }
.bb-team-code--atl { color: #c8443b; }
.bb-team-code--bal { color: #7c5ce0; }
.bb-team-code--buf { color: #3e7fe0; }
.bb-team-code--car { color: #29a0e0; }
.bb-team-code--chi { color: #e08a3c; }
.bb-team-code--cin { color: #fb6b35; }
.bb-team-code--cle { color: #ff6a33; }
.bb-team-code--dal { color: #4a7fc9; }
.bb-team-code--den { color: #ff8033; }
.bb-team-code--det { color: #4aa8e8; }
.bb-team-code--gb  { color: #e9b94a; }
.bb-team-code--hou { color: #d8556a; }
.bb-team-code--ind { color: #4f86c6; }
.bb-team-code--jax { color: #1fa6bd; }
.bb-team-code--kc  { color: #e23a4e; }
.bb-team-code--lac { color: #3aaceb; }
.bb-team-code--lar { color: #4a6fe0; }
.bb-team-code--lv  { color: #b7bec7; }
.bb-team-code--mia { color: #1fb8c4; }
.bb-team-code--min { color: #7a52c4; }
.bb-team-code--ne  { color: #c8455a; }
.bb-team-code--no  { color: #d9c08a; }
.bb-team-code--nyg { color: #4a6bb0; }
.bb-team-code--nyj { color: #2e9e70; }
.bb-team-code--phi { color: #2c9e93; }
.bb-team-code--pit { color: #e8b93c; }
.bb-team-code--sea { color: #6fcb3c; }
.bb-team-code--sf  { color: #d1494d; }
.bb-team-code--tb  { color: #e0342e; }
.bb-team-code--ten { color: #4a93d9; }
.bb-team-code--was { color: #b23a52; }

.bb-context-stamp {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  padding: var(--bb-space-1) var(--bb-space-3);
  border-radius: var(--bb-radius-pill);
  background: var(--bb-accent-bg);
  color: var(--bb-accent);
  font-family: var(--bb-font-mono);
  font-size: var(--bb-fs-sm);
  font-weight: 700;
  letter-spacing: 0.04em;
  border: 1px solid var(--bb-accent);
}

.bb-traj {
  font-family: var(--bb-font-mono);
  font-weight: 700;
}
.bb-traj--up { color: var(--bb-positive); }
.bb-traj--down { color: var(--bb-negative); }
.bb-traj--flat { color: var(--bb-text-tertiary); }

/* --- tabs --- */
.bb-tabs {
  display: flex;
  gap: var(--bb-space-1);
  border-bottom: 1px solid var(--bb-border);
  margin-bottom: var(--bb-space-4);
  flex-wrap: wrap;
}
.bb-tab {
  padding: var(--bb-space-2) var(--bb-space-4);
  font-size: var(--bb-fs-sm);
  font-weight: 600;
  color: var(--bb-text-secondary);
  border-bottom: 2px solid transparent;
  margin-bottom: -1px;
}
.bb-tab:hover {
  color: var(--bb-text-primary);
  text-decoration: none;
}
.bb-tab.is-active {
  color: var(--bb-accent);
  border-bottom-color: var(--bb-accent);
}

/* --- collapsible / drawer content --- */
.bb-drawer__section {
  margin-bottom: var(--bb-space-5);
}
.bb-drawer__section-title {
  margin-bottom: var(--bb-space-2);
}

details.bb-collapsible {
  border: 1px solid var(--bb-border);
  border-radius: var(--bb-radius-md);
  background: var(--bb-surface);
  padding: var(--bb-space-2) var(--bb-space-3);
}
details.bb-collapsible summary {
  cursor: pointer;
  font-weight: 600;
  font-size: var(--bb-fs-sm);
  list-style: none;
}
details.bb-collapsible summary::-webkit-details-marker {
  display: none;
}
details.bb-collapsible summary::before {
  content: "▸";
  display: inline-block;
  margin-right: var(--bb-space-2);
  transition: transform var(--bb-transition-fast);
}
details.bb-collapsible[open] summary::before {
  transform: rotate(90deg);
}
details.bb-collapsible .bb-collapsible__body {
  padding-top: var(--bb-space-2);
}

/* --- modal pattern --- */
.bb-modal-scrim {
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.6);
  display: flex;
  align-items: center;
  justify-content: center;
  z-index: 100;
}
.bb-modal {
  background: var(--bb-surface-raised);
  border: 1px solid var(--bb-border-strong);
  border-radius: var(--bb-radius-lg);
  box-shadow: var(--bb-shadow-lg);
  padding: var(--bb-space-5);
  max-width: 480px;
  width: 90vw;
}

/* --- loading skeletons --- */
.bb-skeleton {
  background: linear-gradient(90deg, var(--bb-surface) 25%, var(--bb-surface-raised) 37%, var(--bb-surface) 63%);
  background-size: 400% 100%;
  animation: bb-skeleton-shimmer 1.4s ease infinite;
  border-radius: var(--bb-radius-sm);
}
@keyframes bb-skeleton-shimmer {
  0% { background-position: 100% 50%; }
  100% { background-position: 0 50%; }
}
.bb-skeleton--line {
  height: 14px;
  margin: 6px 0;
}
.bb-skeleton--row {
  height: 36px;
  margin: 4px 0;
}
/* DW-3.2 Phase 1D -- FALSE LOADING STATES, root cause.
   An idle htmx indicator must be removed from LAYOUT and from the
   ACCESSIBILITY TREE. `display: none` is the only hiding mechanism that
   does both: htmx's own runtime-injected `.htmx-indicator{opacity:0}`
   hides it from EYES only, leaving "Searching…"/"Refreshing…"/
   "Evaluating this package…" permanently announced to screen readers --
   exactly the defect the authenticated audit reported on Dashboard,
   Rankings, Trade Desk, Team Intelligence, Draft Ops and Global Search.

   The selector is deliberately doubled (`.htmx-indicator.htmx-indicator`,
   specificity 0,2,0). The single-class form previously here (0,1,0) TIED
   with `.bb-loading{display:inline-flex}` in `hq.css` and LOST on source
   order, because `hq.css` loads after this sheet (see `base_shell.html`).
   Every `class="htmx-indicator bb-loading"` node in the app was therefore
   laid out, spinning and announced at idle. Doubling the class makes this
   contract immune to any later single-class display utility rather than
   depending on stylesheet load order. Do not lower it back to one class.

   The visible state keeps each element's OWN display mode: `.bb-loading`
   is an inline-flex row (spinner + label), everything else is a block. */
.htmx-indicator.htmx-indicator {
  display: none;
}
.htmx-request .htmx-indicator.bb-loading,
.htmx-request.htmx-indicator.bb-loading {
  display: inline-flex;
}
.htmx-request .htmx-indicator:not(.bb-loading),
.htmx-request.htmx-indicator:not(.bb-loading) {
  display: block;
}

/* --- empty / future-seam / error states --- */
.bb-empty-state {
  border: 1px dashed var(--bb-border-strong);
  border-radius: var(--bb-radius-lg);
  padding: var(--bb-space-5);
  text-align: left;
  color: var(--bb-text-secondary);
}
.bb-empty-state__title {
  color: var(--bb-text-primary);
  font-weight: 650;
  margin-bottom: var(--bb-space-1);
}


/* --- inline server-rendered SVG history charts (Research Terminal) ---
   No client-side charting library exists anywhere in this Web app -- every
   chart is a plain server-built <svg> (see `pages_research.render_history_
   svg`); these rules only style the polyline/dots it emits. */
.bb-chart {
  width: 100%;
  height: auto;
  display: block;
}
.bb-chart-line {
  stroke: var(--bb-accent);
  stroke-width: 2;
  vector-effect: non-scaling-stroke;
}
.bb-chart-dot {
  fill: var(--bb-accent);
}

.bb-banner {
  display: flex;
  align-items: center;
  gap: var(--bb-space-2);
  padding: var(--bb-space-3) var(--bb-space-4);
  border-radius: var(--bb-radius-md);
  font-size: var(--bb-fs-sm);
  margin-bottom: var(--bb-space-4);
}
.bb-banner--caution {
  background: var(--bb-caution-bg);
  border: 1px solid var(--bb-caution-border);
  color: var(--bb-caution);
}
.bb-banner--info {
  background: var(--bb-info-bg);
  border: 1px solid var(--bb-info-border);
  color: var(--bb-info);
}
.bb-banner--positive {
  background: var(--bb-positive-bg);
  border: 1px solid var(--bb-positive-border);
  color: var(--bb-positive);
}
.bb-banner--negative {
  background: var(--bb-negative-bg);
  border: 1px solid var(--bb-negative-border);
  color: var(--bb-negative);
}

/* BB// PROPRIETARY INSIGHT (Post-V1 Signature Visual Identity Pack,
   Parts 57-58). Ultraviolet, reserved for this one badge -- see
   `partials/ui.html`'s `proprietary_insight` macro docstring. */
.bb-proprietary {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  padding: 3px var(--bb-space-2);
  border-radius: var(--bb-radius-sm);
  background: var(--bb-proprietary-bg);
  border: 1px solid var(--bb-proprietary-border);
  font-size: var(--bb-fs-micro);
}
.bb-proprietary__stamp {
  font-family: var(--bb-font-mono);
  font-weight: var(--bb-fw-bold);
  color: var(--bb-proprietary);
}
.bb-proprietary__label {
  font-weight: var(--bb-fw-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--bb-proprietary);
}
.bb-proprietary__text {
  color: var(--bb-text-secondary);
}

/* --- tooltips (title attribute + CSS affordance) --- */
[title] {
  text-decoration-style: dotted;
}
.bb-help {
  border-bottom: 1px dotted var(--bb-text-tertiary);
  cursor: help;
}

/* --- forms (login) --- */
.bb-form-field {
  display: flex;
  flex-direction: column;
  gap: var(--bb-space-1);
  margin-bottom: var(--bb-space-4);
}
.bb-form-field label {
  font-size: var(--bb-fs-micro);
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--bb-text-tertiary);
}
.bb-input {
  background: var(--bb-bg-elevated);
  border: 1px solid var(--bb-border-strong);
  border-radius: var(--bb-radius-md);
  padding: var(--bb-space-2) var(--bb-space-3);
  color: var(--bb-text-primary);
  font-family: var(--bb-font-ui);
  font-size: var(--bb-fs-base);
}
.bb-input:focus {
  outline: none;
  border-color: var(--bb-accent);
}

.bb-auth-shell {
  min-height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  background: var(--bb-bg);
  padding: var(--bb-space-4);
}
.bb-auth-card {
  width: 100%;
  max-width: 380px;
  background: var(--bb-surface);
  border: 1px solid var(--bb-border);
  border-radius: var(--bb-radius-lg);
  padding: var(--bb-space-6);
  box-shadow: var(--bb-shadow-md);
}
.bb-auth-card__wordmark {
  text-align: center;
  margin-bottom: var(--bb-space-5);
}

/* --- Trade Desk (Campaign 021.0 F2) --- */
.bb-trade-grid {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--bb-space-4);
}
@media (max-width: 900px) {
  .bb-trade-grid {
    grid-template-columns: 1fr;
  }
}
.bb-trade-side-assets {
  display: flex;
  flex-direction: column;
  gap: var(--bb-space-2);
  margin-top: var(--bb-space-3);
}
.bb-trade-chip {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--bb-space-2);
  min-height: 44px;
  padding: var(--bb-space-2) var(--bb-space-3);
  background: var(--bb-surface-raised);
  border: 1px solid var(--bb-border-strong);
  border-radius: var(--bb-radius-md);
}
.bb-trade-chip__remove {
  flex-shrink: 0;
  min-width: 32px;
  min-height: 32px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border-radius: var(--bb-radius-sm);
  border: 1px solid var(--bb-border-strong);
  background: transparent;
  color: var(--bb-text-secondary);
  cursor: pointer;
}
.bb-trade-chip__remove:hover {
  background: var(--bb-negative-bg);
  border-color: var(--bb-negative-border);
  color: var(--bb-negative);
}
.bb-trade-search-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--bb-space-2);
  width: 100%;
  min-height: 44px;
  padding: var(--bb-space-2) var(--bb-space-3);
  border: none;
  border-bottom: 1px solid var(--bb-border-subtle);
  background: transparent;
  color: var(--bb-text-primary);
  font-family: var(--bb-font-ui);
  font-size: var(--bb-fs-sm);
  text-align: left;
  cursor: pointer;
}
.bb-trade-search-row:hover {
  background: var(--bb-surface-hover);
}
.bb-stamp--lg {
  font-size: var(--bb-fs-2xl);
  padding: var(--bb-space-3) var(--bb-space-5);
}

/* --- Draft Ops (Campaign 022.0) --- */
/* Section 11: primary decision panel + board on the left/center, roster/
   queue/alternatives secondary on the right. Desktop-first 2-column grid,
   collapsing to a single column under 900px -- same breakpoint every
   other dense Blackbird layout (Trade Desk, Rankings) already uses. */
.bb-draft-grid {
  display: grid;
  grid-template-columns: 2fr 1fr;
  gap: var(--bb-space-4);
  align-items: start;
}
@media (max-width: 900px) {
  .bb-draft-grid {
    grid-template-columns: 1fr;
  }
  /* Section 30 Field Terminal priority: current pick/on-clock +
     recommendation must be the FIRST thing on a phone -- the board and
     the secondary column both reorder below it via the source order
     already being primary-panel-first in the template; this rule only
     needs to guarantee the secondary column never claims visual
     priority via its own grid placement on a narrow viewport. */
  .bb-draft-grid > .bb-draft-secondary {
    order: 2;
  }
  .bb-draft-grid > .bb-draft-primary {
    order: 1;
  }
}
.bb-draft-primary {
  display: flex;
  flex-direction: column;
  gap: var(--bb-space-4);
}
.bb-draft-secondary {
  display: flex;
  flex-direction: column;
  gap: var(--bb-space-4);
}
.bb-draft-header {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--bb-space-3);
}
@media (max-width: 640px) {
  .bb-draft-header {
    grid-template-columns: repeat(2, 1fr);
  }
}
.bb-draft-candidate {
  display: flex;
  flex-direction: column;
  gap: var(--bb-space-2);
  padding: var(--bb-space-3);
  border: 1px solid var(--bb-border-strong);
  border-radius: var(--bb-radius-md);
  background: var(--bb-surface-raised);
}
.bb-draft-candidate__row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--bb-space-2);
  flex-wrap: wrap;
}
.bb-draft-candidate__name {
  font-weight: 650;
  color: var(--bb-text-primary);
}
.bb-draft-pick-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--bb-space-2);
  padding: var(--bb-space-2) var(--bb-space-3);
  min-height: 40px;
  border-bottom: 1px solid var(--bb-border-subtle);
  font-size: var(--bb-fs-sm);
}
.bb-draft-pick-row--user {
  background: var(--bb-accent-bg);
  border-radius: var(--bb-radius-sm);
}
.bb-draft-pick-row--current {
  background: var(--bb-caution-bg);
  border-radius: var(--bb-radius-sm);
}
/* POST-DEPLOY UX CORRECTION PACK -- P1 TRADE WHY MOBILE OVERFLOW.
   `partials/trade_intelligence.html`'s WHY row packs a badge, a
   free-text `reason.statement` and a joined evidence list into the
   SAME `.bb-draft-pick-row` used elsewhere for a two-column standings/
   pick-landscape row -- `justify-content:space-between` with no
   `flex-wrap` reliably overflows three children at 390px. Scoped to
   `--why` (not the base class) so standings/pick-landscape rows this
   pack does not touch keep their existing two-column layout. */
@media (max-width: 620px) {
  .bb-draft-pick-row--why {
    flex-wrap: wrap;
    justify-content: flex-start;
    align-items: flex-start;
    row-gap: var(--bb-space-1);
  }
  .bb-draft-pick-row--why > span {
    flex: 1 1 100%;
    min-width: 0;
    white-space: normal;
    word-break: break-word;
  }
  .bb-draft-pick-row--why > .bb-badge {
    flex: 0 0 auto;
  }
}
/* Stamp variants for the DRAFT/LEAN_DRAFT/WAIT/PASS/INSUFFICIENT_EVIDENCE
   vocabulary -- reuses the same positive/caution/negative/locked tokens
   every other stamp in this file already draws from, never a new color
   scale. */
.bb-stamp--draft-wait { border-color: var(--bb-caution-border); background: var(--bb-caution-bg); color: var(--bb-caution); }

/* --- misc utility --- */
.bb-flex { display: flex; align-items: center; gap: var(--bb-space-2); }
.bb-flex--between { display: flex; align-items: center; justify-content: space-between; gap: var(--bb-space-2); }
.bb-stack { display: flex; flex-direction: column; gap: var(--bb-space-3); }
.bb-mt-4 { margin-top: var(--bb-space-4); }
.bb-mb-4 { margin-bottom: var(--bb-space-4); }
.bb-text-right { text-align: right; }
.bb-w-full { width: 100%; }

/* --- player identity header (Campaign 021.0 F3, Section 24) ---
   Media slot (fallback avatar, provider-ready) + name/position/context
   center + the existing signal tiles stay to the right/below. Chip
   modifiers reuse the same semantic tokens every other badge/chip in
   this file already draws from -- no new colors introduced. */
.bb-player-header {
  display: flex;
  align-items: center;
  gap: var(--bb-space-5);
  flex-wrap: wrap;
  margin-bottom: var(--bb-space-4);
}
.bb-player-header__media {
  flex: 0 0 auto;
}
.bb-player-header__identity {
  flex: 1 1 260px;
  min-width: 200px;
}
.bb-player-header__name {
  margin: 0 0 var(--bb-space-1);
  font-size: var(--bb-fs-2xl);
  font-weight: 650;
  color: var(--bb-text-primary);
  line-height: 1.1;
}
.bb-player-header__meta {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--bb-space-2);
  color: var(--bb-text-secondary);
}
.bb-player-header__stamps {
  display: flex;
  align-items: center;
  gap: var(--bb-space-3);
  flex-wrap: wrap;
}
@media (max-width: 620px) {
  /* PACK 3: stacking a row-gap of `--bb-space-5` (the row layout's
     horizontal breathing room) between three now-block-level children
     is the deferred mobile whitespace defect -- avatar, identity and
     stamps read as three sparse, disconnected sections. The row keeps
     `flex-direction: row` (avatar beside identity, as on desktop) and
     only the stamps wrap to their own line; the gap tightens to match. */
  .bb-player-header {
    flex-wrap: wrap;
    align-items: flex-start;
    gap: var(--bb-space-3);
    text-align: left;
    margin-bottom: var(--bb-space-3);
  }
  .bb-player-header__media {
    order: 0;
  }
  .bb-player-header__identity {
    order: 1;
    flex: 1 1 auto;
    min-width: 0;
  }
  .bb-player-header__stamps {
    order: 2;
    width: 100%;
    margin-top: 0;
  }
  .bb-dossier-hero .bb-player-header {
    margin-bottom: var(--bb-space-3);
  }
}

/* --- fallback avatar (no network, deterministic initials) ---
   `blackbird.services.player_media` is the ONLY producer of the initials/
   position this renders -- see docs/PLAYER_MEDIA_LEGAL_DECISION.md. The
   media slot is provider-ready: an `<img>` branch exists in the template
   for a future lawful headshot provider, but under HEADSHOTS-HOLD only
   `.bb-avatar` below is ever painted. */
.bb-avatar {
  width: 76px;
  height: 76px;
  border-radius: var(--bb-radius-lg);
  border: 2px solid var(--bb-border-strong);
  background: var(--bb-surface-raised);
  color: var(--bb-text-primary);
  display: flex;
  align-items: center;
  justify-content: center;
  font-family: var(--bb-font-mono);
  font-weight: 700;
  font-size: var(--bb-fs-lg);
  letter-spacing: 0.02em;
  user-select: none;
}
.bb-avatar--sm {
  width: 40px;
  height: 40px;
  font-size: var(--bb-fs-sm);
  border-radius: var(--bb-radius-md);
}
.bb-avatar--qb { border-color: var(--bb-info-border); background: var(--bb-info-bg); color: var(--bb-info); }
.bb-avatar--rb { border-color: var(--bb-positive-border); background: var(--bb-positive-bg); color: var(--bb-positive); }
.bb-avatar--wr { border-color: var(--bb-accent); background: var(--bb-accent-bg); color: var(--bb-accent); }
.bb-avatar--te { border-color: var(--bb-caution-border); background: var(--bb-caution-bg); color: var(--bb-caution); }
.bb-avatar--unknown { border-color: var(--bb-neutral-border); background: var(--bb-neutral-bg); color: var(--bb-neutral); }

/* --- chip color modifiers (Section 19 enrichment panels) -- `.bb-chip`'s
   base rule above is hardcoded to the caution tokens (evidence
   limitations); these modifiers let the same shape carry a different
   semantic meaning (roster relevance, mover direction) without a second
   markup pattern. */
.bb-chip--positive { border-color: var(--bb-positive-border); background: var(--bb-positive-bg); color: var(--bb-positive); }
.bb-chip--neutral { border-color: var(--bb-neutral-border); background: var(--bb-neutral-bg); color: var(--bb-neutral); }
.bb-chip--negative { border-color: var(--bb-negative-border); background: var(--bb-negative-bg); color: var(--bb-negative); }


/* =====================================================================
   TOUCH TARGET FLOOR -- PRIMITIVES (DW-3.2 Phase 1F)
   =====================================================================
   WHY THIS EXISTS. An authenticated production audit measured the
   EFFECTIVE hit area of every interactive control in the app. Twelve
   surfaces came back under the ~40px a finger needs; the worst were
   dismissals and compact controls sized entirely by their own text
   (recipient clear 12x10, disclosure summaries 28, icon buttons 29).
   Nothing here changes what any of them LOOK like -- the visible glyph
   is allowed to stay small, only the box that receives the tap grows.

   WHY `pointer: coarse` AND NOT ONLY A WIDTH. This is an information-
   dense intelligence product: a 44px floor applied to a mouse-driven
   desktop board would cost rows of visible data per screen, which is a
   worse regression than the one being fixed. But width alone does not
   answer "is this a finger" either -- the tablet tier (901-1200px) is
   where `layout.css` already turns the evidence drawer into a sheet, so
   an iPad in landscape was showing sheet controls sized for a mouse.
   The condition is therefore the union of the two: the codebase's own
   900px phone breakpoint (unchanged, still authoritative for layout)
   OR any coarse pointer at any width. A desktop mouse at 1440px matches
   neither, and desktop density is untouched by every rule below.

   THE FLOOR IS `--bb-tap-min` (40px), the token `tokens.css` has
   declared for exactly this since 020.0 and which `.bb-navitem`,
   `.bb-result`, `.bb-backpath` and the bottom nav already read. This
   pack adds no second, competing number.

   Shell, board and utility-link controls live in `hq.css`'s matching
   section -- those classes are DEFINED there, and a rule here would
   lose to them on source order. This file carries only the primitives
   it owns. */
@media (pointer: coarse), (max-width: 900px) {
  /* The two chip dismissals, finally agreeing with each other. Both sit
     inside a chip that is already at least this tall (`.bb-trade-chip`
     is `min-height: 44px`) or is a lone chip in a composer, so neither
     one can push a dense row around. */
  .bb-people-clear,
  .bb-trade-chip__remove {
    min-width: var(--bb-tap-min);
    min-height: var(--bb-tap-min);
  }

  /* Rows and tabs that were sized by their text plus a little padding.
     `display: flex` is what makes the added height become hit area
     instead of dead space under a centred line box -- `.bb-people-option`
     and `.bb-disclosure > summary` already prove the pattern. */
  .bb-tab,
  details.bb-collapsible summary {
    display: flex;
    align-items: center;
  }
  .bb-tab,
  .bb-people-option,
  details.bb-collapsible summary {
    min-height: var(--bb-tap-min);
  }
}
