/* Grid page (bio.html) — 7 members, not 8, so this is a plain 4-column grid that flows
   naturally (4 tiles, then 3) rather than forcing an invented 8th placeholder tile. Was gapless
   by request originally; now carries a small gap instead — the Tilted Card hover effect on each
   tile (js/tilted-card.js) needs a little breathing room to read as one tile lifting/tilting on
   its own, which just looks broken running flush edge-to-edge into its neighbour. */
.bio-grid {
  display: grid;
  grid-template-columns: repeat(4, 1fr);
  gap: var(--space-3);
}

.bio-grid__tile {
  display: block;
  color: inherit;
}

/* Row 2 (Gordon/Matt/Justin) starts at column 2, not column 1 — right-aligns the shorter second
   row so the one non-photo slot lands under Kele (column 1) instead of trailing after Justin.
   Relies on DOM order, not an explicit grid-row/column on Gordon's own tile: .bio-grid__wordmark
   (below) is the actual item occupying column 1 — placed via plain auto-flow by simply sitting
   right before Gordon's tile in the markup, so this rule only needs to bump Gordon (and, via
   auto-flow after it, Matt/Justin) one column to the right of wherever auto-placement would
   otherwise put them. */
.bio-grid__tile--row2-start {
  grid-column: 2;
}

/* Fills what would otherwise be a blank cell (there are 7 members, not 8, so the last row is
   short by one) — blank at rest (white background, matching the page's own), and only shows
   text while a sibling photo tile is being hovered: js/bio.js swaps the two empty [data-line]
   spans (nested in .bio-grid__wordmark-inner) to that tile's first/last name on mouseenter and
   clears them again on mouseleave, reading each tile's own data-first/data-last attributes
   rather than hardcoding anything here. user-select:none since there's nothing meaningful to
   select — the text only exists transiently while hovering something else, and standard
   drag-to-select over a hover effect just looks broken rather than useful. aspect-ratio:1 is
   redundant at 4/3/2 columns (it already ends up square there purely from sharing a row with
   Gordon/Matt/Justin's own square photo-wraps, at equal column widths) but load-bearing at the
   1-column breakpoint below — confirmed directly that without it, this cell sits alone on its
   own grid row with no square sibling to inherit a height from, collapsing to a thin sliver
   sized by its own (often-empty) text content instead of matching every other tile.
   The outer element centres its one child *as a block* on both axes (a plain 2D flex-centre);
   the actual left-alignment lives one level down, on .bio-grid__wordmark-inner, so that "Kele"
   (short) and "Okereke" (long) share a common left edge with each other rather than each
   centring independently around its own, shorter or longer, text width — which is what would
   happen if text-align:left were just applied straight on this outer element instead. */
.bio-grid__wordmark {
  aspect-ratio: 1;
  display: flex;
  align-items: center;
  justify-content: center;
  padding: var(--space-4);
  box-sizing: border-box;
  background: var(--color-bg);
  user-select: none;
}

/* opacity starts at 0 and is toggled from js/bio.js, not left implicit at the browser default of
   1 — text content changes can't themselves be transitioned, so a same-tile-to-next-tile swap
   needs an explicit fade-out/fade-in around the actual textContent change (see initBioGrid) to
   read as a quick cross-fade rather than the old instant blink from one name straight to another. */
.bio-grid__wordmark-inner {
  display: flex;
  flex-direction: column;
  font-family: var(--font-body);
  font-size: clamp(2rem, 3.4vw, 2.9rem);
  line-height: 1.2;
  text-align: left;
  opacity: 0;
  transition: opacity 0.15s ease;
}

/* Third line, current-vs-former members only (js/bio.js only ever fills this from a tile's
   data-former attribute — Gordon/Matt/Justin — leaving it empty, and collapsed to 0 height via
   :empty, for the four current members). Sized well below the name lines' own clamp rather than
   sharing .bio-grid__wordmark-inner's font-size — reads as a caption under the name, not a third
   name of equal weight. */
.bio-grid__wordmark-line--status {
  font-size: clamp(0.85rem, 1.1vw, 1rem);
  color: var(--color-text-muted);
  margin-top: var(--space-2);
}

