/* mobile portrait — clearance for fixed bottom tab bar; JS ResizeObserver overrides at runtime */
body { padding-bottom: 48px; transition: padding-top 0.4s ease; }

/* legend outlier dots — Curaçao and France, either side of the diverging bar — see
   #legend-outlier-pos-wrap in index.html / _updateLegendOutlier(). */
.legend-outlier-wrap { height: 10px; }
.legend-outlier-dot { width: 10px; height: 10px; border-radius: 50%; background: #000; }
#legend-outlier-count, #legend-outlier-count-pos { font-size: 9px; color: var(--color-dim); margin-top: 3px; }

/* Same --exp-accent/--imp-accent tokens the elo-ranking pills use (css/taxonomy.css's :root
   block) — js/index.js reads them back via getComputedStyle for the arcs themselves, so
   this page's map arcs, tooltip counts, and player-table headers all agree on one color pair. */
.color-exp { color: var(--exp-accent); }
.color-imp { color: var(--imp-accent); }

/* page header — #sidebar-host is position:absolute (see index.html), taken out of the
   grid flow so it can overlap the map when collapsed instead of pushing it down. That leaves
   #page-quotes as the grid's only real item; its own height drives #page-header's — see
   js/index.js's _computeHeaderHeight()/--page-header-h. */
#page-header { position: fixed; top: 0; left: 0; right: 0; z-index: 200; background: var(--fixed-element-bg); padding: 0; display: grid; }
#page-header ~ * { user-select: none; -webkit-user-select: none; }
#page-quotes { grid-row: 1; grid-column: 1; padding: 0.4rem 1.5rem; padding-left: var(--vert-gutter); padding-right: var(--vert-gutter); align-self: stretch; margin: 0 !important; position: relative; display: flex; flex-direction: column; }

/* page quote (pq-) */
.pq-text { font-family: Georgia, 'Times New Roman', serif; font-size: 16px; color: #555; margin: 0 0 3px; line-height: 1.45; }
.pq-attr { font-size: 10px; color: var(--text-muted); margin: 0; }
.pq-author { font-variant: small-caps; letter-spacing: 0.07em; color: #666; }
.pq-attr cite { font-style: italic; }
.pq-wrap { overflow: hidden; position: relative; }
.pq-panel { width: 100%; flex-shrink: 0; z-index: 1; }
.pq-prev { position: absolute; top: 0; left: 0; transform: translateX(-100%); }
.pq-cur { position: relative; }
/* margin-top:auto is the natural-height default (no extra push); js/index.js's
   _computeHeaderHeight() overrides it inline with a definite px value whenever the sidebar is
   expanded and taller than the quote, so this row sits flush against the map instead of a gap.
   0.4s to match #map-container's own top transition (map-container.css) — same duration so the
   dots and the map move in step instead of one lagging the other. */
.pq-dots { display: flex; gap: 5px; margin-top: auto; padding-top: 4px; user-select: none; transition: margin-top 0.4s ease; }
.pq-dot { font-size: 12px; line-height: 1; color: #ccc; transition: color .25s; cursor: pointer; font-weight: bolder;}
.pq-dot.active { color: #888; }

/* bottom panel */
#bottom-panel {
  position: fixed !important;
  bottom: 0; left: 0; right: 0;
  z-index: 100;
  /* background: var(--fixed-element-bg); */
  background-color: transparent;
  border-top: none;
  padding-left: 0 !important;
  padding-right: 0 !important;
  display: flex; flex-direction: column;
}
#selection-panel { order: 2; background-color: transparent; border-bottom: none; }
/* Fixture selection (js/index.js's _updateSelectionPanel) renders two .nav-item — one per
   team. justify-content-evenly (index.html) is what centers a lone .nav-item in the
   single-team case, but with two, it can't be trusted to butt them together at exactly the
   panel's own center: it distributes free space, so unless both pills happen to render at the
   exact same width, the gap between them (and so their shared edge) drifts off-center. Giving
   both flex-basis 50% instead makes the two .nav-item boxes (not their content — see below) fill
   the row completely (50% + 50% = 100%), leaving no free space left for justify-content-evenly
   to distribute: the boundary between the two boxes always lands exactly at the panel's own
   center, regardless of either pill's own content width.
   Each .nav-item box is deliberately wider than its pill needs though — the pill itself must
   stay content-driven width, not stretched to fill its half, and hug the boundary (the side
   facing the other team) rather than centering within its own half. Each .nav-item is turned
   into its own flex container for this: .selection-panel-row keeps flex's default flex-grow:0
   (stays content-sized, doesn't stretch to fill the 50% box) while justify-content pins it to
   whichever edge faces the boundary — flex-end for the left pill, flex-start for the right one.
   min-width:0 (both the .nav-item and .selection-panel-row) overrides the flex default
   (min-width:auto, refuses to shrink below content) — needed only for the rare case a name is
   actually too long for its own half, so it truncates via .selection-panel-row's own ellipsis
   instead of overflowing past the boundary or the panel's own edge.
   :has(.nav-item + .nav-item) scopes all of this to the two-pill case only, leaving the
   single-team pill's own content-sized, centered look untouched. */
#selection-panel:has(.nav-item + .nav-item) .nav-item {
  flex: 1 1 50%; min-width: 0; display: flex;
}
#selection-panel:has(.nav-item + .nav-item) .nav-item:first-child { justify-content: flex-end; }
#selection-panel:has(.nav-item + .nav-item) .nav-item:last-child { justify-content: flex-start; }
#selection-panel:has(.nav-item + .nav-item) .selection-panel-row { min-width: 0; }
/* .selection-panel-row is itself a flex row of exactly 2 children now: .sp-text (the truncatable
   name/capital/[population] '·'-joined text) and the close ✕ (js/index.js's
   _updateSelectionPanel) — flex-shrink:0 on the close button (below) is what this buys over the
   row's own previous single-inline-flow approach: before, the close ✕ was just the last inline
   node in the same truncating flow as the text, so once that text overflowed, the ✕ itself ended
   up past the ellipsis cutoff — pushed out of the visible box entirely, not just visually
   crowded. Splitting text and button into two flex children keeps the button's own box off
   limits to the truncation; only .sp-text (flex:1 1 auto, min-width:0) shrinks and ellipsizes.
   text-overflow:ellipsis still needs a plain inline flow to operate on — d-flex/gap-1 across
   *multiple* items (join()'s own separator spans providing the visual spacing) would clip
   whichever child hits the edge with no "…", not truncate as a whole the way one single flex
   child holding the entire joined string does. That's why the split is exactly 2 flex children
   (text run, button), not one flex child per name/separator/capital/population piece. */
.selection-panel-row { display: flex; align-items: center; }
.selection-panel-row .sp-text { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; min-width: 0; flex: 1 1 auto; text-align: center; }
/* Left pill only (js/index.js's own field-order reversal, see _updateSelectionPanel's
   comment: population · capital · name, so the country name — the field that matters most, right
   on the boundary shared with the other pill — sits at the very end of the string). Plain
   text-overflow:ellipsis always truncates the *end* of a line, which for this pill specifically
   means chopping the name itself once the row runs out of room, keeping the least useful field
   (population) fully intact instead. direction:rtl flips which end the browser treats as
   "overflow start" for truncation purposes only — the actual (LTR) text inside still renders
   left-to-right normally, per the Unicode bidi algorithm, it's only the ellipsis placement that
   moves to the front. text-align:left keeps the (still LTR) text hugging the left edge of the
   box rather than sliding right the way plain RTL body text would. */
#selection-panel:has(.nav-item + .nav-item) .nav-item:first-child .sp-text { direction: rtl; text-align: left; }
.selection-panel-row .btn-close { flex-shrink: 0; }
.selection-panel-row .sp-sep { padding: 0 4px; }
#bottomTabList { order: 99; background: var(--fixed-element-bg); }
/* #bottomTabList sits at the bottom of the screen (content above, tabs below) — Bootstrap's
   .nav-tabs assumes the opposite (tabs above content, e.g. the docs example), so every
   top/bottom-relative treatment needs mirroring: the container's baseline border moves from
   border-bottom to border-top, each .nav-link's rounded corners move from the top pair to the
   bottom pair, the negative margin that tucks it against that baseline moves from margin-bottom
   to margin-top, and the active/hover border-color's "hidden edge" (matching the color behind
   it — body background for the active tab, so it visually merges into the pane above instead of
   the pane below) moves from the bottom edge to the top edge. Values reuse Bootstrap's own
   --bs-nav-tabs-* custom properties (the same ones the base .nav-tabs rule reads), just with
   top/bottom swapped in the 3-value border-color shorthands (top / right&left / bottom).
   border-*-radius/margin aren't exposed as variables, so those need direct property overrides. */
#bottomTabList.nav-tabs {
  border-bottom: 0;
  border-top: var(--bs-nav-tabs-border-width) solid var(--bs-nav-tabs-border-color);
  --bs-nav-tabs-link-hover-border-color: var(--bs-border-color) var(--bs-secondary-bg) var(--bs-secondary-bg);
  --bs-nav-tabs-link-active-border-color: var(--bs-body-bg) var(--bs-border-color) var(--bs-border-color);
}
#bottomTabList.nav-tabs .nav-link {
  margin-bottom: 0;
  margin-top: calc(-1 * var(--bs-nav-tabs-border-width));
  border-top-left-radius: 0;
  border-top-right-radius: 0;
  border-bottom-left-radius: var(--bs-nav-tabs-border-radius);
  border-bottom-right-radius: var(--bs-nav-tabs-border-radius);
}
/* #tab-connector — see its own HTML comment. position:relative here is what its absolute
   left/width (set in JS, js/index.js's _updateTabConnector) is measured against. */
#bottomTabList { position: relative; }
#tab-connector {
  position: absolute;
  top: 50%;
  height: 0;
  border-top: 2px dotted var(--text-muted);
  transform: translateY(-50%);
  pointer-events: none;
  z-index: 0;
}
#bottomTabContent {
    padding-left: var(--vert-gutter) !important;
    padding-right: var(--vert-gutter) !important;
    overflow-x: hidden; 
}
.tab-footer-panel { overflow: hidden; max-height: 200px; transition: max-height 0.5s ease; }
.tab-footer-panel.collapsed { max-height: 0; }

