/* BM-546 - the Wipe Biography renderer stylesheet.
 *
 * Lives beside the renderer it styles (/shared/wipeBiography.js) and is loaded by BOTH hosts:
 * the website page links it, and the desktop client imports it through Vite. One file, so the
 * two surfaces cannot drift (root AGENTS.md section 4 - one source of truth for dual-surface UI).
 *
 * Every rule is scoped to a .bm-bio-* class the renderer emits, so dropping this file into any
 * page or panel is inert until the renderer mounts. All sizing is intrinsic (aspect-ratio, rem,
 * auto-fit grids) - never a pixel min-height on a panel, which is the BM-503 failure mode.
 *
 * Colour comes from the host: --panel / --border / --accent / --muted / --text / --bad are read,
 * never redefined, so the module takes on the website theme and the client theme unchanged.
 */
/* --- layout ---------------------------------------------------------- */

.bm-bio-layout {
  display: grid;
  grid-template-columns: minmax(0, 20rem) minmax(0, 1fr);
  gap: 0.95rem;
  align-items: start;
}

.bm-bio-layout.is-solo {
  grid-template-columns: minmax(0, 1fr);
}

@media (max-width: 900px) {
  .bm-bio-layout {
    grid-template-columns: minmax(0, 1fr);
  }
}

.bm-bio-dossier {
  display: grid;
  gap: 0.95rem;
  min-width: 0;
}

.bm-bio-section,
.bm-bio-shelf {
  border: 1px solid var(--border);
  border-radius: 16px;
  background: var(--panel);
  box-shadow: 0 12px 28px rgba(0, 0, 0, 0.3);
  padding: 1.1rem 1.2rem 1.2rem;
  min-width: 0;
}

/* The orange kicker line (.bm-bio-eyebrow) that sat over every heading - the hero's "WIPE
   BIOGRAPHY" and each section's RECORD / JOURNEY / MAP / GROWTH & FORTUNE / NEMESIS / TEAM /
   HONOURS - went out with BM-546 round 4, along with the shelf's "Past wipes". The <h2>s say it
   all; the rival cards kept their label under .bm-bio-rival-label (a quiet caption, not a kicker). */

.bm-bio-section-head {
  display: grid;
  gap: 0.35rem;
  margin-bottom: 0.9rem;
}

.bm-bio-section-head h2 {
  margin: 0;
  font-size: 1.25rem;
}

.bm-bio-section-note,
.bm-bio-note {
  margin: 0;
  color: var(--muted);
  font-size: 0.82rem;
}

.bm-bio-empty {
  margin: 0;
  color: var(--muted);
  font-size: 0.9rem;
}

/* --- shelf ----------------------------------------------------------- */

.bm-bio-shelf {
  position: sticky;
  top: 0.8rem;
  display: grid;
  gap: 0.6rem;
}

.bm-bio-shelf-title {
  margin: 0;
  font-size: 1.1rem;
}

.bm-bio-shelf-list {
  display: grid;
  gap: 0.55rem;
  max-height: 60vh;
  overflow: auto;
}

.bm-bio-shelf-card {
  display: grid;
  grid-template-columns: 3.6rem minmax(0, 1fr);
  gap: 0.65rem;
  align-items: center;
  text-align: left;
  padding: 0.55rem;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.07);
  background: rgba(255, 255, 255, 0.02);
  color: inherit;
  cursor: pointer;
  font: inherit;
  transition: border-color 150ms ease, transform 150ms ease;
}

.bm-bio-shelf-card:hover,
.bm-bio-shelf-card:focus-visible {
  border-color: var(--bm-warm-border, rgba(232, 135, 49, 0.35));
  transform: translateY(-1px);
}

.bm-bio-shelf-card.is-active {
  border-color: var(--accent);
  background: rgba(232, 135, 49, 0.1);
}

.bm-bio-shelf-thumb {
  display: block;
  aspect-ratio: 1;
  border-radius: 9px;
  overflow: hidden;
  background: rgba(0, 0, 0, 0.35);
}

.bm-bio-shelf-thumb img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

.bm-bio-shelf-body {
  display: grid;
  gap: 0.2rem;
  min-width: 0;
}

.bm-bio-shelf-dates {
  font-weight: 700;
  font-size: 0.86rem;
}

.bm-bio-shelf-seed,
.bm-bio-shelf-synopsis,
.bm-bio-shelf-stats {
  color: var(--muted);
  font-size: 0.74rem;
}

.bm-bio-shelf-synopsis {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.bm-bio-shelf-stats {
  display: flex;
  flex-wrap: wrap;
  gap: 0.15rem 0.5rem;
}

/* --- nav + share ----------------------------------------------------- */

.bm-bio-nav {
  position: sticky;
  top: 0;
  z-index: 3;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
  padding: 0.5rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--panel-2);
}

.bm-bio-nav-link,
.bm-bio-btn,
.bm-bio-layer,
.bm-bio-grid-chip {
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  color: inherit;
  font: inherit;
  font-size: 0.78rem;
  padding: 0.25rem 0.7rem;
  cursor: pointer;
}

.bm-bio-nav-link.is-active,
.bm-bio-layer.is-active {
  border-color: var(--accent);
  background: rgba(232, 135, 49, 0.16);
  color: var(--accent-2);
}

.bm-bio-nav-actions {
  margin-left: auto;
}

.bm-bio-share {
  display: inline-flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.35rem;
}

.bm-bio-share-url {
  min-width: 12rem;
  max-width: 22rem;
  padding: 0.25rem 0.55rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(0, 0, 0, 0.3);
  color: inherit;
  font-size: 0.76rem;
}

.bm-bio-share-error {
  color: var(--bad);
  font-size: 0.76rem;
}

/* --- hero plate ------------------------------------------------------ */

.bm-bio-plate-top {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.9rem;
  align-items: center;
}

.bm-bio-plate-avatar {
  width: 4.2rem;
  height: 4.2rem;
  border-radius: 50%;
  overflow: hidden;
  display: grid;
  place-items: center;
  background: rgba(232, 135, 49, 0.16);
  font-size: 1.6rem;
  font-weight: 800;
}

.bm-bio-plate-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.bm-bio-plate-name {
  margin: 0;
  font-size: 1.7rem;
  line-height: 1.1;
  overflow-wrap: anywhere;
}

.bm-bio-plate-meta {
  margin: 0.15rem 0 0;
  color: var(--muted);
  font-size: 0.84rem;
}

.bm-bio-synopsis {
  margin: 0.55rem 0 0;
  font-size: 0.98rem;
  line-height: 1.5;
}

