/* /booster/<npub> — deltas only.
 *
 * The page links show-page.css, feed-cards.css, boosts-thread.css,
 * boost-actions.css and episode-page.css and reuses their classes verbatim: the
 * hero, the .ep-drawer lids, the .cs-* rows, the .ce-scroll container, the stat
 * tiles, the community-style boost list. What is here is only what a page about
 * a PERSON needs that a page about a show or an episode does not — the profile
 * card, the banner, the round avatar and the contact chips.
 *
 * Same arrangement as episode-page.css, and the same reasoning: the .show-*
 * class names stay on identical boxes rather than being mirrored into a parallel
 * .booster-* set, which would be a rename with no meaning behind it. See the
 * naming note in CLAUDE.md.
 *
 * Everything reads --brand / --ink / --surface / --border off theme.css. There
 * are no per-feed accents here: those exist only on index.html.
 */

/* ── The profile card ──────────────────────────────────────────────────────
 *
 * One bordered surface holding the banner, the avatar, the identity and the
 * actions — this page's counterpart to the .ep-card on /episode/<guid>, and for
 * the same reason: everything about the PERSON is one object, while the stat
 * tiles below it are about their boosts and sit on the page as they do on both
 * other pages.
 *
 * overflow:hidden is what lets the banner bleed to the card's rounded top
 * corners without a second border-radius that would have to track this one. */
.bs-card {
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 16px;
  overflow: hidden;
}

/* ── The banner, and the avatar that hangs off it ──────────────────────────
 *
 * ⚠️ NOTHING BUT THE AVATAR MAY OVERLAP THE BANNER. The first version pulled
 * the whole identity row up with a negative margin-top, and because that row
 * held the avatar and the text side by side, it dragged the display name onto
 * the banner. A name over a banner is unreadable and cannot be fixed with a
 * scrim or a text-shadow, because the image underneath is a stranger's and can
 * be any colour at all.
 *
 * The arrangement below is the one every Nostr client uses, read off MyNostr's
 * ProfileModule (`~/Desktop/Files/nostr/mynostr`): a fixed-height banner, an
 * avatar absolutely positioned so it hangs exactly HALF below the banner's
 * bottom edge, and a separate identity block padded to clear it. Twitter and
 * Bluesky are the same. Three numbers move together and are the whole layout —
 * the avatar's size, its negative `bottom` (always half that size), and
 * `.bs-ident`'s `padding-top` (that half, plus breathing room).
 *
 * A FIXED HEIGHT, not an aspect-ratio. The previous rule set `aspect-ratio: 4/1`
 * and then clamped it between a min and a max height, which is three rules
 * disagreeing about the same box: inside the clamps the strip grew with the
 * card, outside them the ratio was simply ignored. Every client sizes this
 * absolutely, `object-fit: cover` crops to whatever the box is, and a banner is
 * decoration whose height nothing else depends on.
 *
 * ⚠️ NO `overflow: hidden` HERE — it would clip the avatar that deliberately
 * escapes the bottom edge. The card's own overflow rounds the banner's top
 * corners, and the image below is sized to the box exactly, so there is nothing
 * else to clip.
 *
 * 42.5% of stored profiles carry a banner. The blank variant is not a missing
 * state to apologise for: it is the brand ramp at low opacity, giving the card a
 * lid and the avatar something to sit against. booster-page.js swaps a DEAD
 * banner to the same variant, so a broken hotlink and an absent field are
 * indistinguishable. */
.bs-banner {
  position: relative;
  height: 160px;
  background: var(--tint, rgba(0, 175, 240, 0.08));
}
.bs-banner img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.bs-banner--blank {
  background: linear-gradient(120deg,
    color-mix(in srgb, var(--brand) 16%, transparent),
    color-mix(in srgb, var(--brand-d) 10%, transparent));
}

.bs-avatar {
  position: absolute;
  left: 1.5rem;
  /* Exactly half of the height below, so the circle straddles the edge. */
  bottom: -56px;
  width: 112px;
  height: 112px;
  border-radius: 50%;
  overflow: hidden;
  background: var(--cream-d, rgba(0, 0, 0, 0.06));
  /* The ring is what separates a dark avatar from a dark banner, and it takes
     the CARD's background so the avatar reads as sitting on the card rather
     than on the image. Brand would compete with the podium rings on the
     community walls, where a brand ring means "top five". */
  box-shadow: 0 0 0 4px var(--surface);
}
.bs-avatar img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.bs-avatar.is-blank {
  /* No glyph. A silhouette placeholder for a person reads as a broken image;
     a plain tinted circle reads as an avatar nobody set. */
  background: linear-gradient(140deg,
    color-mix(in srgb, var(--brand) 22%, transparent),
    color-mix(in srgb, var(--brand-d) 14%, transparent));
}