/* tooltip (tt-) */
#tooltip {
  position: absolute; pointer-events: none; display: none;
  background: var(--fixed-element-bg); border: 1px solid var(--border);
  border-radius: 8px; padding: 12px 14px; font-size: 12px;
  color: var(--color-default); max-width: 540px;
  box-shadow: 0 4px 16px rgba(0,0,0,.10);
  z-index: 1000;
}
.tt-columns { margin-top: 2px; }
.tt-vdiv { width: 1px; background: var(--border); flex-shrink: 0; margin: 0 10px; }
.tt-name { font-size: 14px; font-weight: 500; margin-bottom: 2px; justify-content: space-between; }
.tt-pop { font-size: 10px; color: var(--text-muted); }
.tt-rank { font-size: 10px; color: var(--text-muted); margin-left: 6px; }
.tt-flag { width: 20px; height: 20px; }
.tt-count { font-size: 28px; font-weight: 500; line-height: 1; margin: 4px 0 2px; }
.tt-count-row { margin-bottom: 2px; }
.tt-count-row .tt-count { margin-bottom: 0; }
.tt-count-row .tt-sub { margin-bottom: 0; }
.tt-label { font-size: 11px; color: var(--text-muted); margin-bottom: 2px; }
.tt-not-qualified { font-size: 10px; color: var(--text-muted); margin-bottom: 6px; }
.tt-sub { font-size: 10px; color: var(--text-muted); margin-bottom: 6px; }
.tt-countries { font-size: 12px; color: #777; border-bottom: 1px solid var(--border-strong); padding-bottom: 6px; }
.tt-player { font-size: 10px; color: #555; padding: 3px 0; border-top: 1px solid var(--bg-hover); display: flex; justify-content: space-between; gap: 8px; }
.tt-countries + .tt-players .tt-player:first-child { border-top: none; }
.tt-country { color: var(--text-muted); font-size: 10px; }
.tt-more-label { color: var(--text-muted); font-size: 10px; padding-top: 6px; }
.tt-players.tt-more::after { content: 'etc.'; display: block; text-align: left; font-style: italic; color: var(--text-muted); font-size: 10px; padding-top: 2px; }
.tt-divider { border-top: 1px solid var(--border); margin: 8px 0 4px; }

/* scroll-to-top / zoom controls */
#scroll-top-btn { position: absolute; right: 12px; top: calc(-26px - 8px); cursor: pointer; }
/* Same round icon-button look as the stage carousel's prev/next controls (css/global.css's
   .elo-viz .carousel-control-prev-icon/-next-icon) — border-radius:50%, var(--bg-button)
   background, opacity 0.35 fading to 0.9 on hover — instead of the plain rounded-square look
   #zoom-reset/#zoom-span share (map-container.css). #bottom-panel #scroll-top-btn
   (two-ID descendant selector) is needed to out-specificity that shared rule regardless of
   stylesheet load order — map-container.css loads after this file.
   color:#1C274C matches those same icons' fixed navy stroke — the shared rule's inherited
   var(--text-muted) (#999) all but disappeared against var(--bg-button) at opacity 0.35, an
   effective-contrast problem the carousel icons don't have since theirs is a background-image
   asset already baked to that navy, not a currentColor SVG inheriting a muted gray. */
#bottom-panel #scroll-top-btn {
  width: 16px; height: 16px; padding: 7px; box-sizing: content-box;
  background-color: var(--bg-button);
  border: 1px solid var(--border); border-radius: 50%;
  color: #1C274C;
  opacity: 0.667;
}
/* Fills the same 16x16 content box the carousel icons' background-image fills — the inline SVG
   had its own tiny hardcoded width/height (10x7) sized for the old plain-icon-no-chrome look, so
   even once the button box itself matched, the glyph inside stayed too small for it. viewBox is
   still what preserves the chevron's own proportions within this box (SVG default
   preserveAspectRatio="xMidYMid meet" — no distortion). */
#bottom-panel #scroll-top-btn svg {
  width: 16px; height: 16px;
}
#bottom-panel #scroll-top-btn:hover { opacity: 0.9; }
#zoom-reset, #zoom-span { cursor: pointer; }
#zoom-reset:hover:not(:disabled), #zoom-span:hover:not(:disabled) { color: #000; opacity: 0.8; }