.bio-grid__wordmark-line--status:empty {
  margin-top: 0;
}

/* Touch-only, per request: .bio-grid__wordmark above only ever shows a name on hover
   (js/bio.js's mouseenter/mouseleave), so on a device with no hover it just sits there
   permanently blank — hidden here, with each tile showing its own name directly on its own
   corner instead. A pure-CSS label via attr() rather than a js/bio.js change — data-first/
   data-last already exist on every tile (js/bio.js already reads them for the hover reveal), so
   this needed no new data, just a different way of displaying what's already there. Deliberately
   left the "(former member)" status (data-former) out of this compact label — showing the raw
   string "true" via attr() isn't useful, and a name-only label matches the site's other touch
   captions (Home, Lyrics, Music) staying similarly brief.
   Placed *after* every unconditional .bio-grid__wordmark rule above, not just visually grouped
   near them — the same real bug as the earlier sitewide nav fix, not just tidiness: this and the
   base .bio-grid__wordmark rule both set `display` at equal specificity, and a plain selector
   placed later in the file wins that tie over an earlier media-query one regardless of which
   condition is actually true — the first attempt (placed above the base rule) was silently
   overridden right back to the base rule's own `display: flex` on any real touch device. */
@media (hover: none) {
  .bio-grid__wordmark {
    display: none;
  }

  .bio-grid__tile {
    position: relative;
  }

  .bio-grid__tile::after {
    content: attr(data-first) " " attr(data-last);
    position: absolute;
    left: 12px;
    bottom: 12px;
    right: 12px;
    color: #fff;
    font-family: var(--font-body);
    font-size: 0.85rem;
    text-shadow: 0 1px 5px rgba(0, 0, 0, 0.7);
    pointer-events: none;
  }
}

.bio-grid__photo-wrap {
  aspect-ratio: 1;
  overflow: hidden;
  border-radius: 12px;
  background: var(--color-border);
}

.bio-grid__photo {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  /* No CSS transition on transform — js/tilted-card.js's own requestAnimationFrame loop already
     eases every frame toward its target; a CSS transition on top of that would just be a second,
     independent smoothing system fighting the first one's constant re-targeting (confirmed
     directly — that combination is what was causing the "conflicting animations" look). */
}

@media (max-width: 900px) {
  .bio-grid {
    grid-template-columns: repeat(3, 1fr);
  }

  /* The column-2 placement above is tuned for the 4-column layout specifically (so the gap
     lands under Kele) — at 3 columns that same fixed column index would land somewhere
     different and no longer reflect "one blank slot under the first tile," so it's dropped
     back to normal auto-flow here instead of carrying a now-meaningless offset. */
  .bio-grid__tile--row2-start {
    grid-column: auto;
  }
}

/* Stays at 2 columns all the way down to the narrowest real phone widths — per request, "always
   2 columns minimum." A narrower @media(max-width:420px) rule used to collapse this to a single
   column below that width (each photo was getting uncomfortably small at 2-per-row down there);
   removed rather than kept as a size trade-off, since 2 columns minimum is now the explicit ask
   regardless of how small that makes each tile. */
@media (max-width: 600px) {
  .bio-grid {
    grid-template-columns: repeat(2, 1fr);
  }
}

/* Detail pages (bio/<slug>.html) — plain white/black/Gill Sans MT, which is just this site's
   own sitewide default (--color-bg/--color-text/--font-body), so nothing below overrides any
   of that; these rules are purely about the photo/text layout. */
.bio-page__back {
  margin-bottom: var(--space-5);
}

.bio-page__back a {
  color: var(--color-text-muted);
  transition: color 0.4s ease 0.1s;
}

.bio-page__back a:hover {
  color: var(--color-accent);
  transition-duration: 0.2s;
  transition-delay: 0s;
}

.bio-page h1 {
  margin-bottom: var(--space-5);
  /* Explicit override, not an inherited default — the sitewide h1 rule went grey, but this page's
     own spec was always plain black text regardless of what the top-level section titles do. */
  color: var(--color-text);
}

