/* BOS Taxon Tabs page — layout chrome only. Reused sections (specimens,
   traits, relationships, depositories, literature, resources, subtaxa) keep
   their bos-taxon.css styling; this file only styles the new layout: the
   collapsible tree drawer, the top gallery+info row, and the tab bar. */

/* grid-template-columns still transitions, so #bttMain's width animates in
   sync with the drawer's own slide (below) — safe now because
   .btt-drawer-inner no longer reflows as this track resizes (fixed pixel
   width, not a percentage — see below), unlike before: the tree content
   used to reflow into an ever-narrower column as the track shrank, and its
   wrapped height ballooned well past normal mid-transition; since
   #bttDrawer had no fixed height of its own, that inflated height drove
   the grid row (and the whole page) taller, leaving a large gap below the
   actual tab content once collapsed. */
#bttPage {
  display: grid;
  grid-template-columns: 0 1fr;
  transition: grid-template-columns 0.2s ease;
}
#bttPage.btt-drawer-open {
  /* 344px fallback matches WRAP_SAFE_W in bttSizeDrawerToFit() (bos-taxon-
     tabs.js) — both exist so the drawer never renders at a width that wraps
     .tree-row.ancestor (the "@container sidebar (max-width: 340px)" rule,
     bos-taxon.css) before JS has had a chance to set --btt-drawer-w itself. */
  grid-template-columns: var(--btt-drawer-w, 344px) 1fr;
}
/* While actively dragging the resize handle, killing the transition keeps
   the panel tracking the pointer 1:1 instead of chasing it. */
#bttPage.btt-drawer-resizing {
  transition: none;
}

#bttDrawer {
  position: sticky;
  /* --site-header-h is set by header-tabs.php's own lockHeaderHeight()
     script from the navbar's real on-load height — header.header-with-
     topbar's own offsetHeight is always 0 (its only child, .navbar, is
     position:fixed — style.css — and so out of flow), and that same script
     locks the navbar to this height with !important so it can never shrink
     on scroll the way main.js's header.sticky class normally would
     elsewhere (see header-tabs.php's own comment) — so, unlike a plain
     measure-on-load value, this number stays valid at every scroll
     position. 96px matches this theme's own .full-screen.top-space-margin
     constant as a sane fallback before that script runs / on viewports
     ≤991px, where it deliberately skips locking (see its own comment) and
     leaves --site-header-h unset. */
  top: var(--site-header-h, 96px);
  align-self: start;
  overflow: hidden;
  border-right: 1px solid var(--border-strong);
  min-width: 0;
  /* Constrained to the viewport (minus header), not #bttMain's full content
     height — that's what makes .btt-drawer-inner's overflow-y:auto below
     actually scroll independently instead of just matching the page's own
     scroll (which is what plain height:100% against the grid row gave you). */
  height: calc(100vh - var(--site-header-h, 96px));
  transition: top 0.25s ease, height 0.25s ease;
  /* Same container-query trick as .sidebar on the original taxon page
     (bos-taxon.css): names this panel "sidebar" so the shared
     "@container sidebar (max-width: 260px)" rule there — which reflows
     .tree-row.ancestor to rank/count-on-top, name-below instead of
     truncating — also applies here once the (user-resizable) drawer gets
     narrow. Without this, that rule never matches inside #bttDrawer at all,
     since it has no container named "sidebar" to query against. */
  container-type: inline-size;
  container-name: sidebar;
}
/* Fixed width (matches the open track width) regardless of #bttDrawer's own
   current width, and absolutely positioned — so it never reflows/wraps
   during the collapse, and (being out of normal flow) never contributes to
   #bttDrawer's height either. The open/closed animation is purely this
   translateX, clipped by #bttDrawer's overflow:hidden when off to the left. */
#bttDrawer .btt-drawer-inner {
  position: absolute;
  top: 0;
  left: 0;
  width: var(--btt-drawer-w, 344px);
  height: 100%;
  overflow-y: auto;
  box-sizing: border-box;
  padding: 16px;
  transform: translateX(0);
  transition: transform 0.2s ease;
  scrollbar-width: thin;
  scrollbar-color: var(--stone-dark, #8b96ac) transparent;
}
#bttDrawer .btt-drawer-inner::-webkit-scrollbar { width: 8px; }
#bttDrawer .btt-drawer-inner::-webkit-scrollbar-track { background: transparent; }
#bttDrawer .btt-drawer-inner::-webkit-scrollbar-thumb { background: var(--stone-dark, #8b96ac); border-radius: 4px; }
#bttDrawer .btt-drawer-inner::-webkit-scrollbar-thumb:hover { background: var(--navy, #5b8dee); }
#bttPage:not(.btt-drawer-open) #bttDrawer .btt-drawer-inner {
  transform: translateX(-100%);
}

/* ── Compact tiers (≤1024px): overlay instead of push ──────────────────────
   #bttDrawer leaves the grid entirely (position: fixed, no longer sized by
   #bttPage's grid-template-columns) and slides in as a full-height layer
   over the page instead of pushing #bttMain sideways — pushing a 344px
   sidebar into a phone-width content column isn't viable. The slide
   transform moves from .btt-drawer-inner (base rule above) to #bttDrawer
   itself: the absolute-inner/fixed-outer split above exists specifically to
   stop the *pushing* layout's mid-transition reflow from inflating
   #bttDrawer's own height (see the comment at the top of this file) — that
   concern doesn't apply once #bttDrawer is position:fixed and already out
   of flow, so the inner can go back to being a plain full-size box and the
   outer element handles its own slide directly. */
@media (max-width: 1024px) {
  /* Every var(--btt-drawer-w) consumer below (drawer width, toggle's
     open-state left offset) resolves to this unless bos-taxon-tabs.js has
     left a wider inline override in place — it clears any such override
     when the viewport enters this tier (bttInitDrawer()'s applyTier()). */
  #bttPage {
    --btt-drawer-w: min(344px, 88vw);
  }
  /* #bttDrawer no longer participates in the grid at all once it's
     position:fixed (below) — #bttMain is the only real grid item left, so
     the track list must collapse to one column here. Left at the base
     rule's 2 tracks ("0 1fr" / "var(--btt-drawer-w) 1fr"), grid
     auto-placement drops the sole remaining item (#bttMain) into track 1
     (width 0) instead of track 2, since #bttDrawer isn't there to claim
     track 1 anymore — that's what left #bttMain rendering at 0 width. Both
     selectors need the override: #bttPage.btt-drawer-open (id+class) is
     more specific than plain #bttPage and would otherwise keep winning
     against a same-media-query override of the id-only selector alone. */
  #bttPage,
  #bttPage.btt-drawer-open {
    grid-template-columns: 1fr;
  }
  #bttDrawer {
    position: fixed;
    top: var(--site-header-h, 96px);
    left: 0;
    bottom: 0;
    height: auto;
    width: var(--btt-drawer-w);
    z-index: 60;
    /* Base rule (above, unconditional) sets align-self:start for the
       pushing-layout grid item case — harmless there, but confirmed (via a
       minimal isolated repro) to make Chromium resolve height:auto as
       shrink-to-fit-content instead of stretching to fill top..bottom once
       #bttDrawer is position:fixed, even though a fixed element takes no
       part in grid layout at all. Resetting it here is what actually makes
       the drawer render at a non-zero height. */
    align-self: auto;
    /* Base rule (above, unconditional) never sets a background — in the
       pushing/grid layout #bttDrawer just showed #bttPage's own painted
       background through it, which no longer happens once it's painted in
       its own position:fixed layer. Without this it renders fully
       transparent, so opening it shows nothing but the backdrop tint. */
    background: var(--cream-dark, #16202f);
    box-shadow: 4px 0 24px rgba(0, 0, 0, 0.4);
    /* The base rule's border-right (the seam against #bttMain in pushing
       mode) is pointless here and actively wrong while closed:
       translateX(-100%) shifts the whole box — border included — left by
       its own width, landing that 1px line right at the viewport's left
       edge, on top of the stacked content now starting there. */
    border-right: none;
    transform: translateX(-100%);
    transition: transform 0.25s ease, top 0.25s ease;
  }
  #bttPage.btt-drawer-open #bttDrawer {
    transform: translateX(0);
  }
  #bttPage:not(.btt-drawer-open) #bttDrawer .btt-drawer-inner {
    transform: none;
  }
  /* Fixed-width overlay isn't user-resizable — dragging it would also fight
     the page's own touch-scroll gesture. */
  .btt-drawer-resize {
    display: none;
  }
}

/* Dismiss backdrop behind the overlay drawer — created once by
   bttInitDrawer() (bos-taxon-tabs.js) and appended to #bttPage; a click
   anywhere on it closes the drawer (same handler as the toggle button).
   Hidden outright at ≥1025px, where the drawer pushes content instead of
   overlaying it and a dimming backdrop would make no sense. */
.btt-drawer-backdrop {
  display: none;
  position: fixed;
  inset: 0;
  background: rgba(0, 0, 0, 0.45);
  z-index: 55;
}
@media (max-width: 1024px) {
  #bttPage.btt-drawer-open .btt-drawer-backdrop {
    display: block;
  }
}

/* ── Tree text contrast ───────────────────────────────────────────────────
   Several tree text colors (bos-taxon.css) use var(--stone), #C8C2B4 — too
   light against the drawer's near-white background, worst on
   .tree-group-sublabel (italic species-group headers). Bumped to the
   theme's secondary/muted text colors here, scoped to this page only. */