#bottomTabList .nav-link .tab-icon { height: 24px; min-height: 24px; max-height: 24px; width: auto; flex-shrink: 0; }

/* display:flex on every nav-link (not just #tab-players-btn) — a plain block .nav-link sizes
   its auto height off the *line box* (font-size × line-height), not off an inline <img> child's
   own height, so tab-teams-btn/tab-tournament-btn end up a few px taller than their 24px icon
   (a vertical-align:middle/line-height artifact). Flex sizing ignores line-height entirely,
   so this also fixes centering, not just height. */
#tab-teams-btn, #tab-tournament-btn, #tab-players-btn { min-width: 0; justify-content: center; display: flex; align-items: center; gap: 4px; cursor: pointer; padding: 2px 0;}
/* White requires BOTH: #tab-players-btn is the tab currently being viewed (.active, Bootstrap's
   own Tab-managed class) AND there's a selection to show (.tpb-selection-open, toggled by
   js/index.js's _updateSelectionPanel — dim mode or a fixture selection alike, tracked
   there since that's the one place already computing "is there a selection to show" for every
   path that can change it). Neither alone is enough: .active alone (an earlier version of this
   rule) painted it white even with nothing selected (the ambient "N countries" idle view);
   .tpb-selection-open alone (a later, also-wrong version) painted it white even while looking at
   a completely different tab, which read as a stray highlight rather than "this is what you're
   looking at". With either half missing, #tab-players-btn falls back to the same plain nav-link
   look #tab-teams-btn/#tab-tournament-btn get whenever they're not themselves the active tab —
   no separate rule needed for that side, it's just the default Bootstrap treatment neither of
   them overrides either. #id + 2 classes beats Bootstrap's own .nav-tabs .nav-link.active on
   specificity, so this wins even when every class is present at once. */