/* The fun-facts row that used to live here (.bm-bio-funfacts / .bm-bio-funfact*) went out with
   BM-546 round 2: the same facts are woven into the synopsis prose server-side, and the renderer no
   longer puts the bulleted version on the page. renderFunFacts() is still exported, so if a surface
   ever wants it back the styles are one `git show` away - they are not kept here as dead weight. */

.bm-bio-vitals {
  margin-top: 1rem;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  gap: 0.5rem;
}

.bm-bio-vital {
  display: grid;
  gap: 0.1rem;
  padding: 0.55rem 0.6rem;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.02);
}

.bm-bio-vital-value {
  font-size: 1.15rem;
  font-weight: 800;
}

.bm-bio-vital-label {
  color: var(--muted);
  font-size: 0.7rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

/* --- record ---------------------------------------------------------- */

.bm-bio-facts {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(14rem, 1fr));
  gap: 0.15rem 1.2rem;
}

.bm-bio-fact {
  display: flex;
  justify-content: space-between;
  gap: 0.6rem;
  padding: 0.32rem 0;
  border-bottom: 1px dashed rgba(255, 255, 255, 0.06);
}

.bm-bio-fact dt {
  color: var(--muted);
  font-size: 0.82rem;
}

.bm-bio-fact dd {
  margin: 0;
  font-weight: 700;
  font-size: 0.86rem;
}

.bm-bio-chips {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
}

.bm-bio-chip,
.bm-bio-badge {
  display: inline-flex;
  align-items: center;
  padding: 0.16rem 0.55rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.09);
  background: rgba(255, 255, 255, 0.03);
  font-size: 0.72rem;
  color: var(--muted);
}

.bm-bio-badge {
  color: var(--accent-2);
  border-color: rgba(232, 135, 49, 0.35);
  background: rgba(232, 135, 49, 0.12);
  width: max-content;
}

.bm-bio-causes {
  margin-top: 0.9rem;
  display: grid;
  gap: 0.4rem;
}

/* --- deaths by cause: icon tiles -------------------------------------- */

/* A wrapping row rather than a fixed grid: the tile count is however many ways the wipe found to
   kill you (1 on a quiet wipe, 8 on a loud one), and a grid would leave holes at both ends. */
.bm-bio-cause-tiles {
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
}

.bm-bio-cause {
  position: relative;
  display: grid;
  justify-items: center;
  align-content: center;
  gap: 0.15rem;
  /* Noticeably bigger than the pills these replaced - an icon has to be read, not skimmed - but
     sized in rem and min-width only, so three of them still fit across a phone. */
  min-width: 4.25rem;
  padding: 0.5rem 0.55rem 0.45rem;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.08);
  background: rgba(255, 255, 255, 0.025);
  color: var(--muted);
  cursor: default;
  transition: border-color 140ms ease, color 140ms ease;
}