#bttDrawer .tree-group-sublabel {
  color: var(--text-secondary, #4a4a42);
}
#bttDrawer .tree-rank,
#bttDrawer .tree-common {
  color: var(--text-muted, #7a7a70);
}
/* .tree-sp-count (bos-taxon.css) uses var(--cream-dark) — fine on the
   light original page, but reads flat here since --cream-dark is this
   page's own elevated-panel colour, too close to the tree rows around it. */
#bttPage .tree-sp-count {
  background: var(--navy-dark);
}

/* Comparator "Add to comparator" buttons — three variants share the base
   .cmp-add-btn class (bos-comparator.css):
     1. Standalone single-species button (taxon-tabs.php, above Introduction)
     2. .cmp-add-all-btn ("Add all N species", Taxonomy section titles)
     3. .cmp-add-btn--icon/--list — per-card overlay, injected by
        bos-subtaxa.js onto Subtaxa/Species grid cards
   Variant 3 has its own design entirely (a translucent-white badge that
   floats over an arbitrary photo, hidden until the card is hovered —
   .stax-card:hover .cmp-add-btn--icon, bos-comparator.css) that must stay
   untouched by the dark-navy treatment below. Every rule here is scoped
   with :not(.cmp-add-btn--icon):not(.cmp-add-btn--list) — a previous pass
   missed this and the opacity rule below leaked onto the overlay variant,
   which broke BOTH its hover-only visibility (opacity:0.6 always, instead
   of the base rule's opacity:0 until card-hover) AND its contrast (that
   0.6 opacity also faded the hover state's white-on-navy-mid text/icon,
   blending them with whatever photo colour was underneath). */
#bttPage .cmp-add-btn:not(.cmp-add-btn--icon):not(.cmp-add-btn--list) {
  color: var(--stone-dark);
  background: var(--navy-dark);
  border-color: transparent;
  border-radius: 3px;
}

/* Single-species button only (not .cmp-add-all-btn) — icon + text (the
   same cmpBadge "two panels" SVG in every state, updateAddButton() in
   bos-comparator-shortlist.js), with the label swapping between "Add to
   comparator"/"In comparator" same as always. State reads through opacity:
   faded while available to add, full opacity once .cmp-add-btn--active
   (JS toggles this on add/remove) is set. Hover always goes full opacity
   too, regardless of state, so the button reads as clickable either way.
   Sits right above the Introduction text (taxon-tabs.php), so margin-
   bottom gives it some breathing room instead of crowding straight into
   that paragraph. */
#bttPage .cmp-add-btn:not(.cmp-add-btn--icon):not(.cmp-add-btn--list):not(.cmp-add-all-btn) {
  margin-bottom: 14px;
  opacity: 0.6;
}
#bttPage .cmp-add-btn--active:not(.cmp-add-btn--icon):not(.cmp-add-btn--list):not(.cmp-add-all-btn),
#bttPage .cmp-add-btn:not(.cmp-add-btn--icon):not(.cmp-add-btn--list):not(.cmp-add-all-btn):hover {
  opacity: 1;
  color: var(--navy);
}
/* Hovering while already "In comparator" needs its own feedback — without
   this, hover and resting-active are visually identical (both just the
   rule above), so the button looks unclickable/dead once it's active.
   filter:brightness, not a background swap — stays the same dark-navy hue,
   just a lighter tint of it, so this can't drift back into a literal
   light-blue background the way swapping to var(--navy)/var(--navy-mid)
   would (see the "no light blue backgrounds" block below). */
#bttPage .cmp-add-btn--active:not(.cmp-add-btn--icon):not(.cmp-add-btn--list):not(.cmp-add-all-btn):hover {
  filter: brightness(1.35);
}

/* .cmp-add-all-btn stays a plain (muted) text button — hover just
   brightens its text to the accent colour, same affordance pattern as the
   icon variant above. */
#bttPage .cmp-add-btn.cmp-add-all-btn:hover {
  color: var(--navy);
}

/* Positioned immediately left of the section's sort/view controls
   (.subtaxa-controls, bos-taxon.css — already margin-left:auto) rather
   than trailing after them (taxon-subtaxa-section.php renders it first,
   right after the title). Taking the margin-left:auto here instead pushes
   THIS button (and .subtaxa-controls immediately after it) to the right
   edge as one group; margin-right holds them apart by a deliberate gap
   rather than sitting flush together. */
#bttPage .cmp-add-all-btn {
  margin-left: auto;
  margin-right: 10px;
}

/* Per-card overlay button (.cmp-add-btn--icon, bos-comparator.css —
   injected by bos-subtaxa.js on Subtaxa/Species grid cards). Base rule's
   opacity (0 resting/not-in-comparator, revealed only on .stax-card:hover;
   1 always once .cmp-add-btn--active) is untouched — that hover-to-reveal
   behaviour is correct as-is. Only the COLOURS are overridden here:
   - resting (not in comparator): base rule is rgba(255,255,255,0.85), a
     near-white floating pill — fine on the light original page, but a
     bright white badge is exactly the kind of bright background this
     theme bans elsewhere (.tree-sp-count, .cmp-add-btn, .cmp-feedback,
     .cmp-badge — see the "no light blue backgrounds" block above; this is
     the same rule, just bright WHITE rather than bright blue, so it needed
     its own line rather than being an accidental match for that one).
     Same navy-dark hue as everywhere else on this theme, at the same
     0.85 opacity the base rule used — keeps the "floats over any photo"
     translucent quality, just recoloured.
   - active (in comparator): solid var(--navy-dark), no transparency —
     needs to read clearly regardless of what's under it, unlike the
     resting state which is meant to blend.
   - hover: filter:brightness rather than a lighter fill, same reasoning
     as .cmp-add-btn--active:hover above — brightens in place without
     introducing a new (bright) colour value.
   Scoped to body.btt-header-page, not #bttPage — this component also
   renders on any other page using this same dark header (home.php/
   search.php — see header-tabs.php's own comment on why body.btt-header
   -page exists as a second, broader scope), not just the taxon-tabs
   layout, so a #bttPage-only fix would leave it half-fixed site-wide. */
body.btt-header-page .cmp-add-btn--icon {
  background: rgba(16, 47, 70, 0.85);
  color: var(--stone-dark);
}
body.btt-header-page .cmp-add-btn--icon.cmp-add-btn--active {
  background: var(--navy-dark);
  color: #fff;
}
body.btt-header-page .cmp-add-btn--icon:hover {
  opacity: 1;
  filter: brightness(1.3);
  color: #fff;
}
/* No-photo species cards: .cmp-add-btn is disabled when the card has no
   thumbnail (updateAddButton(), bos-comparator-shortlist.js — data-cmp-
   thumb is empty) — can't add a species to the comparator with nothing to
   show for it there. The base .cmp-add-btn:disabled rule (bos-comparator.
   css) fixes opacity at 0.4 unconditionally, which (by specificity) beats
   .cmp-add-btn--icon's own hover-to-reveal opacity (0 resting, 1 on
   .stax-card:hover) — so a no-photo card's badge sat permanently, dimly
   visible while every other card's stayed fully hidden until hovered,
   breaking the grid's visual consistency. Hidden at rest like every other
   card here, still dims in (not full opacity, so it reads as unavailable)
   on card hover rather than fully activating. Scoped to .cmp-add-btn--icon
   specifically — the standalone/add-all buttons elsewhere on this page
   SHOULD stay persistently visible-but-dimmed when disabled (e.g.
   "shortlist full"), they're not hover-reveal overlays. */
body.btt-header-page .cmp-add-btn--icon:disabled {
  opacity: 0;
}
body.btt-header-page .stax-card:hover .cmp-add-btn--icon:disabled {
  opacity: 0.4;
}
/* Touch/no-hover: same "always dimly visible instead of hover-revealed"
   treatment as the enabled icon's own @media (hover: none) rule
   (bos-comparator.css) — :hover can never fire to reveal this otherwise. */
@media (hover: none) {
  body.btt-header-page .cmp-add-btn--icon:disabled {
    opacity: 0.4;
  }
}

/* ── No light blue backgrounds anywhere on this page — ever ────────────────
   var(--navy)/var(--navy-mid) are redefined bright (#5b8dee / #4a7de0,
   #bttPage token block above) specifically as TEXT/icon accent colours for
   this dark theme — never as a fill. Every base rule below (bos-comparator.
   css) uses one of them as a background, which is correct on the light
   original page (where --navy is a genuinely dark navy, #0F2340) but reads
   as a bright light-blue badge here. All swapped to var(--navy-dark)
   (#102f46, set alongside --navy/--navy-mid up top) instead — this is a
   blanket rule, not per-component: if a future component here ever fills a
   background with var(--navy) or var(--navy-mid), that's the bug to fix,
   add it to this list rather than reaching for --navy again. (.cmp-add-btn
   --icon's own active/resting/hover backgrounds are handled separately
   below, scoped to body.btt-header-page rather than #bttPage — see there
   for why.) */
#bttPage .cmp-add-btn--active,
#bttPage .cmp-add-btn--active:hover {
  background: var(--navy-dark);
}
/* .cmp-badge (the persistent floating "Comparator" shortlist button) and
   .cmp-feedback (the "added/removed" toast) are both created by
   bos-comparator-shortlist.js via document.body.appendChild(...) —
   createUI()/showFeedback() — NOT rendered anywhere inside #bttPage's own
   markup. An #bttPage-scoped selector is a descendant selector and simply
   never matches an element that's a sibling of #bttPage under <body>, so
   an earlier pass "fixing" these here was silently a no-op — confirmed by
   the bug still reproducing after that supposed fix. body.btt-header-page
   (set on <body> itself by header-tabs.php, this page's own header
   variant — see there) is the correct scope: any element appended to
   body ends up a real descendant of that. */