#tab-players-btn.active.tpb-selection-open { background-color: white; }
/* "born in" cell, only when its birthplace actually geocoded (js/index.js's
   _allPlayersRow) — click pans/zooms the map to that city at max zoom and shows its tooltip
   (_zoomToCity). No visual affordance beyond the cursor — .pt-born's existing muted styling
   already reads as secondary/interactive-adjacent next to the name column's own Wikipedia link. */
.pt-born--clickable { cursor: pointer; }

/* "plays for" cell, only when that team is a qualified country (js/index.js's
   _allPlayersRow) — click selects it via activateCountry, same as clicking its flag on the map
   or its Elo pill. Non-qualified nations (p.nation with no QUALIFIED_BY_NAME entry) aren't
   selectable, so the cell stays inert there — same reasoning as .pt-born--clickable above. */
.pt-caps--clickable { cursor: pointer; }

/* Birth-city dots (js/index.js's _updatePlayerCityDots) — hover shows a tooltip, click
   selects the matching table row(s) below; a plain pointer signals that without needing to
   touch .standalone-dot's own (unstyled) default, which is also reused by Cape Verde/Curaçao's
   unrelated country markers. */
.city-dots circle { cursor: pointer; }

/* Persistent "selected" row(s) in #tab-players' table (js/index.js's _selectedPids,
   rendered declaratively per-row by _allPlayersRow — not toggled imperatively, so a re-render
   for any other reason never drops it). Clicking a "born in" cell selects just that one row (and
   pans/zooms the map there); clicking a city dot on the map selects every row sharing that city
   instead. Either action replaces the previous selection outright.
   Background tint + a left accent bar on the first cell — the same purple as the map's own city
   dots (CITY_DOT_COLOR), tying a selected row visually back to "the dot that selected it". This
   pairing (tinted row + left bar) is the standard selected-row treatment in most data-grid/table
   UIs (e.g. Gmail's read/unread bar): distinct from hover, doesn't reflow the row like a real
   border would, and reads correctly against table-striped's own alternating background.
   box-shadow: inset, not border-left — a real border shifts cell content/width; inset doesn't. */