/* The identity block. Full width, under the banner, never over it — see the
   warning above. `padding-top` is the avatar's overhang (56px) plus room to
   breathe, and it moves whenever the avatar's size does. */
.bs-ident {
  padding: 4.25rem 1.5rem 1.5rem;
}

/* nip05 sits directly under the name, above the boost dates, because it is part
   of the identity rather than part of the record. The check is decorative and
   deliberately NOT a verification claim: nothing here re-resolves the
   .well-known, so it marks that the profile published one. */
.bs-nip05 {
  margin: 0.15rem 0 0;
  font-size: 0.85rem;
  color: var(--brand-d);
  overflow-wrap: anywhere;
}
.bs-nip05 span { opacity: 0.75; }

/* ── The bio ───────────────────────────────────────────────────────────────
 *
 * ⚠️ TWO LINES, WHERE /show CLAMPS TO THREE. Measured over the 1,050 profiles
 * carrying an `about`: median 60 characters, p75 125, p90 220, and 16.6% run
 * past 160 — so two clamped lines covers roughly 84% of them outright and a
 * three-line clamp would ship a "More" control that five profiles in six never
 * need. Everything else about the control is show-desc.js's, unchanged.
 *
 * The selector has to out-specify `.show-desc.is-clamped .show-desc-body`, which
 * is why it names both classes. */
.show-desc.bs-bio.is-clamped .show-desc-body {
  -webkit-line-clamp: 2;
  line-clamp: 2;
}
.bs-bio { margin-top: 0.9rem; }
/* A bio is a stranger's free text and routinely carries a pasted URL, which does
   not wrap on its own and would otherwise push the card sideways. */
.bs-bio .show-desc-body { overflow-wrap: anywhere; }
.bs-bio a { color: var(--brand-d); font-weight: 600; text-decoration: none; }
.bs-bio a:hover { color: var(--brand-dd); text-decoration: underline; }

/* ── A `nostr:` mention inside a bio ───────────────────────────────────────
 *
 * A small face and a display name, which is how every Nostr client renders one.
 * ⚠️ NOT A LINK, and not styled as one — see renderBioText in
 * functions/_shared/detail-page.js. /booster/<npub> only exists for people who
 * have BOOSTED, and a mentioned npub need never have done; rather than link some
 * and 404 the rest, none of them link, so this must not borrow the brand colour
 * that says "clickable" everywhere else on the page.
 *
 * inline-flex with a baseline alignment, so the chip sits IN the sentence rather
 * than beside it. Its height is deliberately close to the line box: the bio is
 * clamped to two lines, and a chip taller than the leading would make one line
 * of a two-line paragraph visibly taller than the other. */
/* ── A mention inside the bio ──────────────────────────────────────────────
 *
 * ⚠️ INLINE TEXT, NOT A PILL. It was a rounded chip with a tinted background,
 * which is the right treatment for a mention that stands alone in a toolbar and
 * the wrong one inside a sentence: a bio runs two or three lines and a pill in
 * the middle of it reads as the bio's main content, which is exactly what the
 * note over renderBioText was worried about when it argued the chips shouldn't
 * link. Setting it as ordinary link-coloured text answers that objection rather
 * than working around it.
 *
 * The avatar stays because it is what makes a mention read as a PERSON at a
 * glance, but it is sized to the text (1em, so it tracks the font rather than
 * imposing a line height) and sits on the baseline beside the name. Nothing here
 * adds vertical space to the paragraph. */
.bs-mention {
  display: inline;
  font-weight: 600;
  color: var(--brand-d);
  /* The npub fallback is 15 characters of bech32 and must not be broken across
     a line at an arbitrary point the way the surrounding prose may be. */
  white-space: nowrap;
}
.bs-mention:hover .bs-mention-name,
.bs-mention:focus-visible .bs-mention-name { text-decoration: underline; }
a.bs-mention { text-decoration: none; }

/* 1em, not 1.15: the avatar must not be taller than the line it sits in, or the
   paragraph's leading opens up wherever a mention falls. `vertical-align` rather
   than a flex row for the same reason — an inline-flex wrapper is a box, and a
   box in the middle of a sentence is the pill again. */
.bs-mention-pic {
  width: 1em;
  height: 1em;
  border-radius: 50%;
  object-fit: cover;
  background: var(--border);
  display: inline-block;
  vertical-align: -0.13em;
  margin-right: 0.22em;
}
/* The dot a mention we could not resolve keeps. Not a silhouette glyph: at 1em a
   glyph is illegible, and a flat tinted circle reads as an avatar nobody has
   rather than as a broken image. */