body.btt-header-page .cmp-badge {
  background: var(--navy-dark);
}
body.btt-header-page .cmp-feedback {
  background: var(--navy-dark);
}

/* .cmp-modal-backdrop/.cmp-modal (bos-taxon.css) — the "N species already
   shortlisted, add or reset?" and "clear the entire shortlist?" confirmation
   dialogs (showAddAllPrompt()/showClearConfirm(), bos-comparator-shortlist.
   js) — same document.body.appendChild(...) sibling trap as .cmp-badge/
   .cmp-feedback above, so body.btt-header-page again, not #bttPage. Base
   rules hardcode a white card for the light original page; recoloured to
   the same elevated-surface treatment used elsewhere on this theme
   (--cream-dark fill, --border-strong outline — see #bttDrawerToggle/
   .btt-gallery-empty above). --primary button's background is swapped off
   var(--navy) too, per the blanket no-light-blue-fill rule above — hover on
   all buttons uses filter:brightness rather than a lighter fill, same
   reasoning as everywhere else on this theme. */
body.btt-header-page .cmp-modal {
  background: var(--cream-dark);
  border: 1px solid var(--border-strong);
}
body.btt-header-page .cmp-modal-msg {
  color: var(--text-primary);
}
body.btt-header-page .cmp-modal-sub {
  color: var(--text-secondary);
}
body.btt-header-page .cmp-modal-btn {
  background: var(--cream-dark);
  color: var(--text-primary);
  border-color: var(--border-strong);
}
body.btt-header-page .cmp-modal-btn:hover {
  background: var(--cream-dark);
  filter: brightness(1.3);
}
body.btt-header-page .cmp-modal-btn--primary {
  background: var(--navy-dark);
  border-color: var(--navy-dark);
  color: #fff;
}
body.btt-header-page .cmp-modal-btn--primary:hover {
  background: var(--navy-dark);
  filter: brightness(1.3);
}
body.btt-header-page .cmp-modal-btn--secondary {
  color: var(--text-secondary);
}

/* ── Major-only rank column ───────────────────────────────────────────────
   .tree-rank's 80px width (bos-taxon.css) is sized for the longest MINOR
   rank label (Superfamily, Infraorder, Superorder, ...). In major-only mode
   those rows are hidden (bos-taxon-tabs.js toggleAncestors), so reserving
   that much width just steals room from .tree-sci for no reason — the
   longest label actually on screen is "Kingdom"/"Species" territory, close
   to the pre-minor-ranks 62px width this column used to have. Narrowed back
   down here, scoped to the .tree-major-only class that JS toggles onto
   #bttDrawer, so .tree-sci gets the width back while no minor rows need it. */
#bttDrawer.tree-major-only .tree-rank {
  width: 60px;
  flex-basis: 60px;
}

/* Drag handle along the drawer's right edge — same col-resize affordance
   as .sidebar-resize on the original taxon page (bos-taxon.css). */
.btt-drawer-resize {
  position: absolute;
  top: 0;
  right: 0;
  bottom: 0;
  width: 6px;
  cursor: col-resize;
  background: transparent;
  z-index: 5;
}
.btt-drawer-resize:hover,
.btt-drawer-resize.dragging {
  background: rgba(196, 147, 63, 0.35);
}

/* Toggled briefly by bttSizeDrawerToFit() (bos-taxon-tabs.js) to measure each
   row's natural, un-wrapped width — overrides the ellipsis/wrap/narrow-
   container rules (bos-taxon.css) that normally apply at #bttDrawer's actual
   current width, so scrollWidth reports the width the row would need
   regardless of that. Never left applied. */
#bttDrawer.btt-measuring .tree-row,
#bttDrawer.btt-measuring .tree-group-sublabel {
  display: inline-flex !important;
  flex-wrap: nowrap !important;
  width: auto !important;
  max-width: none !important;
  white-space: nowrap !important;
}
#bttDrawer.btt-measuring .tree-sci,
#bttDrawer.btt-measuring .tree-common {
  flex: none !important;
  overflow: visible !important;
  text-overflow: clip !important;
  white-space: nowrap !important;
  max-width: none !important;
  order: initial !important;
}
#bttDrawer.btt-measuring .tree-rank,
#bttDrawer.btt-measuring .tree-sp-count {
  order: initial !important;
  margin-left: 0 !important;
}

#bttDrawerToggle {
  position: fixed;
  left: 0;
  top: 120px;
  z-index: 20;
  writing-mode: vertical-rl;
  text-orientation: mixed;
  padding: 10px 1px;
  background: var(--cream-dark);
  color: var(--text-secondary);
  border: 1px solid var(--border-strong);
  border-left: none;
  border-radius: 0 6px 6px 0;
  cursor: pointer;
  font-size: 13px;
}
#bttPage.btt-drawer-open #bttDrawerToggle {
  left: var(--btt-drawer-w, 344px);
}

/* --btt-gutter lives here, not on .btt-top-row, so .btt-tabs-nav and
   .btt-tab-pane (its siblings under #bttMain, not its descendants) actually
   inherit it too instead of only working by coincidence of a duplicated
   fallback value. */
#bttMain {
  --btt-gutter: 36px;
  min-width: 0;
}
@media (max-width: 1024px) {
  #bttMain {
    --btt-gutter: 24px;
  }
}
@media (max-width: 767px) {
  #bttMain {
    --btt-gutter: 16px;
  }
}

/* ── Main panel gutter ──────────────────────────────────────────────────── */
/* 1fr/2fr (not literal 33%/67%) so the split still holds after the 40px
   gap is subtracted — fr distributes only the space left over once fixed
   tracks and gaps are accounted for, which literal percentages don't. */
.btt-top-row {
  display: grid;
  grid-template-columns: 1fr 2fr;
  gap: 40px;
  padding: 32px var(--btt-gutter) 16px;
}
@media (max-width: 1024px) {
  .btt-top-row { grid-template-columns: 1fr; }
}

/* ── Gallery: 33% of #bttMain via the 1fr track above, prev/next, fade
   between photos ─────────────────────────────────────────────────────── */
.btt-gallery-medium {
  width: 100%;
  min-width: 300px;
}
.btt-gallery-rep-wrap {
  position: relative;
}
/* Placeholder when the taxon has no photos at all. #bttGallery is a grid
   item in .btt-top-row, which stretches it to match #bttInfo's height by
   default — height:100% (not a fixed px height) is what makes the light
   background actually reach the bottom of that stretched box instead of
   leaving bare page background showing below a short fixed-height block. */
.btt-gallery-empty {
  width: 100%;
  height: 100%;
  min-height: 320px;
  box-sizing: border-box;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 10px;
  background: var(--cream-dark);
  color: var(--stone-dark);
  border-radius: 4px;
}
.btt-gallery-empty span {
  font-size: 13px;
  font-weight: 500;
}
.btt-gallery-rep {
  display: block;
  width: 100%;
  max-width: 100%;
  height: auto;
  max-height: 640px;
  object-fit: contain;
  margin: 0 auto;
  cursor: zoom-in;
  background: #000;
}
/* Black crossfade between photos: an opaque overlay (not the image's own
   opacity) fades in over the current photo, the src swaps underneath while
   fully covered, then the overlay fades back out onto the new photo — a
   fade-to-black-and-back rather than a straight cross-dissolve. Covers the
   whole wrap (not just the <img>) so letterboxed edges go black too. */
.btt-gallery-fade-overlay {
  position: absolute;
  inset: 0;
  background: #000;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.15s ease;
  z-index: 1;
}
.btt-gallery-fade-overlay.btt-gallery-fade-active {
  opacity: 1;
}
/* Full-height click strips along the image's left/right edges, rather than
   a small circular button floating over the middle. Transparent until
   hovered, so they don't tint the photo at rest. */
.btt-gallery-nav {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 44px;
  display: flex;
  align-items: center;
  justify-content: center;
  border: none;
  background: rgba(0, 0, 0, 0);
  color: #fff;
  font-size: 22px;
  line-height: 1;
  cursor: pointer;
  z-index: 2;
  transition: background 0.15s ease;
}
.btt-gallery-nav:hover {
  background: rgba(0, 0, 0, 0.5);
}
.btt-gallery-nav-prev { left: 0; }
.btt-gallery-nav-next { right: 0; }

/* ── Highlighted-image caption: sex / lifestage / copyright ─────────────── */
/* Sits flush against the image with no gap (.btt-gallery-rep-wrap has no
   bottom margin) so the two read as one joined photo+caption block. */