.bm-bio-cause:hover,
.bm-bio-cause:focus-visible {
  border-color: var(--accent, #e88731);
  color: var(--accent-2, #ffb36c);
  outline: none;
}

.bm-bio-cause-icon {
  width: 1.45rem;
  height: 1.45rem;
  display: block;
}

.bm-bio-cause-count {
  font-size: 1rem;
  font-weight: 800;
  font-variant-numeric: tabular-nums;
  color: var(--text);
  line-height: 1.1;
}

/* The tooltip is CSS, not the native `title`: `title` waits out the browser's own delay, cannot be
   styled and never appears for a keyboard user. This one shows on hover AND on :focus-visible, and
   the tile's aria-label carries the same words for a screen reader, so nothing depends on it. */
.bm-bio-cause-tip {
  position: absolute;
  bottom: calc(100% + 0.3rem);
  left: 50%;
  transform: translateX(-50%);
  z-index: 4;
  padding: 0.18rem 0.5rem;
  border-radius: 8px;
  border: 1px solid var(--border);
  background: rgba(3, 6, 12, 0.96);
  color: var(--text);
  font-size: 0.7rem;
  white-space: nowrap;
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity 120ms ease;
}

.bm-bio-cause:hover .bm-bio-cause-tip,
.bm-bio-cause:focus-visible .bm-bio-cause-tip {
  opacity: 1;
  visibility: visible;
}

@media (prefers-reduced-motion: reduce) {
  .bm-bio-cause,
  .bm-bio-cause-tip {
    transition: none;
  }
}

/* --- journey / life line --------------------------------------------- */

/* The wrap is the anchor for the moment card, so it must NOT clip - only the svg box does. */
.bm-bio-lifeline-wrap {
  position: relative;
}

.bm-bio-lifeline {
  width: 100%;
  aspect-ratio: 1000 / 140;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(0, 0, 0, 0.22);
  overflow: hidden;
}

.bm-bio-lifeline svg {
  width: 100%;
  height: 100%;
  display: block;
  /* A horizontal drag is a PAN and belongs to the line; a vertical one is still the page's, so the
     timeline never traps a touch scroll (BM-546 round 3). */
  touch-action: pan-y;
}

.bm-bio-lifeline:focus-visible {
  outline: 2px solid var(--accent, #e88731);
  outline-offset: 2px;
}

/* Zoomed in, the line is draggable - say so with the cursor. */
.bm-bio-lifeline.is-zoomed {
  cursor: grab;
}

.bm-bio-lifeline.is-panning {
  cursor: grabbing;
}

/* The window group is redrawn wholesale on every zoom or pan. A pan must never fade (it would lag
   the pointer), so ONLY a zoom step adds the flash class - and prefers-reduced-motion drops the
   transition entirely. */
.bm-bio-line-window {
  transition: opacity 140ms ease;
}

.bm-bio-line-window.is-zoom-flash {
  opacity: 0.45;
}

@media (prefers-reduced-motion: reduce) {
  .bm-bio-line-window {
    transition: none;
  }

  .bm-bio-line-dot {
    transition: none;
  }
}

.bm-bio-line-axis {
  /* Token, not a white literal: the grid behind it is token-coloured, so a literal made the
     "bold axis" the faintest line on the light theme. */
  stroke: var(--muted, #8594ad);
  stroke-opacity: 0.8;
  stroke-width: 2;
}

/* The graph-PAPER grid (BM-546 round 7, time-anchored in round 8) - the backdrop the timeline is
   drawn on.

   Round 6 put a filled activity area in this slot; the review's actual ask was graph paper, so
   this is a ruler and nothing else: majors on the date axis's own tick positions, minors between
   them, rows across the plot band. The vertical axis carries NO value - the dots are all in one
   row - the grid just lets an eye carry a dot down to the date under it.

   Round 8 put units on the ruler. A major is a real day (or 6 hours, or an hour, as the window
   narrows) in the DISPLAY time zone, and a minor is the next step down; both come from the same
   axisTicks() call that draws the date ticks, so a gridline and the label under it are the same
   x by construction. That also means the grid now MOVES with a zoom or a pan, which is why the
   redraw rules below matter.

   TWO WEIGHTS, both quiet. The timeline line itself (.bm-bio-line-axis, above) stays the
   emphasised axis of the reference image and is deliberately not touched here; the majors sit a
   clear step under it and the minors a step under those. It is paper, not content: a 7-unit
   coloured dot has to keep winning the eye.

   Colour comes from the host's --muted token (the same one the axis labels use) at low opacity, so
   the paper takes the dark theme, the warm light theme and the client's palette with one rule. No
   literal here would have worked on all three.

   non-scaling-stroke because the whole grid is a hairline: the viewBox is 1000 wide and rendered
   anywhere from ~320px to ~1400px, so a stroke measured in user units would be 0.3px on a phone
   (invisible) and 1.4px on a desktop (heavy). In device pixels it is the same hairline everywhere.

   No transition is declared, and from BM-546 round 8 that matters MORE rather than less: the
   verticals are real time boundaries now, so they are redrawn on every frame of a pan. A
   transition on a path that is replaced sixty times a second would never finish one, and would
   turn a crisp ruler into a smear - so there is nothing here for prefers-reduced-motion to
   suppress either.

   pointer-events: none for the same reason the date axis has it - the grid is painted over the
   transparent capture rect, and without this it would steal the hit test for every scrub and pan
   started over the middle of the line. */
.bm-bio-line-grid {
  pointer-events: none;
}

.bm-bio-line-grid-minor,
.bm-bio-line-grid-major {
  fill: none;
  stroke: var(--muted, #8594ad);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.bm-bio-line-grid-minor {
  stroke-opacity: 0.16;
}

.bm-bio-line-grid-major {
  stroke-opacity: 0.4;
}

.bm-bio-line-tick {
  stroke: #ff7a6b;
  stroke-width: 2.5;
  stroke-opacity: 0.8;
}

/* A fat transparent line over each death tick: the tick is 2 units wide, the pointer is not. */
.bm-bio-line-hit {
  stroke: transparent;
  stroke-width: 10;
}

.bm-bio-line-death:hover .bm-bio-line-tick {
  stroke-opacity: 1;
  stroke-width: 3.5;
}

.bm-bio-line-datetick line {
  stroke: var(--muted, #8594ad);
  stroke-opacity: 0.55;
  stroke-width: 1.5;
}

.bm-bio-line-datetick text {
  fill: var(--muted);
  font-size: 12px;
  letter-spacing: 0.02em;
}

.bm-bio-line-cursor {
  stroke: var(--accent-2);
  stroke-width: 1.5;
  stroke-dasharray: 3 3;
  /* The cursor is drawn last so it sits over the dots - which means it is also sitting exactly
     under the pointer. Without this it would win the hit test and every dot would fire
     pointerenter/pointerleave in a loop as the cursor caught up. */
  pointer-events: none;
}

/* The date axis is decoration under the line; it must never take a pointer event off a dot. */
.bm-bio-line-datetick {
  pointer-events: none;
}

.bm-bio-line-point {
  cursor: pointer;
}

.bm-bio-line-stem {
  stroke-width: 2;
  stroke-opacity: 0.45;
}

/* A dark ring keeps a dot readable whatever colour its group is. */
.bm-bio-line-dot {
  stroke: rgba(6, 10, 16, 0.85);
  stroke-width: 2.5;
  transition: r 120ms ease;
}

.bm-bio-line-point:hover .bm-bio-line-dot,
.bm-bio-line-point:focus .bm-bio-line-dot {
  r: 9.5;
}

.bm-bio-line-point:focus {
  outline: none;
}

.bm-bio-line-point.is-active .bm-bio-line-dot {
  r: 9.5;
  stroke: var(--accent-2, #ffb36c);
  stroke-width: 3;
}

/* --- zoom control + overview strip ------------------------------------ */

/* Both sit INSIDE the timeline box (the wrap), under the line, which is what makes them read as
   part of the timeline rather than as page furniture. */
.bm-bio-zoom {
  display: flex;
  align-items: center;
  gap: 0.45rem;
  margin-top: 0.45rem;
  flex-wrap: wrap;
}

.bm-bio-zoom-label {
  font-size: 0.72rem;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--muted);
}

.bm-bio-zoom-step {
  width: 1.6rem;
  height: 1.6rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  color: inherit;
  font: inherit;
  font-size: 0.95rem;
  line-height: 1;
  cursor: pointer;
}

.bm-bio-zoom-step:hover {
  border-color: var(--accent);
  color: var(--accent-2);
}

/* The track is drawn by hand rather than left to `accent-color`.
 *
 * A native range input insets its thumb by half its own width at each end, so the thumb can never
 * reach the ends of the bar it sits on - at 1x it stopped short of the left edge and at max it
 * stopped short of the right. Giving the track the same half-thumb margin shortens the BAR to
 * exactly the distance the thumb's centre travels, so the two line up end to end. The control's
 * overall width is unchanged, so the card it sits in does not move. */
.bm-bio-zoom-slider {
  flex: 1 1 8rem;
  min-width: 6rem;
  max-width: 22rem;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
  background: transparent;
  height: 1rem;
  margin: 0;
}

.bm-bio-zoom-slider::-webkit-slider-runnable-track {
  height: 0.3rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  margin: 0 0.5rem;
}

.bm-bio-zoom-slider::-moz-range-track {
  height: 0.3rem;
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.14);
  margin: 0 0.5rem;
}

.bm-bio-zoom-slider::-webkit-slider-thumb {
  -webkit-appearance: none;
  appearance: none;
  width: 1rem;
  height: 1rem;
  border: none;
  border-radius: 50%;
  background: var(--accent, #e88731);
  margin-top: -0.35rem;
}

.bm-bio-zoom-slider::-moz-range-thumb {
  width: 1rem;
  height: 1rem;
  border: none;
  border-radius: 50%;
  background: var(--accent, #e88731);
}

.bm-bio-zoom-slider:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
}

.bm-bio-zoom-readout {
  min-width: 2.6rem;
  font-variant-numeric: tabular-nums;
  font-size: 0.82rem;
  color: var(--accent-2, #ffb36c);
}

.bm-bio-zoom-reset {
  border: 1px solid rgba(255, 255, 255, 0.09);
  border-radius: 999px;
  background: rgba(255, 255, 255, 0.03);
  color: inherit;
  font: inherit;
  font-size: 0.72rem;
  padding: 0.18rem 0.6rem;
  cursor: pointer;
}

.bm-bio-zoom-reset[hidden] {
  display: none;
}

/* The whole wipe, always - the map OF the line. Height is set by the viewBox's aspect ratio so no
   pixel height is pinned anywhere (BM-503). */
.bm-bio-overview {
  margin-top: 0.3rem;
  border-radius: 8px;
  border: 1px solid rgba(255, 255, 255, 0.05);
  background: rgba(0, 0, 0, 0.18);
  overflow: hidden;
}

.bm-bio-overview-svg {
  display: block;
  width: 100%;
  aspect-ratio: 1000 / 28;
  cursor: pointer;
  touch-action: pan-y;
}

.bm-bio-overview-axis {
  stroke: rgba(255, 255, 255, 0.14);
  stroke-width: 1.5;
}

.bm-bio-overview-mark {
  stroke-width: 2;
  stroke-opacity: 0.55;
}

.bm-bio-overview-mark.is-death {
  stroke-opacity: 0.75;
}

.bm-bio-overview-window {
  fill: rgba(232, 135, 49, 0.14);
  stroke: var(--accent, #e88731);
  stroke-width: 1.5;
  stroke-opacity: 0.8;
}

/* --- the anchored moment card ---------------------------------------- */

.bm-bio-line-card {
  position: absolute;
  top: calc(100% + 0.55rem);
  transform: translateX(-50%);
  z-index: 5;
  display: grid;
  gap: 0.35rem;
  width: min(24rem, 88%);
  padding: 0.7rem 0.85rem 0.75rem;
  border-radius: 12px;
  border: 1px solid var(--border);
  background: rgba(8, 12, 19, 0.97);
  box-shadow: 0 16px 34px rgba(0, 0, 0, 0.45);
}

/* Flipped above the line when there is no room below it - a short embedded frame, or a moment
   opened with the page scrolled to the bottom. mount() measures and toggles the class; `left` is
   set in pixels there too, so the card is always clamped inside the timeline box. */
.bm-bio-line-card.is-above {
  top: auto;
  bottom: calc(100% + 0.55rem);
}

.bm-bio-line-card[hidden] {
  display: none;
}

.bm-bio-line-card:focus {
  outline: none;
}

.bm-bio-card-bar {
  position: absolute;
  inset: 0 0 auto 0;
  height: 3px;
  border-radius: 12px 12px 0 0;
}

.bm-bio-card-close {
  position: absolute;
  top: 0.3rem;
  right: 0.35rem;
  width: 1.4rem;
  height: 1.4rem;
  display: grid;
  place-items: center;
  padding: 0;
  border: 0;
  border-radius: 50%;
  background: transparent;
  color: var(--muted);
  font: inherit;
  font-size: 1rem;
  line-height: 1;
  cursor: pointer;
}

.bm-bio-card-close:hover {
  color: var(--text);
  background: rgba(255, 255, 255, 0.07);
}

.bm-bio-card-head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  gap: 0.5rem;
  align-items: center;
  padding-right: 1.2rem;
}

.bm-bio-card-icon {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #10161f;
  font-weight: 800;
  font-size: 0.72rem;
}

.bm-bio-card-headings {
  display: grid;
  gap: 0.05rem;
  min-width: 0;
}

.bm-bio-card-kicker {
  font-size: 0.64rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--muted);
}

.bm-bio-card-title {
  font-weight: 700;
  font-size: 0.95rem;
  overflow-wrap: anywhere;
}

.bm-bio-card-detail {
  color: var(--muted);
  font-size: 0.82rem;
  line-height: 1.45;
  overflow-wrap: anywhere;
}

.bm-bio-card-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.4rem;
  color: var(--muted);
  font-size: 0.74rem;
}

.bm-bio-legend {
  display: flex;
  flex-wrap: wrap;
  gap: 0.35rem 0.8rem;
  margin: 0.5rem 0 0.9rem;
  color: var(--muted);
  font-size: 0.72rem;
}

.bm-bio-legend-item {
  display: inline-flex;
  align-items: center;
  gap: 0.32rem;
}

.bm-bio-legend-item i {
  width: 0.6rem;
  height: 0.6rem;
  border-radius: 50%;
  display: inline-block;
}

/* --- timeline tabs (List / Map) ---------------------------------------
 *
 * Round 5: the two tabs DIVIDE the panel rather than sitting in the corner of it. A two-column
 * grid of 1fr 1fr is the whole mechanism - each tab is exactly half of the content box at every
 * width, with no gap, no wrap and no radius, so they read as two rectangular cards and not as
 * pills. The divider between them is tab two's left border; box-sizing keeps that border inside
 * its own half, so the 50/50 split survives it. */

.bm-bio-tablist {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 0;
  margin: 0 0 0.75rem;
  border: 1px solid var(--border);
  border-radius: 0;
}

.bm-bio-tab {
  box-sizing: border-box;
  min-width: 0;
  width: 100%;
  border: 0;
  border-radius: 0;
  background: rgba(255, 255, 255, 0.03);
  color: inherit;
  font: inherit;
  font-size: 0.8rem;
  font-weight: 500;
  /* Generous vertical padding, small horizontal: at 360px each half is ~170px and "List" must
     stay centred on one line whatever the host's font is. */
  padding: 0.5rem 0.4rem;
  text-align: center;
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
  cursor: pointer;
}

/* The hairline between the two cards. */
.bm-bio-tab + .bm-bio-tab {
  border-left: 1px solid var(--border);
}

/* Never colour alone: the selected tab is heavier, lit, and carries a solid bar along its bottom
   edge. The bar is an inset shadow rather than a border so it cannot change the tab's box and
   nudge the 50/50 split. */
.bm-bio-tab.is-active,
.bm-bio-tab[aria-selected="true"] {
  background: rgba(232, 135, 49, 0.16);
  color: var(--accent-2);
  font-weight: 700;
  box-shadow: inset 0 -3px 0 var(--accent, #e88731);
}

.bm-bio-tab:focus-visible {
  /* Inset so the ring is not clipped by the tablist's own border. */
  outline: 2px solid var(--accent, #e88731);
  outline-offset: -3px;
}

.bm-bio-tabpanel {
  min-width: 0;
}

/* The inactive panel is really gone - not dimmed - so nothing inside it is focusable or announced. */
.bm-bio-tabpanel[hidden] {
  display: none;
}

.bm-bio-tabpanel:focus {
  outline: none;
}

.bm-bio-days,
.bm-bio-moment-list,
.bm-bio-mates,
.bm-bio-honors,
.bm-bio-h2h-list {
  list-style: none;
  margin: 0;
  padding: 0;
}

.bm-bio-days {
  display: grid;
  gap: 0.75rem;
  max-height: 34rem;
  overflow: auto;
}

.bm-bio-day-label {
  margin: 0 0 0.3rem;
  font-size: 0.74rem;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--muted);
}

.bm-bio-moment-list {
  display: grid;
  gap: 0.3rem;
}

.bm-bio-moment {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr) auto;
  gap: 0.6rem;
  align-items: center;
  padding: 0.45rem 0.55rem;
  border-radius: 10px;
  border: 1px solid rgba(255, 255, 255, 0.05);
  background: rgba(255, 255, 255, 0.015);
}

.bm-bio-moment.is-focus {
  border-color: var(--accent);
  background: rgba(232, 135, 49, 0.1);
}

/* The row the open card belongs to - louder than a hover focus, and it keeps a left rail so it is
   findable after the list scrolls it into place. */
.bm-bio-moment.is-active {
  border-color: var(--accent);
  background: rgba(232, 135, 49, 0.16);
  box-shadow: inset 3px 0 0 var(--accent);
}

.bm-bio-moment-glyph {
  width: 1.5rem;
  height: 1.5rem;
  border-radius: 50%;
  display: grid;
  place-items: center;
  color: #10161f;
  font-weight: 800;
  font-size: 0.72rem;
  flex: none;
}

.bm-bio-moment-body {
  display: grid;
  gap: 0.1rem;
  min-width: 0;
}

.bm-bio-moment-title {
  font-weight: 700;
  font-size: 0.88rem;
  overflow-wrap: anywhere;
}

.bm-bio-moment-detail {
  color: var(--muted);
  font-size: 0.8rem;
  overflow-wrap: anywhere;
}

.bm-bio-moment-side {
  display: flex;
  align-items: center;
  gap: 0.4rem;
  color: var(--muted);
  font-size: 0.74rem;
}

/* --- map ------------------------------------------------------------- */

/* The toggles used to sit in the map section's <header>, which gave them their spacing. Inside the
   Map tab panel they have to carry it themselves - above the map, under the note. */
.bm-bio-layers {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0.45rem 0 0.6rem;
}

/* Never a CSS grid on the map stage - markers are absolutely positioned
   against it and a grid changes their containing block (memory:
   overlay_map_marker_render). */
.bm-bio-map {
  position: relative;
  display: block;
  width: 100%;
  aspect-ratio: 1;
  border-radius: 14px;
  overflow: hidden;
  border: 1px solid rgba(255, 255, 255, 0.07);
  background: rgba(0, 0, 0, 0.35);
}

.bm-bio-map-image {
  width: 100%;
  height: 100%;
  object-fit: contain;
  display: block;
}

.bm-bio-marker {
  position: absolute;
  width: 0.85rem;
  height: 0.85rem;
  padding: 0;
  transform: translate(-50%, -50%);
  border-radius: 50%;
  border: 2px solid rgba(0, 0, 0, 0.6);
  background: var(--bm-bio-marker-color, var(--accent));
  cursor: pointer;
  /* Above the path layer (z-index 1), below the tip (3). */
  z-index: 2;
}

.bm-bio-marker.is-focus {
  box-shadow: 0 0 0 4px rgba(255, 179, 108, 0.45);
  z-index: 2;
}

.bm-bio-marker.is-active {
  box-shadow: 0 0 0 5px rgba(255, 179, 108, 0.6);
  transform: translate(-50%, -50%) scale(1.25);
  z-index: 2;
}

/* --- movement path --------------------------------------------------- */

/* The route layer sits between the map image and the markers: same 0-100 space the markers are
   positioned in, so a 0-100 viewBox stretched over the stage lines up exactly. */
.bm-bio-path {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  pointer-events: none;
  z-index: 1;
}

.bm-bio-path[hidden] {
  display: none;
}

.bm-bio-path-under,
.bm-bio-path-line {
  fill: none;
  stroke-linejoin: round;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
}

/* Dark under-stroke first so the route stays visible over snow and sand. */
.bm-bio-path-under {
  stroke: rgba(4, 7, 12, 0.55);
  stroke-width: 3;
}

.bm-bio-path-line {
  stroke: var(--accent-2, #ffb36c);
  stroke-width: 1.5;
  stroke-opacity: 0.75;
}

/* "You were here" - moves along the route while the journey line is scrubbed. */
.bm-bio-path-here[hidden] {
  display: none;
}

.bm-bio-here-halo {
  fill: rgba(255, 179, 108, 0.25);
  stroke: rgba(255, 179, 108, 0.75);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.bm-bio-here-dot {
  fill: #fff;
  stroke: rgba(4, 7, 12, 0.7);
  stroke-width: 1;
  vector-effect: non-scaling-stroke;
}

.bm-bio-map-tip {
  position: absolute;
  transform: translate(-50%, -160%);
  padding: 0.2rem 0.5rem;
  border-radius: 8px;
  background: rgba(3, 6, 12, 0.92);
  border: 1px solid var(--border);
  font-size: 0.72rem;
  pointer-events: none;
  white-space: nowrap;
  z-index: 3;
}

/* --- charts ---------------------------------------------------------- */

.bm-bio-charts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(16rem, 1fr));
  gap: 0.8rem;
}

.bm-bio-chart {
  margin: 0;
  display: grid;
  gap: 0.3rem;
  padding: 0.7rem 0.8rem 0.8rem;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.02);
}

.bm-bio-chart figcaption {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 0.5rem;
  color: var(--muted);
  font-size: 0.76rem;
}

.bm-bio-chart figcaption strong {
  color: var(--text);
  font-size: 1rem;
}

.bm-bio-chart-svg {
  width: 100%;
  aspect-ratio: 400 / 150;
  display: block;
}

.bm-bio-chart-grid {
  stroke: rgba(255, 255, 255, 0.08);
  stroke-width: 1;
}

.bm-bio-chart-axis {
  fill: var(--muted);
  font-size: 9px;
}

.bm-bio-chart-cursor {
  stroke: rgba(255, 179, 108, 0.5);
  stroke-width: 1;
}

/* The hover readout is empty until you point at the chart, so its box has to be EXACTLY one line
 * tall whether or not it has text - otherwise the card grows the moment you hover it, and because
 * these cards share a grid row, the tallest one growing moves all of them.
 *
 * It reserved 1.1em against a line box of 1.5em, so it under-reserved by roughly 5px. The two are
 * now the same number, and the text is pinned to one line: a second line would grow the card again
 * on a long label. */
.bm-bio-chart-readout {
  margin: 0;
  line-height: 1.5;
  min-height: 1.5em;
  font-size: 0.74rem;
  color: var(--accent-2);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.bm-bio-chart-legend {
  display: flex;
  gap: 0.6rem;
  color: var(--muted);
  font-size: 0.72rem;
}

/* --- nemesis --------------------------------------------------------- */

.bm-bio-rivals {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(11rem, 1fr));
  gap: 0.6rem;
  margin-bottom: 0.9rem;
}

.bm-bio-rival-card {
  display: grid;
  gap: 0.1rem;
  padding: 0.65rem 0.75rem;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.02);
}

/* The card's caption. It replaced a .bm-bio-eyebrow kicker and is deliberately quiet - muted, not
   uppercase, not orange - so it reads as a label rather than as a second kicker. */
.bm-bio-rival-label {
  margin: 0;
  color: var(--muted);
  font-size: 0.72rem;
}

.bm-bio-rival-card strong {
  font-size: 1rem;
  overflow-wrap: anywhere;
}

.bm-bio-rival-card span {
  color: var(--muted);
  font-size: 0.78rem;
}

.bm-bio-h2h-list {
  display: grid;
  gap: 0.35rem;
}

.bm-bio-h2h {
  display: grid;
  grid-template-columns: minmax(6rem, 10rem) minmax(0, 1fr) auto;
  gap: 0.6rem;
  align-items: center;
  font-size: 0.82rem;
}

.bm-bio-h2h-name {
  overflow-wrap: anywhere;
}

.bm-bio-h2h-bar {
  display: flex;
  height: 0.5rem;
  border-radius: 999px;
  overflow: hidden;
  background: rgba(255, 255, 255, 0.05);
}

.bm-bio-h2h-win {
  background: #7ec7ff;
}

.bm-bio-h2h-loss {
  background: #ff7a6b;
}

.bm-bio-h2h-score {
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* --- team ------------------------------------------------------------ */

.bm-bio-mates {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(13rem, 1fr));
  gap: 0.55rem;
  margin-bottom: 0.6rem;
}

.bm-bio-mate {
  display: grid;
  gap: 0.25rem;
  padding: 0.6rem 0.7rem;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.02);
}

.bm-bio-mate-name {
  font-weight: 700;
  overflow-wrap: anywhere;
}

.bm-bio-mate-dates {
  color: var(--muted);
  font-size: 0.76rem;
}

/* --- honours --------------------------------------------------------- */

/* Honour tiles.
 *
 * The default is the stacked layout's: auto-fit, which is right when the panel is full width.
 * Inside the top block's left column it is overridden below, because there auto-fit could only
 * ever manage ONE tile per row - which is what made that column tall enough to leave the facts
 * panel beside it half empty.
 *
 * A `minmax(max(7rem, (100% - gaps) / 3), 1fr)` would express "at most three" in one line, but
 * Chrome does not resolve the percentage predictably inside repeat(auto-fit, ...) - it produced
 * two columns where three fit - so the count is set explicitly at the two widths that matter. */
.bm-bio-honors {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(12rem, 1fr));
  gap: 0.55rem;
}

/* Three across wherever the two-column top is active - which is the same 1100px the split itself
 * starts at, so there is one breakpoint governing both rather than two that can disagree. */
@media (min-width: 1101px) {
  .bm-bio-top-main .bm-bio-honors {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }
}

.bm-bio-honor {
  display: grid;
  gap: 0.18rem;
  padding: 0.65rem 0.75rem;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.06);
  background: rgba(255, 255, 255, 0.02);
}

