/**
 * Section: Team roster
 *
 * Ported from `_mockups/our-team.html` `.tm-lead` / `.tm-grid` / `.tm-card`,
 * which the inventory names as canonical for this component. `about.html`
 * `.ab-tm` is the same card in a compact five-across variant without the
 * hover states, and folds into this one treatment.
 *
 * WHAT IS NOT IN THIS FILE, and where it is instead. `.chip` and `.chipset`
 * (the language pills), `.ph-badge` (the missing-name marker) and `.note` are
 * shared components in `components.css`; `.head`, `.section` and `.reveal`
 * are global. None of them is re-declared here. Two owners for one rule is
 * how the button system broke in Phase 0. The only shared class this file
 * touches is `.chip`, and only inside `.team-roster-langs`, where a pill that
 * is a label rather than a control must stop behaving like a control.
 *
 * CONTRAST, measured rather than eyeballed. The section sits on the default
 * mist ground and every card is paper, so both are given:
 *
 *   --gold-deep  #7e622e  on paper #ffffff   5.72:1   the role line
 *   --gold-deep  #7e622e  on mist  #f7f4ee   5.21:1   the eyebrow
 *   --gold-deep  #7e622e  through the .ph-badge stripe   5.17:1   missing names
 *   --ink        #14231f  on paper          16.28:1   names
 *   --ink-soft   #45524d  on paper           8.18:1   bios
 *   --ink-soft   #45524d  on mist            7.45:1   the two notes
 *   --pine       #16564b  on paper           8.51:1   the feature-card link
 *
 * The badge figure is one number and not two because `.ph-badge` pins its own
 * `background-color: var(--paper)` and inherits no ground, so it measures the
 * same on the card and on the band. 5.17 is the composite of --gold-tint-10
 * over paper computed in float; rounding that composite to a hex first reports
 * 5.18, which is the wrong way to err in a header that claims to be measured.
 *
 * All clear WCAG AA for normal text. Re-measure if any token moves. Nothing
 * here uses a tint token as a text colour: --gold and --gold-bright appear
 * only as borders, which is what AGENTS.md §1.7 requires.
 *
 * COLUMN COUNT IS DERIVED, NOT CHOSEN. `--pasia-team-cols` and
 * `--pasia-team-cols-md` are set inline by the renderer from how many people
 * are in the roster, capped at four and two. They are counts, not design
 * values, and they exist so the grid never asks the client a layout question.
 */

/* Scoped to `.head` on purpose, and this is not tidiness. The note sits inside
   the section head, where base.css already declares `.head p` at --text-lg. A
   bare `.team-roster-note` is one class against one class plus one element and
   loses, so the note rendered at lead size and the two paragraphs were
   indistinguishable. The mock-up solves the same collision with
   `p.tm-rosternote`, which only wins on source order; this wins on
   specificity, which does not depend on enqueue order. */
.head .team-roster-note {
  font-size: var(--text-sm);
  color: var(--ink-soft);
  margin-top: var(--s-3);
}

/* --- the shared member-card shell ----------------------------------------
 * Echoes `.hcard`'s hover language (lift plus gold keyline) so team cards and
 * helper cards read as relatives, without cloning the component: a team member
 * is not a helper and the two must never be confusable.
 * ---------------------------------------------------------------------- */
.team-roster-lead,
.team-roster-card {
  background: var(--paper);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  box-shadow: var(--shadow-s);
  overflow: hidden;
  transition: border-color var(--dur-normal) ease,
              box-shadow var(--dur-normal) ease,
              transform var(--dur-normal) var(--ease-out-soft);
}

.team-roster-lead:hover,
.team-roster-lead:focus-within,
.team-roster-card:hover,
.team-roster-card:focus-within {
  border-color: var(--gold);
  box-shadow: var(--shadow-m);
  transform: translateY(-3px);
}

/* Fixed-ratio portrait box. Portraits arrive at differing intrinsic widths, so
   the box carries the ratio and object-fit crops, which is what keeps the grid
   from jumping as each photograph loads. */
.team-roster-photo {
  position: relative;
  overflow: hidden;
}

.team-roster-photo img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: 50% 25%;
  transition: transform var(--dur-slow) var(--ease-out-soft);
}