.btt-gallery-caption {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 12px;
  margin-top: 0;
  padding: 5px 10px;
  background: #000;
  color: #fff;
  font-size: 11px;
  line-height: 1.5;
}
.btt-gallery-caption:empty {
  display: none;
}
.btt-gallery-caption-left,
.btt-gallery-caption-right {
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.btt-gallery-caption-right {
  flex-shrink: 0;
  color: rgba(255, 255, 255, 0.75);
}
.btt-gallery-caption a {
  color: #fff;
}

.btt-gallery-thumbs {
  display: flex;
  gap: 8px;
  overflow-x: auto;
  margin-top: 8px;
  /* Themed scrollbar — the browser default (light track/thumb) clashes with
     the dark page. Firefox/standard + WebKit variants both needed. */
  scrollbar-width: thin;
  scrollbar-color: var(--stone-dark, #8b96ac) var(--cream-dark, #16202f);
}
.btt-gallery-thumbs::-webkit-scrollbar { height: 6px; }
.btt-gallery-thumbs::-webkit-scrollbar-track { background: var(--cream-dark, #16202f); border-radius: 3px; }
.btt-gallery-thumbs::-webkit-scrollbar-thumb { background: var(--stone-dark, #8b96ac); border-radius: 3px; }
.btt-gallery-thumbs::-webkit-scrollbar-thumb:hover { background: var(--navy, #5b8dee); }
.btt-gallery-thumbs img {
  width: 64px;
  height: 64px;
  object-fit: cover;
  cursor: pointer;
  border-radius: 4px;
  border: 2px solid transparent;
}
.btt-gallery-thumbs img.active {
  border-color: var(--gold, #c4933f);
}

/* ── Info column: flush-left with the section titles below it ───────────── */
/* Top padding cut down too — the rank badge used to sit on its own line
   above the name (see .taxon-rank-badge-inline below for where it moved) —
   and the bottom border removed, since it read as a stray rule between the
   title and the Introduction section right below it. Border-right also
   neutralized: bos-taxon.css's plain `.taxon-identity` (shared with the
   original page's ≤900px sidebar-collapse layout) adds one at ≤900px,
   which has no purpose here — this page's identity block was never part of
   a horizontal sidebar strip. */
#bttInfo .taxon-identity {
  padding: 4px 0 16px;
  border-bottom: none;
  border-right: none;
}
/* Singapore residency/conservation status — .dist-badge* is the same badge
   language the Distribution field (bos-taxon.css) already uses per-country;
   reused here standalone so this reads as one visual system, not a second
   badge style invented just for the title. */
.taxon-status-badges {
  display: flex;
  align-items: center;
  gap: 6px;
  margin-bottom: 6px;
}
/* Habitat type (taxon.habitatTypeID → habitat_types) and residency status
   (residency_statuses), each coloured by value so the badge itself carries
   meaning at a glance instead of three same-coloured pills. Structural
   properties shared across every value live on the base class; each
   -<value> modifier (added alongside it in the view, see $habitatColorMap /
   $residencyColorMap in taxon-tabs.php) sets only background/color — always
   one of those two for text, never a third grey-on-grey compromise. */
.dist-badge--habitat,
.dist-badge--residency {
  text-transform: uppercase;
  font-weight: 600;
  letter-spacing: 0.06em;
}
.dist-badge--habitat-terrestrial { background: #6d4c33; color: #fff; }
.dist-badge--habitat-freshwater  { background: #1f7a6c; color: #fff; }
.dist-badge--habitat-marine      { background: #1c3f6e; color: #fff; }

.dist-badge--residency-native     { background: #3f7d44; color: #fff; } /* Native */
.dist-badge--residency-gone       { background: #2d2d2d; color: #fff; } /* Locally Extinct — same tone as conservation's "gone" bucket */
.dist-badge--residency-invasive   { background: #c0392b; color: #fff; } /* Invasive */
.dist-badge--residency-introduced { background: #c99a3c; color: #000; } /* Introduced, Introduced via pet trade, Imported lab culture */
.dist-badge--residency-visitor    { background: #3b6fa0; color: #fff; } /* Visitor */
.dist-badge--residency-unsure     { background: #757575; color: #fff; } /* Unsure */
.taxon-author-inline {
  font-size: var(--fs-sm, 14px);
  color: var(--text-muted, #77706a);
  font-weight: 300;
  margin-left: 8px;
}
#bttInfo .taxon-common-name {
  font-size: var(--fs-base, 16px);
  /* Must read at least as prominent as .prose (introduction body text,
     var(--text-secondary)) — was var(--text-muted), a step dimmer than
     .prose, which read as backwards (a title's own common name looking
     less important than the body copy below it). */
  color: var(--text-secondary, #77706a);
}
/* Rank badge, moved inline after the author/year instead of its own line
   above the name — same look as .taxon-rank-badge (bos-taxon.css) but a
   size down and defined fresh here rather than overridden, since the
   inline-after-text placement is different enough from that shared
   block-level badge to not be worth fighting its specificity/margins. */
.taxon-rank-badge-inline {
  display: inline-block;
  vertical-align: middle;
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--gold-light, #e8be7a);
  background: rgba(196, 147, 63, 0.16);
  border: 1px solid rgba(196, 147, 63, 0.4);
  border-radius: 2px;
  padding: 1px 6px;
  margin-left: 8px;
}

/* ── Details field list ───────────────────────────────────────────────────
   bos-taxon.css's .field-grid packs fields into an auto-fit multi-column
   grid — fine when every value is short, but CSS Grid stretches every cell
   in a row to match the row's tallest cell, so one long value (a long
   "Other common names" list, a long Distribution) drags every other field
   sharing its row into a lot of wasted whitespace, and which fields land in
   which row shifts unpredictably with container width. Replaced here with
   one field per full-width row (label left, value right) — a long value
   only ever makes its own row taller, never a sibling's. */
#bttInfo .field-grid {
  display: block;
  background: none;
  border: none;
  border-radius: 0;
  overflow: visible;
}
/* Details is now merged into the same .section as Introduction (no more
   52px .section gap between them) — a little breathing room only when it's
   actually preceded by intro/subheading content, none when it's the first
   (only) thing in the section. */
#bttInfo .field-grid:not(:first-child) {
  margin-top: 20px;
}
#bttInfo .field-item,
#bttInfo .field-item--dist {
  display: grid;
  grid-template-columns: 140px minmax(0, 1fr);
  gap: 4px 20px;
  padding: 12px 0;
  background: none;
  border-bottom: 1px solid var(--border);
}
#bttInfo .field-item:last-child,
#bttInfo .field-item--dist:last-child {
  border-bottom: none;
}
#bttInfo .field-label {
  margin-bottom: 0;
  padding-top: 2px;
  font-size: 13px;
}
#bttInfo .field-value {
  min-width: 0;
  overflow-wrap: break-word;
}
/* Rich-text values (Etymology/Biology/Habitat) can carry their own <p>
   margins from the stored HTML — drop the last one so the value doesn't
   leave extra space before the row's own bottom padding/border. */
#bttInfo .field-value p:last-child {
  margin-bottom: 0;
}
@media (max-width: 479px) {
  #bttInfo .field-item,
  #bttInfo .field-item--dist {
    grid-template-columns: 1fr;
  }
}

/* ── More breathing room between sections + off the window edge ─────────── */
#bttInfo .section {
  margin-bottom: 52px;
}
#bttInfo .section:last-child {
  margin-bottom: 0;
}

/* ── Breadcrumb sticky hand-off ───────────────────────────────────────────
   .taxon-breadcrumb (bos-taxon.css) is `position: sticky; top: 0` inside
   #bttInfo — a column no taller than the gallery/intro row, so once that
   row scrolls past, the breadcrumb's containing block ends and it scrolls
   away with it, well before the tab panes below are done being read.
   bttInitBreadcrumbPin() (bos-taxon-tabs.js) swaps it to this fixed mode,
   spanning #bttMain's current width (left/width set inline in JS — that
   box resizes with the tree drawer), right as it would otherwise disappear,
   so it keeps tracking the viewport top through the tab panes too and lines
   up with their width instead of the narrower info column's.

   Both states' bos-taxon.css `top: 0` (and `.specimen-filters`' `top: 62px`
   below) predate the fixed header (header .navbar { position:fixed } —
   style.css) — like #bttDrawer above, that header reserves no document-flow
   space, so `top: 0` sticks these flush to the literal viewport top, right
   where the fixed header renders, hiding them underneath it instead of
   below it. Same --site-header-h #bttDrawer uses above (see its own
   comment) — the header never actually leaves the viewport at any scroll
   position (header-tabs.php's lockHeaderHeight() prevents main.js's
   header.sticky class from shrinking it), so unlike #bttDrawer there's no
   "flush to the top once scrolled past" state to design for here either —
   just keep these below it, always. */
#bttPage .taxon-breadcrumb {
  top: var(--site-header-h, 96px);
}
.taxon-breadcrumb.btt-breadcrumb-fixed {
  position: fixed;
  top: var(--site-header-h, 96px);
  z-index: 40;
  margin-bottom: 0;
  padding-left: var(--btt-gutter, 36px);
  padding-right: var(--btt-gutter, 36px);
  border-bottom-color: var(--border-strong);
}
/* .specimen-filters sticks directly below the breadcrumb — its own
   ~62px rendered height (bos-taxon.css's own literal, unchanged) stacked
   on top of the header offset instead of replacing it. */
#bttPage .specimen-filters {
  top: calc(var(--site-header-h, 96px) + 62px);
}
.taxon-breadcrumb-spacer {
  display: none;
}

/* Current taxon — appended to the breadcrumb (taxon-tabs.php), but only
   shown once pinned: in the normal in-flow state the full name is already
   visible right below in .taxon-identity, so repeating it here would be
   redundant. Not a link, so it gets its own leading chevron (::before)
   rather than relying on a.taxon-breadcrumb-item's trailing one
   (bos-taxon.css) — that's scoped to <a> siblings and deliberately never
   fires after the last ancestor regardless of this element's visibility. */
.taxon-breadcrumb-current {
  display: none;
}
.taxon-breadcrumb.btt-breadcrumb-fixed .taxon-breadcrumb-current {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  color: var(--navy);
  font-weight: 600;
}
.taxon-breadcrumb.btt-breadcrumb-fixed .taxon-breadcrumb-current::before {
  content: '';
  display: inline-block;
  width: 5px;
  height: 5px;
  border-top: 1.5px solid var(--stone);
  border-right: 1.5px solid var(--stone);
  transform: rotate(45deg);
  flex-shrink: 0;
}

/* bos-taxon.css's base rule keeps the full ancestor path on one line,
   scrolling horizontally rather than wrapping — deliberate there (the
   original page's narrower sidebar column has room for a wider breadcrumb
   relative to the viewport), but on this page's full-width layout a long
   path (Kingdom > Phylum > ... > Family) needing a horizontal scroll
   gesture just to read is worse than letting it wrap to a second line —
   at any width, not just the narrower tiers, since #bttMain's own width
   varies with the (user-resizable) tree drawer too. */
#bttPage .taxon-breadcrumb {
  flex-wrap: wrap;
  overflow-x: visible;
  white-space: normal;
}

.btt-tabs-nav {
  display: flex;
  gap: 4px;
  border-bottom: 1px solid var(--border-strong);
  padding: 0 var(--btt-gutter, 36px);
  overflow-x: auto;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: thin;
  scrollbar-color: var(--stone-dark, #8b96ac) transparent;
  /* Respected by scrollIntoView() (bttInitTabs(), bos-taxon-tabs.js) when
     landing on a #<tab> deep link — same combined header+pinned-breadcrumb
     offset #bttPage .specimen-filters/.idkey-panel-right already use, so
     the nav doesn't end up scrolled to underneath either fixed element. */
  scroll-margin-top: calc(var(--site-header-h, 96px) + 62px);
}
.btt-tabs-nav::-webkit-scrollbar { height: 6px; }
.btt-tabs-nav::-webkit-scrollbar-track { background: transparent; }
.btt-tabs-nav::-webkit-scrollbar-thumb {
  background: var(--stone-dark, #8b96ac);
  border-radius: 4px;
}
.btt-tabs-nav a {
  padding: 10px 16px;
  cursor: pointer;
  text-decoration: none;
  color: var(--text-secondary);
  border-bottom: 2px solid transparent;
  flex-shrink: 0;
  white-space: nowrap;
}
.btt-tabs-nav a:hover {
  color: var(--text-primary);
}
.btt-tabs-nav a.active {
  border-bottom-color: var(--navy);
  color: var(--text-primary);
  font-weight: 600;
}
.btt-tab-pane {
  display: none;
  padding: 28px var(--btt-gutter, 36px) 40px;
}
.btt-tab-pane.active {
  display: block;
}

/* Must not read smaller than the .prose body text it introduces (--fs-base,
   16px) — weight/size step up is what should read as "heading" here, not
   a shrink. */
.btt-subheading {
  margin: 16px 0 8px;
  font-size: var(--fs-md, 18px);
  font-weight: 600;
}

/* ── Map wheel-zoom hint ─────────────────────────────────────────────────
   Briefly shown (initMap(), bos-taxon-tabs.js) when the user scrolls over
   the map without Ctrl/Cmd held, since a plain scroll deliberately keeps
   scrolling the page instead of zooming the map. */
.btt-map-wheel-hint {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: center;
  justify-content: center;
  background: rgba(0, 0, 0, 0.5);
  color: #fff;
  font-size: 14px;
  font-weight: 500;
  text-align: center;
  padding: 16px;
  box-sizing: border-box;
  opacity: 0;
  pointer-events: none;
  transition: opacity 0.2s ease;
  z-index: 1000;
}
.btt-map-wheel-hint.visible {
  opacity: 1;
}

/* ══════════════════════════════════════════════════════════════════════════
   Dark colour scheme — matches the comparator (bos-comparator.css: page
   #0e1520, bars/panels var(--navy), accent #5b8dee). Scoped entirely to
   #bttPage so the original taxon page (bos-taxon.css, loaded there too but
   never edited here) is completely unaffected — same rule this whole file
   has followed since Plan 1.

   Approach: bos-taxon.css's shared components (specimens, traits,
   relationships, depositories, literature, subtaxa cards/tree — the bulk of
   this page's actual content) already draw almost all of their colour from
   the bos-common.css design tokens (--navy, --text-primary/secondary/muted,
   --stone/-dark, --cream/-dark, --border/-strong, plus --link/--text/--body,
   which are used but never defined at :root, always falling back to a
   hardcoded light value). Redefining those tokens here — light-mode values
   in, dark-mode values out — reskins virtually all of that reused content
   automatically, with no per-component duplication. --gold/--gold-light are
   deliberately left alone: an amber accent on dark navy is already
   established on this very page (drawer resize handle, gallery thumb
   border) and reads fine unchanged.

   --navy carries a dual role in bos-taxon.css: emphasis TEXT colour in one
   set of rules (headings, hover states) and solid BADGE BACKGROUND (with
   hardcoded white text) in another (active filter pills/chips, specimen
   type badges). Both roles still work with a single bright accent — that's
   exactly how the comparator itself uses #5b8dee (.cmp-ctrl-btn--active
   background AND its own accent-color) — so one redefinition covers both
   without splitting into separate variables. --stone/--stone-dark have the
   same dual role (secondary badges/text) and get the same treatment, one
   step down in prominence from --navy's bright blue.

   What variable redefinition does NOT reach: a handful of components set
   background/highlight colours as bare hex or rgba(15,35,64,…) (a navy-
   based tint, invisible once the page itself goes dark) instead of a
   token — those get explicit #bttPage-scoped overrides below, right after
   the token block. */
#bttPage {
  --navy: #5b8dee;
  --navy-mid: #4a7de0;
  --navy-dark: #102f46;
  --text-primary: #eef0f4;
  --text-secondary: rgba(238, 240, 244, 0.72);
  --text-muted: rgba(238, 240, 244, 0.5);
  --text: #eef0f4;   /* used but never defined at :root (bos-taxon.css .taxon-lit-item) */
  --body: #eef0f4;   /* same — one rule falls back to var(--body, #2d2926) */
  --stone-dark: #8b96ac;
  --stone: #57647a;
  --cream: #0e1520;       /* was the page's light background; now the page itself */
  --cream-dark: #16202f;  /* was a subtle-darker card tint; now a step lighter — elevated surfaces get lighter, not darker, on a dark page */
  --border: rgba(255, 255, 255, 0.1);
  --border-strong: rgba(255, 255, 255, 0.2);
  --link: #4a7de0;

  background: var(--cream);
  color: var(--text-secondary);
}

/* ── Components whose colour isn't token-driven ─────────────────────────── */

/* Tint overlays keyed to the old navy-on-cream page (rgba built from navy
   itself, 2–6% opaque) — need the same trick inverted: white-based tints,
   or they're invisible against the new dark background. */
#bttPage .tree-group-ancestors { background: rgba(255, 255, 255, 0.025); }
#bttPage .tree-group-siblings  { background: rgba(255, 255, 255, 0.045); }
#bttPage .tree-children        { background: rgba(255, 255, 255, 0.035); }
#bttPage .tree-row:hover,
#bttPage .tree-row.tree-child:hover { background: rgba(255, 255, 255, 0.07); }
/* "Currently viewing" row — was a pale cream flat fill; an accent-tinted
   one reads better against dark than a literal inverted (near-black) fill
   would, and keeps the same "this one" affordance. */
#bttPage .tree-row.current { background: rgba(91, 141, 238, 0.18); }

/* Cards/panels hardcoded to solid white — same elevated-surface colour as
   #bttDrawerToggle above, consistent with --cream-dark's new role. */
#bttPage .specimen-card,
#bttPage .specimen-filters,
#bttPage .filter-ms-panel,
#bttPage .subtaxa-filter-tab,
#bttPage .stax-card,
#bttPage .stax-ref-popover {
  background: var(--cream-dark);
}
/* Resting (non-.active) state only — .subtaxa-filter-tab.active already
   switches to var(--navy) via bos-taxon.css and needs no override. */
#bttPage .subtaxa-filter-tab {
  border-color: var(--border-strong);
}
/* .type-paratype (bos-taxon.css) uses var(--navy-mid) as a small badge fill
   — fine at its light-theme default (#1a3456, dark navy), but --navy-mid is
   redefined bright (#4a7de0) up top for this page, same "stats-bar" issue.
   Scoped here (not fixed in bos-taxon.css itself) since that file is shared
   with the light-themed taxon.php page, where the original colour is
   correct and shouldn't change. */
#bttPage .type-paratype {
  background: var(--cream-dark);
}
/* .tag-filter-chip (bos-taxon.css, shared with light-themed taxon.php) is
   hardcoded solid white for its resting state — unlike the cards above, it
   never referenced a --cream token at all, so the redefinition at the top
   of this file doesn't reach it. Resting state matches the page background
   (this page's own --cream); .tag-filter-chip:hover:not(.active) already
   steps up to --cream-dark unchanged, so hover still reads as "elevated"
   the same way it does on the light page (fff -> cream-dark; here it's just
   the two darker steps of the same pair). */
#bttPage .tag-filter-chip {
  background: var(--cream);
}
/* Same class of bug in the trait-range slider (taxon-subtaxa-section.php):
   the draggable thumb and the number input's focus state are hardcoded
   white, missed by the --cream token redefinition for the same reason as
   .tag-filter-chip above. .trait-range-input's resting state already uses
   var(--cream) and needs no fix. */
#bttPage .trait-range-thumb {
  background: var(--cream-dark);
}
#bttPage .trait-range-input:focus {
  background: var(--cream-dark);
}