/* float + shape-outside is the only combination that makes surrounding text genuinely wrap
   around a shape rather than a rectangular box — and it only works on an element still in
   normal flow, which is what ruled out position:fixed/absolute for "always visible" (see
   PROGRESS.md's own note on this page for the fuller reasoning). shape-outside's url() must
   point at the same *trimmed* PNG as the visible src (trimmed to the actual opaque content's
   bounding box, not the original full canvas — the source photos have ~70% transparent padding
   around the figure, which would otherwise sit inside the img's own box doing nothing useful for
   the wrap and throwing off sizing consistency between members with different amounts of
   padding). The actual per-member URL is supplied via the --bio-photo-url custom property set
   inline on each page's own <img>, so this one rule serves all seven without repeating itself.
   Sized at 70% of the original clamp (320/65vh/820 → 224/45.5vh/574) — the first version read as
   too large relative to the text. margin-left and shape-margin are both deliberately tight
   (var(--space-3)/var(--space-2) rather than var(--space-5)/var(--space-4)) — the original wider
   gap pushed the shape far enough from the text column that on a wide viewport the paragraphs
   (which fill the container up to main's own capped width) never actually reached the shape's
   boundary, so no visible wrapping ever kicked in; tightening the gap is what makes the wrap
   engage at every width instead of only on narrow ones. */
.bio-page__photo {
  float: right;
  height: clamp(224px, 45.5vh, 574px);
  width: auto;
  margin: 0 0 var(--space-4) var(--space-3);
  shape-outside: var(--bio-photo-url);
  shape-image-threshold: 0.5;
  shape-margin: var(--space-2);
}

@media (max-width: 700px) {
  /* Still floats and still wraps (per the user's own request that this keep working as the
     window shrinks) — just smaller, so the remaining text column doesn't collapse to an
     unreadably narrow sliver on a phone-width screen. */
  .bio-page__photo {
    height: clamp(154px, 29.4vh, 294px);
  }
}

/* Matt's own photo (him seated) reads noticeably larger than the others at the same height —
   a seated pose fills more of its own bounding box width-for-width than a standing figure does,
   so the same height clamp as everyone else made his particular photo look disproportionately
   big. Scaled to 60% of the original (pre-70%-reduction) clamp instead of the shared 70%. */
.bio-page--matt .bio-page__photo {
  height: clamp(192px, 39vh, 492px);
}

@media (max-width: 700px) {
  .bio-page--matt .bio-page__photo {
    height: clamp(132px, 25.2vh, 252px);
  }
}

.bio-page__body p {
  margin: 0 0 var(--space-4);
  line-height: 1.6;
}

.bio-page__links {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--space-3);
  padding-top: var(--space-2);
  border-top: 1px solid var(--color-border);
}

/* Instagram then website, stacked — not side by side, per request. */
.bio-page__socials {
  display: flex;
  flex-direction: column;
  gap: var(--space-1);
}

/* DOB then source, stacked and right-aligned as their own column on the opposite side of the row
   from the socials — margin-left:auto (rather than relying on the row's own justify-content
   alone) keeps this column pinned right even when socials is the only other child, or absent. */
.bio-page__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: var(--space-1);
  margin-left: auto;
}

/* Shared by the Instagram link and the website link, so both sit at the same size/weight with
   their own icon (globe for website, matching the Instagram glyph's own treatment) rather than
   one of them being plain underlined text. */
.bio-page__social {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  color: var(--color-text);
  font-family: var(--font-body);
  font-size: 0.9rem;
  text-decoration: none;
  transition: color 0.4s ease 0.1s;
}

.bio-page__social:hover {
  color: var(--color-accent);
  transition-duration: 0.2s;
  transition-delay: 0s;
}

.bio-page__social svg {
  width: 1.1em;
  height: 1.1em;
  flex-shrink: 0;
}

/* One per non-empty line of the "Notes" field (previously two fixed elements, DOB and source).
   Specificity needs to beat .bio-page__body p's own margin shorthand (these are still <p> tags,
   inside .bio-page__body) — qualifying with the .bio-page__meta parent rather than reaching for
   !important. */
.bio-page__meta .bio-page__note {
  margin: 0;
  color: var(--color-text-muted);
  font-family: var(--font-body-light);
  font-size: 0.85rem;
  text-align: right;
}