/* The gold keyline inside the portrait, revealed on hover and on focus. */
.team-roster-photo::after {
  content: "";
  position: absolute;
  inset: var(--s-2);
  pointer-events: none;
  border: 1.5px solid var(--gold-bright);
  border-radius: var(--r-sm);
  opacity: 0;
  transition: opacity var(--dur-normal) ease;
}

.team-roster-lead:hover .team-roster-photo::after,
.team-roster-lead:focus-within .team-roster-photo::after,
.team-roster-card:hover .team-roster-photo::after,
.team-roster-card:focus-within .team-roster-photo::after {
  opacity: 1;
}

.team-roster-lead:hover .team-roster-photo img,
.team-roster-card:hover .team-roster-photo img {
  transform: scale(1.04);
}

/* --- the feature card ----------------------------------------------------- */
.team-roster-lead {
  display: grid;
  grid-template-columns: minmax(230px, 320px) minmax(0, 1fr);
  border-radius: var(--r-lg);
  margin-bottom: var(--s-4);
}

/* No portrait supplied, so there is no picture column to leave standing
   empty. Set by the renderer from the content, never by a field. */
.team-roster-lead--nophoto {
  grid-template-columns: minmax(0, 1fr);
}

/* A RATIO FLOOR, carried by an in-flow spacer rather than by the box.

   THE BUG. The picture column takes its height from the body beside it, and
   the body is short in exactly the state this build is in: a portrait, a role
   and a "Client to supply" badge, with no bio yet. Measured with no floor at
   all, at 1440: 320x159, a 2.01 letterbox that, cropped at
   `object-position: 50% 25%`, missed the face entirely and showed a window
   frame and the back of a chair. The mock-up has the same structure and never
   surfaces it because it always ships a long bio; on this build no client copy
   has arrived, so the degenerate case is the DEFAULT rather than the edge.

   WHY NOT `aspect-ratio` ON THE BOX, which is the obvious fix and was tried
   and measured first. A ratio plus a definite width gives the box a definite
   height, and a definite height beats `align-self: stretch`. So it is a CAP,
   not a floor: with a long bio the body measured 599px and the portrait stayed
   at 427, stopping short of the card's bottom and leaving a bare paper gap
   under the picture. That trades one wrong state for another.

   THE SPACER. `::before` is an ordinary in-flow block, so it takes the box's
   width and, through its own ratio, a height of 4/3 of it. That becomes the
   box's CONTENT height, which the grid row must be at least as tall as, while
   the box itself keeps `height: auto` and stretches freely past it whenever
   the body is taller. Floor, not cap, measured both ways. The img is
   absolutely positioned and covers the box at whatever height it settles at,
   and `::after` is the gold keyline, so the spacer is the only free
   pseudo-element and it costs nothing.

   Note the direction of travel against the horizontal mobile card lower down,
   which is the same property failing the other way: never pin the cross axis
   of a box that carries a ratio, and never let a ratio own an axis something
   else already owns. */
.team-roster-lead .team-roster-photo::before {
  content: "";
  display: block;
  aspect-ratio: 3 / 4;
}

.team-roster-lead .team-roster-photo img {
  position: absolute;
  inset: 0;
}

.team-roster-lead-body {
  padding: clamp(1.5rem, 3.5vw, 2.75rem);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--s-3);
}

.team-roster-lead-body h3 { font-size: var(--text-xl); }

.team-roster-role {
  font-family: var(--sans);
  font-size: var(--text-xs);
  font-weight: 700;
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--gold-deep);
}

.team-roster-bio {
  color: var(--ink-soft);
  font-size: var(--text-sm);
}

.team-roster-lead-body .team-roster-bio {
  font-size: var(--text-base);
  max-width: 52ch;
}

/* The link text is a client field and `.team-roster-lead` clips, so this fails
   the silent way: one 66-character unbreakable token ran 433px past the card's
   clip edge at 320 with the document unmoved. `anywhere` rather than
   `break-word`, measured: the link is inline and its containing block is sized
   from min-content at that width, so `break-word` had nothing narrower to
   break to and left the overrun at the identical 433px. */