.bm-bio-honor.is-earned {
  border-color: rgba(232, 135, 49, 0.4);
  background: rgba(232, 135, 49, 0.1);
}

/* No `.is-locked` rule any more: BM-540 round 9 renders only the honors a player EARNED, so an
   unearned tile no longer exists to dim. */

.bm-bio-honor-title {
  font-weight: 800;
  font-size: 0.9rem;
}

.bm-bio-honor-rank {
  color: var(--accent-2);
  font-size: 0.78rem;
}

.bm-bio-honor-desc {
  color: var(--muted);
  font-size: 0.78rem;
}

/* --- the pecking order (BM-540 round 11) ------------------------------
 *
 * A ladder of tiers, each holding one or more cells (a team, or a solo player). The rank rail down
 * the left is what makes it read as a hierarchy rather than a list of cards; the rail line is
 * decorative and drawn between badges only, so the top tier never hangs off a stray stroke.
 *
 * The outcome colours are the panel's whole point: once a side is picked, every cell it fought is
 * tinted with the result, so "where did I fall on this, and against whom" is answered by looking
 * rather than by reading. Win/loss/draw are drawn as a left edge plus a wash - never colour alone,
 * since the same information has to survive a colourblind reader (the detail strip below spells
 * every result out in words as well).
 */