/* Caption under #specimen-map explaining what points vs. highlighted areas
   mean — pulled up into the map's own margin-bottom (bos-taxon.css) so it
   reads as directly attached to the map rather than floating detached. */
#bttPage .map-note {
  margin-top: -14px;
  margin-bottom: 20px;
  font-size: var(--fs-xs);
  color: var(--stone-dark);
}

/* Leaflet's own popup chrome (leaflet.css, unpkg CDN) — the specimen-map pin
   popup — is hardcoded white/dark-grey-text, same "bright default" issue as
   the cards above, just from a third-party stylesheet instead of ours.
   .map-popup-* (bos-taxon.css) already styles the popup's own CONTENT with
   theme-aware tokens and needs no change; only Leaflet's wrapper/tip/close-
   button chrome around that content is fixed here. */
#bttPage .leaflet-popup-content-wrapper {
  background: var(--cream-dark);
  color: var(--text-primary);
}
#bttPage .leaflet-popup-tip {
  background: var(--cream-dark);
}
#bttPage .leaflet-popup-close-button {
  color: var(--text-secondary);
}
#bttPage .leaflet-popup-close-button:hover {
  color: var(--text-primary);
}

/* Clicking a map popup entry scrolls to and flashes the matching specimen
   card (.specimen-flash, bos-taxon.css @keyframes specimenFlash) — hardcoded
   #fff/#FDF0D5, a warm pale-cream flash that's a soft highlight on the
   light original page but a jarring bright patch against this page's dark
   background. @keyframes has no selector scoping, so simply redeclaring
   the same name here (loaded after bos-taxon.css, and only on this page)
   overrides it outright — no #bttPage prefix possible or needed on the
   rule itself. Flashes to this page's own already-established amber-glow
   accent instead (same rgba(196,147,63,...) as the drawer resize handle)
   rather than inventing a new colour. */