.bs-mention-pic.is-blank {
  background: linear-gradient(140deg,
    color-mix(in srgb, var(--brand) 30%, transparent),
    color-mix(in srgb, var(--brand-d) 18%, transparent));
}

/* ── Lightning address and website ─────────────────────────────────────────
 *
 * Chips rather than rows in a definition list: there are at most two, they are
 * both short, and both are things you take away from the page rather than read
 * on it. The lightning one is a copy control and NOT a payment control — see the
 * note over renderContact. */
.bs-contact {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin: 0.85rem 0 0;
}
/* The slot ships empty and `hidden` so booster-page.js has somewhere to put a
   lightning address only Primal knows about. Same trap as .cs-row[hidden]
   above: without this pair the author `display: flex` beats the user agent's
   [hidden] rule and an empty row of chips reserves space on the 35% of profiles
   that publish neither field. */
.bs-contact[hidden] { display: none; }
.bs-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  max-width: 100%;
  padding: 0.3rem 0.65rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--cream, rgba(0, 0, 0, 0.03));
  color: var(--ink);
  font: 600 0.82rem/1.3 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  text-decoration: none;
  cursor: pointer;
  /* A lightning address can be long and a website longer; the chip truncates
     rather than wrapping to two lines or widening the card. */
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bs-chip:hover,
.bs-chip:focus-visible {
  border-color: var(--brand);
  color: var(--brand-d);
}
.bs-chip-glyph { flex: none; opacity: 0.8; }

/* ── The Shows and Albums drawer ───────────────────────────────────────────
 *
 * The rows, the lid and the scroll container are all show-page.css's .cs-* and
 * .ep-list, unchanged. Two additions only.
 *
 * An UNIDENTIFIED show has no page, so its row is a <div> carrying .cs-link
 * rather than an <a>. It must not look clickable: no pointer, no hover, and the
 * title dimmed so the row reads as a record with a gap in it rather than as a
 * link that failed. 33% of the shows in the index are in this state, and they
 * are kept here (where the show page's community drawer drops them) because this
 * is one person's history and dropping a third of it would under-report what the
 * stat tile above already counted. */
/* ⚠️ WITHOUT THIS THE RANGE FILTER DOES NOTHING. `.cs-row` is `display: flex` in
   show-page.css, and an author `display` declaration beats the user-agent's
   `[hidden] { display: none }`, so setting el.hidden on a row leaves it on
   screen. The show page never needed the pair because its community drawer only
   ever re-orders rows; this page hides them, so it has to say so. `.cs-controls`
   two hundred lines up in that same file carries exactly this rule for exactly
   this reason. */
.cs-row[hidden] { display: none; }

.cs-link--dead {
  cursor: default;
  color: inherit;
}
.cs-link--dead:hover { background: none; }
.cs-link--dead .cs-title {
  color: var(--muted);
  font-style: italic;
}

/* ⚠️ RESTATED FROM episode-page.css, which scopes the same rule to
   .show-main--episode. The cards inside .ce-scroll are a grid of their own
   inside a .show-section, which has no list-reset to inherit from; without this
   the first card sits flush against the container's lid. Scoping it there rather
   than leaving it bare was right for that file — this page simply has to say so
   too, which is the same arrangement .pcast-sort has between index.html and
   show-page.css. */
.show-main--booster .pcast-list {
  margin-top: 0.4rem;
}

/* Painted when a range empties the list. Half of all boosters have sent nothing
   in the last 7 days, so this is an ordinary answer rather than an error, and it
   is worded as one. */
.cs-empty {
  margin: 0;
  padding: 1.25rem 1rem;
  text-align: center;
  color: var(--muted);
  font-size: 0.9rem;
}

/* ── Narrow screens ────────────────────────────────────────────────────────
 *
 * ⚠️ THE THREE AVATAR NUMBERS MOVE TOGETHER OR THE LAYOUT BREAKS: the size, the
 * negative `bottom` (always exactly half the size), and `.bs-ident`'s
 * `padding-top` (that half plus breathing room). Change one alone and the
 * avatar either detaches from the banner or overlaps the eyebrow beneath it.
 * 88px here, so bottom is -44px and the padding clears 44. */
@media (max-width: 640px) {
  .bs-banner { height: 116px; }
  .bs-avatar {
    left: 1rem;
    bottom: -44px;
    width: 88px;
    height: 88px;
    box-shadow: 0 0 0 3px var(--surface);
  }
  .bs-ident { padding: 3.4rem 1rem 1.15rem; }
  .bs-chip { font-size: 0.78rem; }
}