.bm-bio-ladder {
  list-style: none;
  margin: 0 0 0.9rem;
  padding: 0;
  display: grid;
  gap: 0.5rem;
}

.bm-bio-tier {
  position: relative;
  display: grid;
  grid-template-columns: 1.75rem minmax(0, 1fr);
  align-items: center;
  gap: 0.7rem;
}

/* The rail between tiers - the visual "falls below them" the ranking means. */
.bm-bio-tier + .bm-bio-tier::before {
  content: "";
  position: absolute;
  left: 0.85rem;
  top: -0.5rem;
  height: 0.5rem;
  border-left: 2px solid rgba(255, 255, 255, 0.12);
}

.bm-bio-tier-rank {
  display: grid;
  place-items: center;
  width: 1.75rem;
  height: 1.75rem;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(0, 0, 0, 0.3);
  font-size: 0.8rem;
  font-weight: 700;
  color: var(--muted);
}

.bm-bio-tier:first-child .bm-bio-tier-rank {
  border-color: var(--bm-warm-border, rgba(232, 135, 49, 0.35));
  background: rgba(232, 135, 49, 0.16);
  color: var(--text);
}

.bm-bio-tier-cells {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 0.45rem;
  min-width: 0;
}

.bm-bio-cell {
  min-width: 0;
}