@keyframes specimenFlash {
  0%, 100% { background: var(--cream-dark); }
  10%, 70% { background: rgba(196, 147, 63, 0.35); }
}

/* ── Relationship reference badge: reposition beside the taxon name ──────
   Base badge (bos-taxon.css) floats top-right of the whole card, over the
   photo — a solid pill + shadow makes sense there since the photo's colour
   is unknown. On this page it moves down to sit right-aligned in
   .stax-name-row instead, next to the taxon name — still a DOM sibling of
   .stax-card, never nested inside its <a> (see bos-taxon.css's own comment
   on why: the reference list's links need to be real, independently-
   clickable anchors). Scoped to #bttPage only — the original taxon page
   keeps the floating-over-photo placement unchanged.

   .stax-card-wrap gets its own (unnamed) query container so `cqw` below can
   track its width — the photo's height is aspect-ratio:4/3 (75% of card
   width), so `75cqw` lands exactly on the .stax-photo/.stax-info boundary
   regardless of how wide the card actually renders, and the badge sits
   right at the top of .stax-info from there.

   .stax-ref-badge is a real <button> here (taxon-tabs.php), not the div the
   original page uses — reset its default chrome away since none of the
   pill styling below comes from the browser. */
#bttPage .stax-card-wrap {
  container-type: inline-size;
}
#bttPage .stax-ref-badge {
  top: calc(75cqw + 7px);
  right: 10px;
  box-shadow: none;
  background: none;
  border: none;
  padding: 0;
  font: inherit;
  cursor: pointer;
}
/* Same treatment as .tree-sp-count above — --navy-dark instead of
   --cream-dark, which read too flat this close to the card's own
   (identical) elevated-panel background. */
#bttPage .stax-ref-count {
  background: var(--navy-dark);
  color: var(--stone-dark);
  gap: 3px;
  padding: 0 5px;
}
#bttPage .stax-ref-icon {
  width: 10px;
  height: 10px;
  flex-shrink: 0;
}
/* Reserves room so the absolutely-positioned badge above doesn't sit on
   top of .stax-count when a relationship has both a species count and
   supporting references. */
#bttPage .stax-name-row.has-ref-badge {
  padding-right: 26px;
}

/* ── Relationship reference panel: overlay below the card, no reflow ─────
   The original hover popover (bos-taxon.css, still used on the frozen
   taxon page) is position:absolute with a few px gap before it — the
   cursor crosses a dead zone moving from trigger to popover, and :hover
   drops before it gets there, closing it before its links are reachable.

   .stax-ref-panel (taxon-tabs.php) fixes the dead zone by sitting flush
   (top:100%, zero gap) against .stax-card-wrap's own bottom edge — the
   nearest positioned ancestor, so the panel is exactly the card's width —
   rather than floating detached from a small trigger badge. It stays
   position:absolute/out of flow throughout, so it never affects
   .subtaxa-grid layout, and a z-index above the (non-positioned, so
   z-index:auto) card content lets it visually sit over any cards that
   follow instead of being hidden under them.

   Desktop: hovering anywhere on the card reveals it — gated behind
   (hover:hover) and (pointer:fine) so a touchscreen's first-tap-triggers-
   hover quirk can't fire this and force a second tap just to follow the
   card's own link. :hover on the panel itself keeps it open while reading.
   Touch/keyboard: .stax-ref-badge is a real <button>; clicking/tapping it
   toggles a .expanded class (bos-taxon-tabs.js) that shows the panel
   independently of hover — the only way in on a device with no hover, and
   also lets a mouse user pin it open instead of holding position. The same
   click handler scrolls the page up afterwards if the expanded panel would
   otherwise run past the bottom of the viewport.

   max-height (not display) is what's animated — 0 → a generous cap, with
   overflow-y:auto as a fallback for the rare reference list taller than
   that cap, so nothing becomes unreachable. Box-sizing:border-box (global
   reset, bos-common.css) is what makes max-height:0 fully collapse the
   panel's own padding too, not just its content, so there's no residual
   sliver when closed. */
#bttPage .stax-ref-panel {
  position: absolute;
  top: 100%;
  left: 0;
  width: 100%;
  z-index: 20;
  max-height: 0;
  overflow-y: auto;
  opacity: 0;
  transition: max-height 0.25s ease, opacity 0.2s ease;
  background: var(--cream-dark);
  border-radius: 4px;
  box-shadow: 0 6px 18px rgba(0,0,0,0.35);
  padding: 10px 14px;
}
#bttPage .stax-ref-panel.expanded {
  max-height: 320px;
  opacity: 1;
}
@media (hover: hover) and (pointer: fine) {
  #bttPage .stax-card-wrap:hover .stax-ref-panel,
  #bttPage .stax-ref-panel:hover {
    max-height: 320px;
    opacity: 1;
  }
}

/* taxon_relationship_references.citationDetail (e.g. "Transferred from X"),
   shown directly below its reference link/plain item inside
   .stax-ref-panel — .stax-ref-item keeps the divider-per-reference border
   (bos-taxon.css), so this stays grouped under the item it annotates rather
   than getting its own border. Sized like .stax-ref-item but italic + muted
   so it reads as an annotation rather than the citation itself.
   var(--text-secondary) is redefined above (dark-theme block) to a light,
   high-contrast value against .stax-ref-panel's own dark
   var(--cream-dark) — same pairing .stax-ref-item--plain already relies on
   in this panel. */
#bttPage .stax-ref-detail {
  display: block;
  font-size: var(--fs-xs);
  line-height: 1.4;
  color: var(--text-secondary);
  font-style: italic;
  padding-bottom: 3px;
}

/* ── Title hierarchy ──────────────────────────────────────────────────────
   The species name needs to read clearly as this page's title — bigger and
   bolder than the shared light-mode size (var(--fs-lg), weight 400, from
   bos-taxon.css). Colour stays var(--navy) (the page's one bright accent,
   #5b8dee here) — already good contrast against #0e1520, no need to
   introduce a second title colour. The common name (already muted via
   #bttInfo .taxon-common-name above) drops to a lighter weight too,
   widening the gap so it reads unambiguously as secondary rather than a
   second title of similar weight. */
#bttPage .taxon-sci-name {
  font-size: 28px;
  font-weight: 600;
}
#bttInfo .taxon-common-name {
  font-weight: 300;
}

/* ── Footer logo (footer-tabs.php) ────────────────────────────────────────
   Theme default is max-height:30px, width auto — too small for
   nus-bos2-logo.png's proportions. A fixed width+height box squished the
   logo whenever the footer column (col-6/col-lg-3) rendered narrower than
   320px, since the theme's global `img { max-width:100%; height:auto }`
   reset still clamps width while our explicit height stayed fixed.
   aspect-ratio keeps the image's true 1000:139 proportions at any width. */
#bttFooter .footer-logo img {
  width: 100%;
  max-width: 320px;
  height: auto;
  max-height: none;
  aspect-ratio: 1000 / 139;
}

/* ── External database identifiers — one line per record ─────────────────
   .resource-item (bos-taxon.css) gives the row its border/padding; this
   just switches its content to a flex row: provider link then ID badge,
   sitting right next to each other (not spread across the row). */