#tab-players .pt-table tbody tr { transition: background-color 0.3s ease; }
#tab-players .pt-table tbody tr.pt-row-selected { background-color: rgba(124, 58, 237, 0.12); }
#tab-players .pt-table tbody tr.pt-row-selected > td:first-child { box-shadow: inset 3px 0 0 #7c3aed; }

/* #bottomTabContent panes — shown/hidden purely off the .active class Bootstrap's Tab component
   toggles on data-bs-target (js/index.js's show.bs.tab listener), replacing the old
   pane.hidden attribute toggle. */
#bottomTabContent > [id] { display: none; }
#bottomTabContent > [id].active { display: block; }

/* #tab-players active (js/index.js's _playersMapActive) — hides every country flag and the
   dim-mode arcs group, whether or not a selection is active underneath; the map is birth-city
   dots only while this tab is showing. Deliberately a plain display:none via a class toggle on
   the zoom transform group, NOT animateFlagHidden's visibility/data-hidden-target mechanism:
   that attribute pair is also _visibleQualifiedIds()'s own source of truth for "which teams
   currently pass the sidebar filter" (read by the ambient player table too) — hiding every flag
   through it would permanently zero out that filter-visible set for every later caller, not just
   this one paint. A separate CSS-only hide leaves it untouched, so dimState/_ptFocusIds are
   still exactly what they were the instant this class is toggled off — e.g. switching between
   the ambient and a focused view while staying on tab-players never has to rebuild anything
   this rule was hiding. (_switchTab itself already clears any active selection on the way *out*
   to tab-teams/tab-tournament, unrelated to this rule — see its own comment.) */
g.city-mode .flag-qualified, g.city-mode .arcs-group { display: none; }