.bm-bio-node {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  grid-template-areas:
    "names  record"
    "names  kills";
  align-items: center;
  gap: 0 0.7rem;
  width: 100%;
  padding: 0.5rem 0.7rem;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.07);
  border-left: 3px solid rgba(255, 255, 255, 0.07);
  background: rgba(255, 255, 255, 0.02);
  color: inherit;
  font: inherit;
  text-align: left;
  cursor: pointer;
  transition: border-color 0.12s ease, background-color 0.12s ease, transform 0.12s ease;
}

.bm-bio-node:hover {
  transform: translateY(-1px);
  border-color: rgba(255, 255, 255, 0.18);
}

.bm-bio-node:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

.bm-bio-node-names {
  grid-area: names;
  font-weight: 700;
  overflow-wrap: anywhere;
}

.bm-bio-node-record {
  grid-area: record;
  font-size: 0.78rem;
  font-weight: 700;
  white-space: nowrap;
}

.bm-bio-node-kills {
  grid-area: kills;
  font-size: 0.72rem;
  color: var(--muted);
  white-space: nowrap;
}

.bm-bio-node.is-you {
  border-color: var(--bm-warm-border, rgba(232, 135, 49, 0.35));
  background: rgba(232, 135, 49, 0.08);
}

.bm-bio-node.is-selected {
  border-color: var(--accent);
  background: rgba(232, 135, 49, 0.14);
}