.ident-item {
  display: flex;
  align-items: center;
  gap: 8px;
}
.ident-provider {
  font-size: var(--fs-base);
  color: var(--link, #2a5caa);
  text-decoration: none;
  font-weight: 400;
}
span.ident-provider {
  color: var(--text-secondary);
}
.ident-badge {
  flex-shrink: 0;
  font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
  font-size: var(--fs-sm);
  line-height: 1.4;
  color: var(--text-secondary);
  background: var(--cream-dark);
  border: 1px solid var(--border-strong);
  border-radius: 20px;
  padding: 2px 10px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s, color 0.12s;
}
.ident-badge:hover {
  border-color: var(--navy);
  color: var(--navy);
}
.ident-badge.ident-badge-copied {
  color: #fff;
  background: var(--navy);
  border-color: var(--navy);
}

/* ── Taxon footer (after the tabs) — feedback/download/back-to-top actions
   plus a "Cite this page" box. Lives inside #bttMain, so it only spans the
   main content column, not the sidebar. Uses the same --navy/--text-*,
   --border/--cream-dark tokens as the rest of this file (redefined for dark
   mode at #bttPage scope, bos-taxon.css) so it inherits the theme for free. */
.btt-footer {
  margin-top: 40px;
  padding: 24px var(--btt-gutter, 36px) 40px;
  border-top: 1px solid var(--border);
}
.btt-footer-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 10px;
  margin-bottom: 24px;
}
.btt-footer-btn {
  display: inline-flex;
  align-items: center;
  gap: 6px;
  font-family: inherit;
  font-size: var(--fs-xs);
  font-weight: 500;
  color: var(--text-secondary);
  background: none;
  border: 1px solid var(--border);
  border-radius: 4px;
  padding: 7px 14px;
  cursor: pointer;
  transition: color 0.12s, border-color 0.12s;
}
.btt-footer-btn:hover {
  color: var(--navy);
  border-color: var(--navy);
}

/* ── "Cite this page" modal contents — lives in #citeModal, a sibling of
   #bttPage in the DOM (all the .fb-modal-* dialogs are, so they float above
   everything regardless of scroll position), so it does NOT inherit the
   dark-mode token redefinitions scoped to #bttPage — same light-on-white
   fallback values as .fb-modal-desc uses right next to it. */
.btt-cite-body {
  padding: 4px 0 20px;
}
/* Two stacked sections — "Cite" (style tabs + preview + Copy) and "Export"
   (file-download buttons) — same visual grouping as the Download modal's
   .dl-section, but kept in their own .btt-cite-* namespace rather than
   reusing .dl-section directly: .btt-cite-body already owns the outer
   padding the Download modal instead delegates to .dl-section itself. */
.btt-cite-section {
  padding: 0 20px;
}
.btt-cite-section + .btt-cite-section {
  margin-top: 16px;
  padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}
.btt-cite-section-title {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: #eef0f4;
  margin-bottom: 8px;
}
.btt-cite-section-sub {
  font-size: var(--fs-xs);
  color: #b9c2d3;
  line-height: 1.5;
  margin: 0 0 10px;
}
/* Style tabs — MLA/APA/Chicago/Harvard/Vancouver. Selecting one swaps
   #bttCiteText's content (bttInitCiteStyles(), bos-taxon-tabs.js); reuses
   .btt-cite-copy-btn's look via its own rule below rather than sharing the
   class, since "active" needs a persistent (not just hover) highlight. */
.btt-cite-style-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 6px;
  margin-bottom: 14px;
}
.btt-cite-style-btn {
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  color: #b9c2d3;
  background: none;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 4px;
  padding: 4px 10px;
  cursor: pointer;
  transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.btt-cite-style-btn:hover {
  color: #5b8dee;
  border-color: #5b8dee;
}
.btt-cite-style-btn.active {
  color: #fff;
  background: #5b8dee;
  border-color: #5b8dee;
}
/* Stylised quote marks around the citation — visual only (CSS ::before/
   ::after), not part of the actual copied text: the Copy button reads the
   active style's plain-text variant straight from the bttCiteStyles JS
   const, not from this element, so a real citation shouldn't carry
   wrapping quote marks once pasted elsewhere. */
.btt-cite-text {
  font-size: 13px;
  color: #b9c2d3;
  line-height: 1.6;
  margin-bottom: 14px;
  word-break: break-word;
  position: relative;
  padding-left: 18px;
}
.btt-cite-text::before,
.btt-cite-text::after {
  font-style: normal;
  font-family: Georgia, 'Times New Roman', serif;
  font-size: 28px;
  line-height: 0;
  color: #5b8dee;
  opacity: 0.6;
  vertical-align: -6px;
}
.btt-cite-text::before {
  content: '\201C';
  position: absolute;
  left: 0;
  top: 0.75em;
}
.btt-cite-text::after {
  content: '\201D';
  margin-left: 4px;
}
.btt-cite-actions,
.btt-cite-export-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
}
.btt-cite-copy-btn {
  font-family: inherit;
  font-size: 12px;
  font-weight: 500;
  color: #b9c2d3;
  background: none;
  border: 1px solid rgba(255, 255, 255, 0.15);
  border-radius: 4px;
  padding: 5px 12px;
  cursor: pointer;
  transition: color 0.12s, border-color 0.12s;
}
.btt-cite-copy-btn:hover {
  color: #5b8dee;
  border-color: #5b8dee;
}
.btt-cite-copy-btn.copied {
  color: #fff;
  background: #5b8dee;
  border-color: #5b8dee;
}

/* ── Dark theme for the footer modals (Feedback/Download/Cite) ───────────
   .fb-modal-* (bos-taxon.css) is styled light-on-white for the original
   taxon.php page — this page's <body class="btt-header-page"> (set in
   header-tabs.php) is the scoping hook, since the modals themselves sit
   outside #bttPage in the DOM (siblings appended after it, not descendants)
   so #bttPage's own dark-mode custom-property redefinitions never reach
   them. Same palette as #bttPage: #5b8dee accent, #eef0f4 text on #16202f. */