.team-roster-lead-link {
  font-weight: 600;
  margin-top: var(--s-2);
  overflow-wrap: anywhere;
}

.team-roster-lead-link::after { content: " \2192"; }

.team-roster-lead-link:hover {
  text-decoration: underline;
  text-underline-offset: .2em;
}

/* --- the grid beneath ----------------------------------------------------- */
/* The track is always the width it would be at four across, and the row is
   left-aligned, rather than `1fr` tracks that share the whole container out
   between however many people there are.
   `1fr` looked correct at five members and wrong at two: with one card left
   under the feature card the single track took the full 1072px and the 3:4
   portrait became 1429px tall, a billboard beside a 320px feature portrait.
   Fixing the track instead keeps every card the same size at every roster
   length, which is the point of a roster. */
.team-roster-grid {
  display: grid;
  grid-template-columns: repeat(var(--pasia-team-cols, 4), calc((100% - 3 * var(--s-4)) / 4));
  justify-content: start;
  gap: var(--s-4);
}

.team-roster-card {
  display: flex;
  flex-direction: column;
}

.team-roster-card .team-roster-photo {
  aspect-ratio: 3 / 4;
  flex: none;
}

.team-roster-card-body {
  padding: var(--s-3) var(--s-3) var(--s-4);
  display: flex;
  flex-direction: column;
  gap: .5em;
  flex-grow: 1;
}

.team-roster-card-body h3 { font-size: var(--text-lg); }

.team-roster-card-body .team-roster-bio { flex-grow: 1; }

/* --- language pills ------------------------------------------------------
 * A list, not a row of spans: three chips with no announced grouping is three
 * loose words to a screen reader. The chips are labels rather than controls
 * here, so the shared `.chip` interaction affordances are switched off inside
 * this one scope. `.chip` itself is untouched.
 * ---------------------------------------------------------------------- */
.team-roster-langs {
  list-style: none;
  margin: 0;
  padding: var(--s-2) 0 0;
}

.team-roster-langs .chip { cursor: default; }

.team-roster-langs .chip:hover {
  border-color: var(--line);
  color: var(--ink-soft);
}

/* --- the notes and the button --------------------------------------------- */
.team-roster-pdpo { margin-top: var(--s-4); }

.team-roster-actions { margin-top: var(--s-4); }

/* --- breakpoints (canonical: 1024 / 860 / 620 / 560) ---------------------- */
@media (max-width: 1024px) {
  /* Two across, and the same fixed-track reasoning: a lone card takes a
     half-width track rather than the whole row. */
  .team-roster-grid {
    grid-template-columns: repeat(var(--pasia-team-cols-md, 2), calc((100% - var(--s-4)) / 2));
  }
}

@media (max-width: 860px) {
  .team-roster-lead { grid-template-columns: minmax(0, 1fr); }

  /* Landscape-ish crop once the feature card stacks, so the portrait does not
     take the whole first screen. Stacked, the picture is in a row of its own
     with nothing beside it to stretch against, so the spacer is the only
     height source and the ratio simply applies. */
  .team-roster-lead .team-roster-photo::before { aspect-ratio: 4 / 3; }
}

@media (max-width: 620px) {
  .team-roster-grid {
    grid-template-columns: minmax(0, 1fr);
    gap: var(--s-3);
  }

  .team-roster-card {
    display: grid;
    grid-template-columns: minmax(110px, 150px) minmax(0, 1fr);
  }

  .team-roster-card--nophoto {
    grid-template-columns: minmax(0, 1fr);
  }

  /* NO aspect-ratio on the horizontal card, and this is the one place the
     mock-up's own rule could not be ported as written.
     `aspect-ratio: 2/3` plus a stretched or 100% height makes the box derive
     its WIDTH from the ratio, which overflows the 150px picture track: at
     320px the portrait measured 268px inside a 280px card and painted over
     the text beside it. Here the track owns the width and the row owns the
     height, both definite, so a ratio has nothing left to decide and
     object-fit does the cropping. Measured clear at 620, 375 and 320. */
  .team-roster-card .team-roster-photo {
    aspect-ratio: auto;
    width: 100%;
    height: 100%;
  }
}

@media (max-width: 560px) {
  .team-roster-lead-body { padding: var(--s-4); }
}