/* Fought the selected side - the result, as an edge AND a wash. */
.bm-bio-node.is-win {
  border-left-color: var(--bm-bio-win, #4caf7d);
  background: rgba(76, 175, 125, 0.09);
}

.bm-bio-node.is-loss {
  border-left-color: var(--bad);
  background: rgba(214, 90, 74, 0.09);
}

.bm-bio-node.is-draw {
  border-left-color: rgba(255, 255, 255, 0.28);
}

/* --- the selected side's rivalries ------------------------------------ */

.bm-bio-ladder-detail {
  padding-top: 0.7rem;
  border-top: 1px solid rgba(255, 255, 255, 0.07);
}

.bm-bio-duels-head {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.2rem 0.6rem;
  margin: 0 0 0.5rem;
  font-size: 0.95rem;
}

.bm-bio-duels-who {
  font-weight: 700;
  overflow-wrap: anywhere;
}

.bm-bio-duels-rank {
  font-size: 0.78rem;
  font-weight: 400;
  color: var(--muted);
}

.bm-bio-duels {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.3rem;
}

.bm-bio-duel {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto auto;
  align-items: center;
  gap: 0.6rem;
  padding: 0.4rem 0.6rem;
  border-radius: 10px;
  border-left: 3px solid rgba(255, 255, 255, 0.12);
  background: rgba(255, 255, 255, 0.02);
}

.bm-bio-duel.is-win { border-left-color: var(--bm-bio-win, #4caf7d); }
.bm-bio-duel.is-loss { border-left-color: var(--bad); }

.bm-bio-duel-foe {
  overflow-wrap: anywhere;
}

.bm-bio-duel-score {
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
  color: var(--muted);
}

.bm-bio-duel-score b {
  color: var(--text);
}

.bm-bio-duel-dash {
  padding: 0 0.25rem;
}

.bm-bio-duel-outcome {
  font-size: 0.75rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  color: var(--muted);
}

.bm-bio-duel.is-win .bm-bio-duel-outcome { color: var(--bm-bio-win, #4caf7d); }
.bm-bio-duel.is-loss .bm-bio-duel-outcome { color: var(--bad); }

.bm-bio-duel-upset {
  grid-column: 3;
  justify-self: end;
  padding: 0 0.4rem;
  border-radius: 999px;
  background: rgba(232, 135, 49, 0.18);
  font-size: 0.65rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.04em;
  white-space: nowrap;
  cursor: help;
}

/* With an upset badge present the outcome and the badge share the last column, so the row keeps
 * its three-column rhythm instead of the score sliding left on one line in ten. */
.bm-bio-duel.is-upset {
  grid-template-columns: minmax(0, 1fr) auto auto auto;
}

@media (max-width: 640px) {
  .bm-bio-tier {
    grid-template-columns: 1.4rem minmax(0, 1fr);
    gap: 0.5rem;
  }

  .bm-bio-tier-rank {
    width: 1.4rem;
    height: 1.4rem;
    font-size: 0.7rem;
  }

  .bm-bio-tier + .bm-bio-tier::before {
    left: 0.68rem;
  }

  .bm-bio-tier-cells {
    display: grid;
    grid-template-columns: minmax(0, 1fr);
  }

  .bm-bio-duel {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .bm-bio-duel.is-upset {
    grid-template-columns: minmax(0, 1fr) auto;
  }

  .bm-bio-duel-outcome,
  .bm-bio-duel-upset {
    grid-column: 2;
    justify-self: end;
  }
}

/* --- profile links on name cards (BM-540 round 12) --------------------
 *
 * A player's name is a link only when the server said this viewer may follow it (see
 * `playerNameHtml`), so the two states sit side by side constantly - "Kudu + Bokkie" where only one
 * of them has an account. The difference is therefore kept quiet: no button, no colour shift at
 * rest, just an underline on hover, so a cell of names does not read as a row of buttons and an
 * unlinked player does not look broken next to a linked one.
 */

.bm-bio-profile {
  color: inherit;
  text-decoration: none;
  border-radius: 4px;
}

.bm-bio-profile:hover,
.bm-bio-profile:focus-visible {
  text-decoration: underline;
  text-underline-offset: 2px;
  text-decoration-color: var(--accent);
}

.bm-bio-profile:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* Inside a ladder cell the whole cell is already clickable (it picks that side), so the member
 * links need to stay visibly separate targets rather than blending into the cell's own hit area. */
.bm-bio-node-sep,
.bm-bio-node-more {
  color: var(--muted);
  font-weight: 400;
}

.bm-bio-node a.bm-bio-profile:hover {
  text-decoration-thickness: 2px;
}

/* --- moment icons (BM-540 round 13) ----------------------------------
 *
 * The disc keeps carrying the group's COLOUR - that is what makes the timeline scannable as
 * arrival / combat / raid - and the icon inside it carries the moment. Sized in em so it tracks
 * the disc rather than being pinned to a pixel size: the same markup is used on the list row and
 * on the bigger hover card, and each one wants the icon proportional to its own disc.
 */

.bm-bio-glyph-icon {
  /* rem, not em: the disc's own font-size is 0.72rem (it was sized for a single letter), so an
   * em here would resolve to an 11px icon inside a 24px disc.
   *
   * 1.15rem in a 1.5rem disc is a ~77% fill. A single letter wanted the generous padding a
   * 63% fill gave it; a stroked icon just reads as small and timid at that size. */
  width: 1.15rem;
  height: 1.15rem;
  display: block;
}

/* --- cupboards & upkeep (BM-540 round 14) ----------------------------
 *
 * The facts row auto-fits, so it reflows from five across to two on a phone without a breakpoint
 * per count - the number of stats varies (peak upkeep and "longest standing" only appear when
 * there is something to say).
 */

.bm-bio-upkeep-stats {
  list-style: none;
  margin: 0 0 0.9rem;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(8.5rem, 1fr));
  gap: 0.5rem;
}

.bm-bio-upkeep-stat {
  display: grid;
  gap: 0.1rem;
  padding: 0.6rem 0.7rem;
  border-radius: 12px;
  border: 1px solid rgba(255, 255, 255, 0.07);
  background: rgba(255, 255, 255, 0.02);
}

.bm-bio-upkeep-stat-value {
  font-size: 1.25rem;
  font-weight: 800;
  line-height: 1.1;
  font-variant-numeric: tabular-nums;
}

.bm-bio-upkeep-stat-label {
  font-size: 0.78rem;
  font-weight: 600;
}

.bm-bio-upkeep-stat-note {
  font-size: 0.72rem;
  color: var(--muted);
}

.bm-bio-tcs {
  list-style: none;
  margin: 0.9rem 0 0;
  padding: 0;
  display: grid;
  gap: 0.35rem;
}

.bm-bio-tc {
  display: grid;
  grid-template-columns: 1.6rem minmax(0, 1fr) auto;
  align-items: center;
  gap: 0.6rem;
  padding: 0.45rem 0.65rem;
  border-radius: 10px;
  border-left: 3px solid var(--bm-bio-win, #4caf7d);
  background: rgba(255, 255, 255, 0.02);
}

/* A cupboard that was destroyed reads differently from one that stood - edge AND wording, never
 * colour alone (the row spells out "Destroyed" either way). */
.bm-bio-tc.is-lost {
  border-left-color: var(--bad);
}

.bm-bio-tc-index {
  display: grid;
  place-items: center;
  width: 1.6rem;
  height: 1.6rem;
  border-radius: 50%;
  border: 1px solid rgba(255, 255, 255, 0.12);
  font-size: 0.75rem;
  font-weight: 700;
  color: var(--muted);
}

.bm-bio-tc-body {
  display: grid;
  gap: 0.1rem;
  min-width: 0;
}

.bm-bio-tc-when {
  font-weight: 600;
  font-size: 0.85rem;
}

.bm-bio-tc-state {
  font-size: 0.75rem;
  color: var(--muted);
}

.bm-bio-tc-grid {
  padding: 0.1rem 0.45rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(0, 0, 0, 0.3);
  font-size: 0.72rem;
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

@media (max-width: 640px) {
  .bm-bio-tc {
    grid-template-columns: 1.4rem minmax(0, 1fr) auto;
    gap: 0.45rem;
  }
}

/* --- upkeep resource switch (BM-540 round 14) ------------------------ */

.bm-bio-upkeep-tabs {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem;
  margin: 0 0 0.5rem;
}

.bm-bio-upkeep-tab {
  padding: 0.2rem 0.6rem;
  border-radius: 999px;
  border: 1px solid rgba(255, 255, 255, 0.12);
  background: rgba(0, 0, 0, 0.3);
  color: var(--muted);
  font: inherit;
  font-size: 0.75rem;
  font-weight: 600;
  cursor: pointer;
}

.bm-bio-upkeep-tab:hover {
  border-color: rgba(255, 255, 255, 0.22);
  color: var(--text);
}

.bm-bio-upkeep-tab.is-active {
  border-color: var(--bm-warm-border, rgba(232, 135, 49, 0.35));
  background: rgba(232, 135, 49, 0.16);
  color: var(--text);
}

.bm-bio-upkeep-tab:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 2px;
}

/* --- top block: synopsis + honours beside the facts (BM-540 round 15) ---
 *
 * The facts panel is a column of small tiles, so it reads better in a narrow column next to the
 * prose than stretched across the full width. The synopsis and the honours stack under each other
 * on the left. One breakpoint: below 900px the dossier is one column anyway, and the same value is
 * already used by .bm-bio-layout, so the two collapse together rather than at different widths.
 */

.bm-bio-top {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 21rem);
  gap: 0.95rem;
  /* stretch, not start: the two columns are rarely the same natural height, and a short column
   * left them ending at different places down the page. Both columns now take the height of the
   * taller one and their LAST panel absorbs the difference, so the bottoms line up. */
  align-items: stretch;
}

.bm-bio-top-main,
.bm-bio-top-side {
  display: grid;
  gap: 0.95rem;
  min-width: 0;
}

/* The last row is the one that grows: the synopsis keeps its natural height and the honours panel
 * takes up the slack, rather than the prose stretching and leaving its text floating. */
.bm-bio-top-main {
  grid-template-rows: auto 1fr;
}

.bm-bio-top-side {
  grid-template-rows: 1fr;
}

/* Below 1100 the top STACKS.
 *
 * It used to split at 900, which technically fit but left the prose column around 200px wide once
 * the shelf and the 21rem facts column had taken their share - the synopsis wrapped to two or
 * three words a line. The facts panel needs ~21rem and the prose needs ~24rem to read properly, so
 * the split only earns its keep once both fit beside the shelf. */
@media (max-width: 1100px) {
  .bm-bio-top {
    grid-template-columns: minmax(0, 1fr);
  }

  /* Stacked, so there is no second column to line up with - let every panel be its own height
   * again rather than stretching one of them for no reason. */
  .bm-bio-top-main,
  .bm-bio-top-side {
    grid-template-rows: none;
  }

  .bm-bio-top-side .bm-bio-facts {
    align-content: start;
  }
}



/* --- the facts panel fills its column (BM-540 round 16) --------------
 *
 * Whichever top column is shorter gets stretched to match the taller one, and the facts panel is
 * usually that one. Rather than leaving the slack as dead space under the death tiles, the rows
 * distribute down the full height: same content, same order, no gap. Paired with the honours
 * reflow above, the slack being shared out is small, so the rows stay close to their natural
 * rhythm instead of drifting apart.
 */

.bm-bio-top-side .bm-bio-section {
  display: flex;
  flex-direction: column;
}

.bm-bio-top-side .bm-bio-facts {
  flex: 1 1 auto;
  align-content: space-between;
}