body.btt-header-page .fb-modal {
  background: #16202f;
  box-shadow: 0 12px 40px rgba(0, 0, 0, 0.5);
}
body.btt-header-page .fb-modal-head {
  border-bottom-color: rgba(255, 255, 255, 0.1);
}
body.btt-header-page .fb-modal-title {
  color: #eef0f4;
}
body.btt-header-page .fb-modal-close {
  color: #8b96ac;
}
body.btt-header-page .fb-modal-close:hover {
  background: rgba(255, 255, 255, 0.08);
  color: #eef0f4;
}
body.btt-header-page .fb-modal-desc {
  color: #b9c2d3;
}
body.btt-header-page .fb-required {
  color: #e8be7a;
}
body.btt-header-page .fb-optional {
  color: #8b96ac;
}
body.btt-header-page .fb-label {
  color: #8b96ac;
}
body.btt-header-page .fb-input {
  color: #eef0f4;
  background: #0e1520;
  border-color: rgba(255, 255, 255, 0.15);
}
body.btt-header-page .fb-input:focus {
  border-color: #5b8dee;
  background: #0e1520;
}
body.btt-header-page .fb-input option {
  background: #16202f;
  color: #eef0f4;
}
body.btt-header-page .fb-btn-submit {
  background: #5b8dee;
}
body.btt-header-page .fb-btn-submit:hover {
  background: #4a7de0;
}
body.btt-header-page .fb-status--ok    { color: #6fcf7d; }
body.btt-header-page .fb-status--error { color: #f28b82; }

/* ── Download modal — form body (taxon-tabs.php only) ─────────────────────
   .dl-section/.dl-section-title etc. have no light-theme counterpart (this
   form only exists on this page — see the stub it replaced), so unlike the
   .fb-* overrides above, structure AND colour both live here in one pass
   rather than a base rule in bos-taxon.css plus a dark override here.
   Same body.btt-header-page scoping as .fb-modal itself: the modal is a
   sibling of #bttPage under <body>, not a descendant of it. */
body.btt-header-page .dl-section {
  padding: 14px 20px 0;
}
body.btt-header-page .dl-section:first-of-type {
  padding-top: 12px;
}
body.btt-header-page .dl-section + .dl-section {
  margin-top: 4px;
  padding-top: 16px;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
}
body.btt-header-page .dl-section-title {
  font-size: var(--fs-sm);
  font-weight: 700;
  color: #eef0f4;
  margin-bottom: 6px;
}
body.btt-header-page .dl-section-sub {
  font-size: var(--fs-xs);
  color: #b9c2d3;
  line-height: 1.5;
  margin: 0 0 10px;
}
body.btt-header-page .dl-section .fb-field {
  margin: 10px 0 0;
}

/* Checkbox rows — "Taxon data  (128 taxa)" style, label and count pushed to
   opposite ends of the row so a glance down the column lines the counts up. */
body.btt-header-page .dl-check-row {
  display: flex;
  align-items: center;
  gap: 8px;
  padding: 6px 0;
  font-size: var(--fs-sm);
  color: #eef0f4;
  cursor: pointer;
}
/* The site-wide "input, select, textarea { padding:12px 25px; width:100%;
   border:1px solid #e4e4e4; }" reset (style.css) targets every <input>,
   checkboxes included — left un-overridden, each checkbox stretches to fill
   the row and grows a large padded border box around itself, squashing the
   label/count text into whatever space is left over and throwing off the
   left alignment (same problem on #dlAgree in .dl-agree-row below). Reset
   back to a plain, fixed-size checkbox; flex-shrink:0 keeps it from being
   compressed by its flex siblings once it's no longer the thing hogging
   width. */
body.btt-header-page .dl-check-row input[type="checkbox"],
body.btt-header-page .dl-agree-row input[type="checkbox"] {
  width: 15px;
  height: 15px;
  min-width: 15px;
  max-width: 15px;
  flex-shrink: 0;
  padding: 0;
  margin: 0;
  border: 1px solid rgba(255, 255, 255, 0.3);
  accent-color: #5b8dee;
}
/* No flex:1 here — that stretched the label to fill the row, shoving the
   count all the way to the far right edge, away from its own label. Natural
   (content) width instead, so the count badge sits right after it with just
   .dl-check-row's own 8px gap in between. */
body.btt-header-page .dl-check-count {
  display: inline-flex;
  align-items: center;
  font-size: 10px;
  font-weight: 600;
  color: #8b96ac;
  background: rgba(255, 255, 255, 0.08);
  border-radius: 3px;
  padding: 1px 6px;
}
/* Zero-count category — checkbox is already `disabled` (view), dimmed here
   so it reads as unavailable rather than just unchecked. */
body.btt-header-page .dl-check-row--empty {
  color: #6b7385;
  cursor: default;
}
body.btt-header-page .dl-check-row--empty .dl-check-count {
  color: #5a6175;
  background: rgba(255, 255, 255, 0.04);
}

body.btt-header-page .dl-format-field {
  max-width: 220px;
}

body.btt-header-page .dl-notice {
  margin: 12px 0 4px;
  padding: 8px 10px;
  font-size: var(--fs-xs);
  line-height: 1.5;
  color: #b9c2d3;
  background: rgba(255, 255, 255, 0.05);
  border-left: 2px solid #e8be7a;
  border-radius: 0 4px 4px 0;
}

/* Conditions of use — a plain list reads better here than repeating .fb-field
   spacing for four short clauses. */
body.btt-header-page .dl-terms-list {
  margin: 0 0 10px;
  padding-left: 18px;
  font-size: var(--fs-xs);
  line-height: 1.6;
  color: #b9c2d3;
}
/* The site-wide "ul li { list-style: none; }" reset (style.css) strips
   bullets from every <li> by default — restore them here, since this list
   isn't using the theme's usual icon-prefixed list pattern. */
body.btt-header-page .dl-terms-list li {
  list-style: disc outside;
  margin-bottom: 2px;
}
body.btt-header-page .dl-privacy-link {
  margin: 0 0 12px;
  font-size: var(--fs-xs);
}
body.btt-header-page .dl-privacy-link a {
  color: #5b8dee;
}
body.btt-header-page .dl-privacy-link a:hover {
  text-decoration: underline;
}
body.btt-header-page .dl-agree-row {
  display: flex;
  align-items: flex-start;
  gap: 8px;
  font-size: var(--fs-sm);
  color: #eef0f4;
  cursor: pointer;
  padding-bottom: 4px;
}
/* Declared after the shared checkbox reset above so this margin-top wins
   over that rule's margin:0 (equal specificity, later in the cascade) —
   the row's text can wrap to a second line, so the checkbox needs nudging
   down to align with the first line's cap-height instead of the row's
   vertical center. */
body.btt-header-page .dl-agree-row input[type="checkbox"] {
  margin-top: 3px;
}

/* Themed scrollbar for the download modal itself (.fb-modal's own
   max-height:90vh/overflow-y:auto, bos-taxon.css) — three sections of form
   fields make this the one footer modal that actually scrolls in normal
   use, and the browser-default light track/thumb stands out badly against
   its dark background. Same pattern/tokens as .btt-drawer-inner and
   .btt-gallery-thumbs elsewhere on this page: scrollbar-width/-color for
   Firefox, ::-webkit-scrollbar-* for Chrome/Safari/Edge. */
body.btt-header-page .dl-modal {
  scrollbar-width: thin;
  scrollbar-color: var(--stone-dark, #8b96ac) var(--cream-dark, #16202f);
}
body.btt-header-page .dl-modal::-webkit-scrollbar {
  width: 8px;
}
body.btt-header-page .dl-modal::-webkit-scrollbar-track {
  background: var(--cream-dark, #16202f);
  border-radius: 4px;
}
body.btt-header-page .dl-modal::-webkit-scrollbar-thumb {
  background: var(--stone-dark, #8b96ac);
  border-radius: 4px;
}
body.btt-header-page .dl-modal::-webkit-scrollbar-thumb:hover {
  background: var(--navy, #5b8dee);
}

/* ── Specimen coordinates: open in Google Maps ────────────────────────────
   Small icon link next to the formatted DMS coordinates (.sp-field-value.coords,
   bos-taxon.css) — opens the exact lat/lng in Google Maps. Tooltip via
   data-tooltip is handled globally by bos-tooltip.js (header-tabs.php),
   no extra wiring needed here. */
.coords-map-link {
  display: inline-flex;
  align-items: center;
  vertical-align: -2px;
  margin-left: 4px;
  color: var(--stone-dark);
}
.coords-map-link:hover { color: var(--navy); }

/* ── Specimen filters: collapsible at ≤430px ──────────────────────────────
   #specimenFilterControls wraps every actual control (year range, the
   filter-ms dropdowns, "with photos", sort/reset) — display:contents by
   default so it contributes nothing to the flex layout and #specimenFilters
   reads exactly like the old flat structure (label + controls as direct
   flex children) at every width above the collapse tier. Only the ≤430px
   query below turns it into a real block that can be hidden/shown. */
.specimen-filter-controls { display: contents; }
.filter-toggle {
  -webkit-appearance: none;
  appearance: none;
  background: none;
  border: none;
  margin: 0;
  padding: 0;
  font: inherit;
  text-align: left;
  cursor: default;
}
.filter-toggle-caret { display: none; }
@media (max-width: 430px) {
  #specimenFilters { flex-direction: column; align-items: stretch; }
  .filter-toggle {
    display: flex;
    align-items: center;
    justify-content: space-between;
    width: 100%;
    cursor: pointer;
    padding: 2px 0;
  }
  .filter-toggle-caret {
    display: inline-block;
    width: 0;
    height: 0;
    border-left: 5px solid transparent;
    border-right: 5px solid transparent;
    border-top: 6px solid var(--stone-dark);
    transition: transform 0.15s;
  }
  #specimenFilters.filters-open .filter-toggle-caret { transform: rotate(180deg); }
  /* Collapsed by default — no inline state needed on load. */
  .specimen-filter-controls {
    display: none;
    width: 100%;
  }
  #specimenFilters.filters-open .specimen-filter-controls {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
    width: 100%;
    margin-top: 10px;
  }
}

/* ── Specimen basis badge: hide the routine "Specimen" badge on mobile ────
   Observations are the exception worth flagging on a small screen (they
   mean no physical voucher exists) — the far more common "Specimen" badge
   (.basis-specimen also covers Living/Fossil/Material sample/Occurrence)
   is redundant with the rest of the card and just adds clutter at this
   width, so only it is dropped; .basis-observation stays. */
@media (max-width: 600px) {
  #bttPage .specimen-basis.basis-specimen { display: none; }
}

/* ── Lightbox: pinch-to-zoom on the full-res image (bos-taxon-tabs.js) ────
   .lb-img-wrap clips the zoomed/panned image instead of letting it spill
   over the nav arrows/meta panel; touch-action:none on the image hands
   every touch gesture to the JS pinch/pan handler instead of letting the
   browser try to scroll/zoom the page underneath it. */
.lb-img-wrap { overflow: hidden; touch-action: none; }
.lb-img { touch-action: none; }

/* Breadcrumb minor ranks (subfamily, tribe, species group…) follow the
   tree's "all ranks" toggle: hidden unless #bttPage has .tree-all-ranks
   (taxon-tabs.php sets it from the cookie; toggleAncestors() keeps it in
   sync). bos-taxon.css puts a chevron after every ancestor link but the last
   one; when the ancestors after the last major rank are all hidden minor
   ranks, that major rank (.bc-last-major) would keep a dangling chevron. */
#bttPage:not(.tree-all-ranks) .taxon-breadcrumb .bc-minor {
  display: none;
}
#bttPage:not(.tree-all-ranks) .taxon-breadcrumb .bc-last-major::after {
  content: none;
}

/* The breadcrumb's copy of the tree's rank toggle (.tree-toggle-icon,
   bos-taxon.css), pushed to the right end of the bar. */
.taxon-breadcrumb .bc-rank-toggle {
  flex-shrink: 0;
  margin-left: auto;
}

/* Bulleted/numbered lists in rich text (Introduction, Diagnosis, …): the
   site-wide resets ("* { margin:0; padding:0 }" in bos-common.css and
   "ul li { list-style:none }" in style.css) otherwise leave them with no
   markers or indent. */
#bttPage .prose ul,
#bttPage .prose ol { margin: 0.6em 0; padding-left: 1.4em; }
#bttPage .prose ul li { list-style: disc outside; }
#bttPage .prose ol li { list-style: decimal outside; }
#bttPage .prose li + li { margin-top: 0.25em; }

/* ── "Coming Soon" notice (spiders — Taxon::withholdTaxonContent()) ────────
   Stands in for the introduction and everything below it on a taxon page
   whose content is withheld. */
#bttPage .btt-coming-soon {
  margin-top: 20px;
  padding: 18px 20px;
  background: var(--cream-dark);
  border: 1px solid var(--border-strong);
  border-radius: 6px;
}
#bttPage .btt-coming-soon-title {
  margin-bottom: 6px;
  font-size: 13px;
  font-weight: 600;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--text-primary);
}
#bttPage .btt-coming-soon p {
  margin: 0;
  font-size: var(--fs-base, 16px);
  line-height: 1.5;
  color: var(--text-secondary);
}

/* Staff "Edit" link (bttInitStaffEditLink(), bos-taxon-tabs.js) — sits
   after the rank badge, same size, for curators/admins only. */
.btt-staff-edit-link {
  display: inline-block;
  vertical-align: middle;
  font-size: 9px;
  font-weight: 500;
  letter-spacing: 0.07em;
  text-transform: uppercase;
  color: var(--text-primary);
  border: 1px solid var(--border-strong);
  border-radius: 2px;
  padding: 1px 6px;
  margin-left: 6px;
  text-decoration: none;
}
.btt-staff-edit-link:hover {
  color: var(--text-primary);
  background: var(--cream-dark);
}
