/* Show landing pages (/show/<podcast-guid>), rendered by
 * functions/show/[guid].js. See docs/show-pages-spec.md.
 *
 * Reads its colors as custom properties off :root and defines none of its own,
 * the same contract as nav.css / footer.css — theme.css is the supply. Prefer
 * --brand / --ink / --surface here; the --cream / --navy / --orange aliases are
 * historical and mean something other than their names (see CLAUDE.md).
 *
 * The supporters wall is the visual descendant of localbitcoiners'
 * supporters.html (.sup-* class names carried over deliberately), minus its
 * absolute sat tiers — see the note in the Function.
 */

.show-main {
  max-width: 60rem;
  margin: 0 auto;
  padding: 0 1.25rem 4rem;

  /* The sort control and the drawer chrome read --accent / --accent-d / --tint,
     which only index.html defines (one per feed, off body[data-active-feed]).
     A show page has no active feed, so it supplies the brand as the accent for
     the whole main.
     This used to sit on .cs-drawer, which was wrong: BOTH drawers mount a
     .cs-controls sort row, and the episode drawer does not carry that class, so
     its pill was reading an undefined --accent — every declaration that used one
     was dropped at computed-value time, which is why its "Sort: Latest Episode"
     rendered in body text rather than brand. */
  --accent: var(--brand);
  --accent-d: var(--brand-d);
  --tint: rgba(0, 175, 240, 0.1);
}

/* ── Back ─────────────────────────────────────────────────────────────── */

/* Above the hero, and load-bearing rather than decorative: manifest.webmanifest
   sets display:standalone, so an installed OnlyBoosts has no browser chrome and
   no back button of its own. A visitor walking show → community row → show →
   community row otherwise has no way to reverse at all.
   It ships as a real link to the feed, which is the right destination for
   someone who arrived on a shared link or a search result and has nothing to go
   back TO. show-page.js upgrades it to history.back() only when it can see that
   the previous document was ours. */
.show-back {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  margin-top: 1.25rem;
  padding: 0.32rem 0.75rem 0.32rem 0.6rem;
  border: 1px solid var(--border);
  border-radius: 999px;
  background: var(--surface);
  font: 600 0.78rem/1 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: var(--muted);
  text-decoration: none;
  transition: color 0.15s, border-color 0.15s, background 0.15s;
}
.show-back:hover { color: var(--brand-d); border-color: var(--brand); background: var(--tint); }
.show-back:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.show-back-arrow { font-size: 0.95rem; line-height: 1; }

/* ── Hero ─────────────────────────────────────────────────────────────── */

.show-hero {
  margin: 1rem 0 2.5rem;
}

.show-hero-inner {
  display: flex;
  gap: 1.5rem;
  align-items: center;
}

.show-art img,
.show-art-blank {
  width: 148px;
  height: 148px;
  border-radius: 14px;
  object-fit: cover;
  border: 1px solid var(--border);
  background: var(--cream-d);
}

.show-art-blank {
  display: grid;
  place-items: center;
  font-size: 3rem;
}

.show-ident { min-width: 0; }

.show-eyebrow {
  margin: 0 0 0.2rem;
  font: 700 0.7rem/1 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--brand-d);
}

.show-ident h1 {
  font-family: 'Playfair Display', Georgia, serif;
  font-size: clamp(1.5rem, 4vw, 2.3rem);
  line-height: 1.15;
  color: var(--ink);
  margin: 0;
  /* Show titles run long and arbitrary; wrap rather than overflow. */
  overflow-wrap: anywhere;
}

.show-sub {
  margin: 0.4rem 0 0;
  color: var(--muted);
  font-size: 0.92rem;
}

.show-actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem;
  margin-top: 0.9rem;
}

/* ── Buttons ──────────────────────────────────────────────────────────── */

.show-main .btn {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  padding: 0.5rem 0.95rem;
  border: 1px solid transparent;
  border-radius: 999px;
  font: 600 0.85rem/1 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  cursor: pointer;
  text-decoration: none;
  transition: background 0.15s, border-color 0.15s, color 0.15s;
}
.show-main .btn[hidden] { display: none; }

.show-main .btn-boost {
  background: var(--brand);
  color: #fff;
}
.show-main .btn-boost:hover { background: var(--brand-d); }

.show-main .btn-quiet {
  background: var(--surface);
  border-color: var(--border);
  color: var(--ink);
}
.show-main .btn-quiet:hover { border-color: var(--brand); color: var(--brand-d); }

.show-main .btn-sm {
  padding: 0.32rem 0.7rem;
  font-size: 0.78rem;
}

/* ── Stat row ─────────────────────────────────────────────────────────── */

.show-stats {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(7rem, 1fr));
  gap: 0.75rem;
  margin: 1.5rem 0 0;
  padding: 0;
}
/* ⚠️ LOAD-BEARING. Four rows of tiles ship, one per window on the Charts
   strip, and all but the selected one carry `hidden` (feed-rank.js,
   2026-09-03) — but `display: grid` above outranks the UA's `[hidden]`, so
   without this every row would paint at once. */
.show-stats[hidden] { display: none; }

.show-stat {
  position: relative;   /* the rank chip pins to this corner */
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 12px;
  padding: 0.8rem 0.6rem;
  text-align: center;
}

.show-stat dd {
  margin: 0;
  font: 700 1.5rem/1.1 'Playfair Display', Georgia, serif;
  color: var(--brand-d);
}

.show-stat dt {
  order: 2;
  margin-top: 0.25rem;
  font-size: 0.72rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--muted);
}

/* The credit line, between the show's name and its "last boosted" sub-line.
   Sits closer to the h1 than the sub-line does, because it belongs to the
   name rather than to the boost data. Absent entirely when the collector has
   no usable author, so nothing here reserves space. */
.show-credit {
  margin: 0.2rem 0 0;
  font-size: 0.92rem;
  color: var(--text);
  overflow-wrap: anywhere;
}
.show-credit-label {
  color: var(--muted);
  font-size: 0.78rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  margin-right: 0.35rem;
}

/* dt before dd in the DOM is the correct <dl> pairing; the orders put the
   number first, its label under it, and the rank line (a second dd, present on
   /show and /episode only) last. */
.show-stat {
  display: flex;
  flex-direction: column;
  justify-content: center;
}
.show-stat dd { order: 1; }

/* The subject's rank on the Global feed its card lives on, in the window the
   Charts strip has selected (all time by default), by the tile's own sort:
   "#4", or "T#118" where the place is shared. Rendered by
   functions/_shared/feed-rank.js and pinned to the tile's top corner.

   ⚠️ IT IS A CORNER CHIP, WHICH SEPARATES BY POSITION RATHER THAN BY WEIGHT,
   and that is the point. It shipped first as a third line in the flow and read
   as bolted on for a reason worth recording: `.show-stat dd` sets its type with
   the `font:` SHORTHAND, which carries the family, so a rule overriding only
   the size left the rank in Playfair at brand blue — the same face and colour
   as the figure above it, and larger than the label between them. Three lines,
   three type treatments, no hierarchy.

   Out of the centred column entirely, the chip stops competing with the figure
   at all, and it is the most compact of the treatments measured: it adds
   ~0.65rem of reserved line against ~1.6rem for a ruled footer band. The device
   is the sports stat card's — NBA.com and Sofascore pin a league position to a
   card corner — which is the same borrowing as the "T" itself.

   ⚠️ THE TILE MUST RESERVE THE LINE, or the chip overlaps the figure. At 375px
   a tile is ~107px and "3.0M" is ~40px centred, leaving ~33px of clearance
   where "T#2,274" needs ~38px. Letting it float works until the data gets wide,
   which is the worst kind of works. `.show-stat--ranked` is emitted by the
   renderer for exactly the tiles that carry one, so /booster's rankless tiles
   keep their own spacing. */
.show-stat--ranked { padding-top: 1.45rem; }

/* ⚠️ `.show-stat dd.show-stat-rank`, AND THE `dd` IS LOAD-BEARING. `.show-stat
   dd` above is a class plus an element — specificity (0,1,1) — where a lone
   `.show-stat-rank` is (0,1,0), so the tile's own dd rule WINS and its `font:`
   shorthand puts the chip back to 1.5rem Playfair in brand blue: the figure's
   exact type, inside a pill padded for 0.63rem text. It shipped that way once
   and read as the number spilling out of its pill.

   Specificity beats source order, so moving this rule later in the file does
   NOT fix it. The compound selector is (0,2,1) and cannot lose. The same trap
   is re-armed in the 640px block, where `.show-stat dd` sets a clamp() of its
   own — both selectors have to carry the `dd`. */
.show-stat dd.show-stat-rank {
  position: absolute;
  top: 0.4rem;
  right: 0.45rem;
  margin: 0;
  padding: 0.1rem 0.4rem;
  /* The Chart Positions strip's SELECTED cell wears --brand-tint, and these
     chips are that window's ranks — one shade, one claim (Reed's ask,
     2026-08-31, when the tinted cell was the all-time one; --cream-d until
     then). */
  background: var(--brand-tint);
  border-radius: 999px;
  /* Explicitly NOT Playfair. `.show-stat dd` sets the family through its `font:`
     shorthand, so the family has to be restated here or the chip inherits the
     figure's face — the original bug. */
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 0.63rem;
  font-weight: 700;
  line-height: 1.55;
  letter-spacing: 0.03em;
  color: var(--ink);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}

/* `.show-stats-cap` — "Rank on the all-time Shows feed; T marks a tie" — has
   no emitter since 2026-09-03: the tiles follow the window selected on the
   strip, so a caption naming the period would restate the highlighted cell
   (Reed's call). Its rules came out with it; `git log -S show-stats-cap` has
   them. */

/* ── The OnlyBoosts Charts strip ────────────────────────────────────────
   The subject's chart position in each boost-time window (Week · Month ·
   Year · All time), above the stat tiles; feed-rank.js renders it. It
   replaced the single all-time line on 2026-08-31 (option A of the windows
   design pass), and since 2026-09-03 it is ALSO THE TILES' WINDOW SELECTOR
   (Reed's ask): each cell picks which window's row of tiles is on screen,
   and the tiles' three chips are that window's component ranks.

   The vocabulary is the rank chip's — tabular sans, T#, --brand-d for a
   standing — at strip scale: hairline-separated cells on the card surface,
   the SELECTED cell (`aria-current`, all time on load) washed in
   --brand-tint, the same tint the chips below wear. A dash cell (outside the
   top 100, or no boosts in the window) drops to --muted and is a <button>
   rather than a link, so only real standings lead to a chart view — but it
   still selects its window, which is why the strip is no longer withheld
   when nothing charts. Tokens only, so dark mode needs nothing. */
.show-chart { margin: 0 0 0.7rem; }
.show-chart-label {
  display: inline-block;
  margin: 0 0 0.3rem;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 0.66rem;
  font-weight: 700;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--brand-d);
  text-decoration: none;
}
.show-chart-label:hover,
.show-chart-label:focus-visible { text-decoration: underline; text-underline-offset: 3px; }
.show-chart-strip {
  display: flex;
  align-items: stretch;
  border: 1px solid var(--border);
  border-radius: 10px;
  background: var(--surface);
  overflow: hidden;
}
.show-chart-cell {
  flex: 1 1 0;
  min-width: 0;
  text-align: center;
  padding: 0.4rem 0.3rem 0.45rem;
  border-left: 1px solid var(--border);
  text-decoration: none;
  /* The <button> cells reset to the <a> cells' look: same box, same face,
     no UA chrome. `font: inherit` alone is not enough for a button's
     colour. */
  font: inherit;
  color: inherit;
  background: transparent;
  border-top: 0; border-right: 0; border-bottom: 0;
  border-radius: 0;
  cursor: pointer;
}
.show-chart-cell:first-child { border-left: 0; }
.show-chart-cell:hover { background: var(--cream-d); }
.show-chart-cell:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }
.show-chart-cell[aria-current="true"] { background: var(--brand-tint); }
/* The selected window's label steps up to the standing's colour, so the strip
   reads as a control with a current value and not only as four figures. */
.show-chart-cell[aria-current="true"] .show-chart-win { color: var(--brand-d); }
.show-chart-win {
  display: block;
  font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  font-size: 0.56rem;
  font-weight: 700;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--muted);
  white-space: nowrap;
}
.show-chart-rank {
  display: block;
  margin-top: 0.1rem;
  font: 700 0.95rem/1.2 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: var(--brand-d);
  font-variant-numeric: tabular-nums;
  white-space: nowrap;
}
a.show-chart-cell:hover .show-chart-rank,
a.show-chart-cell:focus-visible .show-chart-rank { text-decoration: underline; text-underline-offset: 3px; }
.show-chart-cell--none .show-chart-rank { color: var(--muted); font-weight: 400; }

/* ── The show's description ───────────────────────────────────────────── */

/* The publisher's own summary, between the hero's identity block and the stat
   tiles. It is the only text on this page not derived from boost data, so it
   reads as prose rather than as another figure: full measure, body size,
   --text rather than --ink, and no card around it.

   IT IS CLAMPED ONLY ONCE JAVASCRIPT SAYS SO. The three-line limit lives on
   .is-clamped, which show-desc.js adds only when the text actually overflows
   it — so a two-line description never grows a "More" it does not need, and a
   reader with no JavaScript gets the whole description rather than three lines
   with no way to reach the rest. Same trade the "Show N more" toggles make. */
.show-desc {
  margin: 1.5rem 0 0;
  max-width: 52rem;
}

.show-desc-body {
  color: var(--text);
  font-size: 0.95rem;
  line-height: 1.62;
  /* A description carries publisher-typed URLs, which do not wrap on their
     own and would otherwise push the hero sideways. */
  overflow-wrap: anywhere;
}
.show-desc-body p { margin: 0; }
.show-desc-body p + p { margin-top: 0.7rem; }
.show-desc-body a { color: var(--brand-d); font-weight: 600; }
.show-desc-body a:hover { color: var(--brand-dd); }

/* The same -webkit-box clamp the podroll tiles' titles use. Three lines is the
   most the hero can spend and still leave the stat tiles on a phone's first
   screen, which is the whole design of this page. */
.show-desc.is-clamped .show-desc-body {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

/* A text control, not a .btn. The buttons above it are the page's actions —
   Boost, RSS, Share — and this only changes how much of one paragraph is on
   screen; styling it as a fourth pill would put it in their company. */
.show-desc-more {
  margin-top: 0.45rem;
  padding: 0;
  border: 0;
  background: none;
  font: 600 0.85rem/1 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: var(--brand-d);
  cursor: pointer;
}
.show-desc-more:hover,
.show-desc-more:focus-visible { text-decoration: underline; text-underline-offset: 3px; }

/* "Nostr Boost Stats" over the three tiles. Same Playfair treatment as a
   section h2, a step down in size — it labels three cards, not a section of the
   page. The linked half is the page's qualifier now (a caveat paragraph used to
   sit under the tiles instead), so it has to read as a link without shouting:
   brand color, underline on hover only. */
.show-stats-title {
  margin: 1.75rem 0 0.6rem;
  font-family: 'Playfair Display', Georgia, serif;
  font-size: clamp(1.05rem, 2.6vw, 1.3rem);
  font-weight: 700;
  color: var(--ink);
}
.show-stats-title a {
  color: var(--brand-d);
  text-decoration: none;
}
.show-stats-title a:hover,
.show-stats-title a:focus-visible { text-decoration: underline; text-underline-offset: 3px; }

.show-stats { margin-top: 0; }

/* ── Sections ─────────────────────────────────────────────────────────── */

/* scroll-margin-top because EVERY SECTION id HERE IS A SHAREABLE URL
   (/show/<guid>#podroll and the five others — see the contract note at the top
   of the Function). #top-nav is `position: sticky` at 64px, so an anchor without
   this scrolls the section's heading to y=0 and leaves it behind the bar: the
   reader lands on a page whose first visible line is the second line of the
   thing they followed a link to. 5rem clears the bar with a margin, matching
   `.prose > section` in page.css, which solved this for /about first.

   It belongs on the section rather than on :target so it applies to the
   JavaScript scrollIntoView path as well — scroll-margin is honoured by both.

   ⚠️ initHashSpy() in show-page.js READS THIS VALUE, via getComputedStyle, to
   place the line at which a section becomes the one the address bar names. The
   two have to be the same number: the spy's job is to hand back a link that
   returns you to where you copied it, and if its line sat anywhere other than
   where an anchor parks, following that link would land a section off. Change
   this and the spy follows; hardcode it there and they drift. */
.show-section {
  margin-top: 2.75rem;
  scroll-margin-top: 5rem;
}

/* A section with no heading of its own (the episode drawer — its summary
   already names it). Sits closer to what precedes it, since there's no
   heading to provide the separation. */
.show-section--bare { margin-top: 1.5rem; }

.show-section-head { margin-bottom: 1.1rem; }

.show-section h2 {
  font-family: 'Playfair Display', Georgia, serif;
  font-size: clamp(1.2rem, 3vw, 1.6rem);
  color: var(--ink);
  margin: 0;
}

/* CURRENTLY UNEMITTED, and kept deliberately. Every count badge came off the
   page's headings: the Nostr Community one read as a size claim about the show's
   audience where it counts who published a boost to Nostr, and neither podroll
   heading may carry one because both figures are bounded by which feeds the
   collector has read. Each section's sub-line says what bounds it instead. The
   rule stays because the shape is right for a figure that IS complete and
   unqualified, should the page ever have one. */
.show-count {
  display: inline-block;
  margin-left: 0.5rem;
  padding: 0.15rem 0.5rem;
  border-radius: 999px;
  background: var(--cream-d);
  color: var(--brand-d);
  font: 600 0.72rem/1.4 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  vertical-align: middle;
}

.show-section-sub {
  margin: 0.35rem 0 0;
  color: var(--muted);
  font-size: 0.9rem;
}

.show-empty {
  color: var(--muted);
  font-style: italic;
  margin: 0.5rem 0 0;
}

/* ── The Nostr Community wall ─────────────────────────────────────────── */
/*
   Modelled on localbitcoiners' supporters.html, which is the visual ancestor
   and reads better than the boxed version this replaced. The card has NO
   chrome: no border, no background, no rounded panel. A circular avatar, the
   name under it, the sats under that, centered — the avatars are the pattern,
   and a grid of bordered panels competed with them.

   No rank numerals either. The wall is ordered by sats, so position says
   standing; a number on every face turned a community into a scoreboard. The
   podium is marked by size and a brand ring instead.

   THE PODIUM WRAPS RATHER THAN COUNTING. It is a centered flex row, not a
   5-column grid, so how many fit per line follows the card width and the
   viewport: five across on desktop, three across on a phone with the last two
   centered beneath. That is what makes "top 5 on desktop, top 3 on mobile"
   possible at all — PODIUM is a server-side constant and CSS cannot move a
   card into the grid below, but it can let the row break where it must.
*/

/* ── The community wall ──
 * ⚠️ MOVED TO assets/css/supporter-wall.css AND LINKED SEPARATELY. The
 * homepage's Members tab renders the same wall, so the rules had to be reachable
 * from a page that does not load this file. Do not re-add them here: two copies
 * of one component's look is the drift the move exists to prevent. */

/* The "Show N more boosters" toggle under the wall. */
.show-more { margin-top: 1rem; }

/* ── The #boosts section ──────────────────────────────────────────────── */

/* THE SHELL. The band, the list and the "Load more" are one panel.
 *
 * ⚠️ WITHOUT THIS THE CONTROLS DID NOT READ AS THE LIST'S. A toolbar floating
 * above a run of separate cards says nothing about what it acts on — a reader
 * could take the range buttons for page-level chrome, or for something belonging
 * to the community wall above. Bordered, with the band as its lid, it is the
 * same box-with-a-lid idiom `.ep-drawer` uses everywhere else on these pages.
 *
 * --cream, the page's own background, rather than --surface. The rows inside are
 * WHITE cards with their own borders, so a white shell would erase their edges;
 * on cream they read as cards sitting in a well, which is what they are. The
 * band's --cream-d is one step darker again, so the stack reads lid / body. */
.bs-shell {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--cream);
  overflow: hidden;
}

/* The band is `.cs-controls` so it inherits the sort pill, the menu and the
 * layout the two drawers already use — one toolbar on this site, not two. Inside
 * the shell it is a LID, so it drops the top border `.cs-controls` carries for
 * sitting under a <summary> and takes a bottom one instead.
 *
 * Ships `hidden` from the server and revealed by boost-section.js; the
 * `[hidden]` rule is `.cs-controls`'s and applies unchanged. */
.bs-controls {
  border-top: 0;
  border-bottom: 1px solid var(--border);
  gap: 0.5rem 0.75rem;
}

/* The picked show, on /booster only — see boost-section.js#mountChip.
 *
 * It holds the LEFT end of the knobs' row, where it reads as a statement about
 * what is below rather than as another control: the three real controls are all
 * to its right, and the only thing on the chip that can be pressed is the ✕.
 *
 * Brand-tinted rather than bordered like the pills beside it, because it is not
 * one of them. A chip that matched the sort pill would invite a press expecting a
 * menu; this one says "a filter is on" and offers to take it off. */
.bs-chip {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  max-width: 100%;
  padding: 0.28rem 0.35rem 0.28rem 0.7rem;
  border: 1px solid var(--accent);
  border-radius: 999px;
  background: var(--tint);
  font: 600 0.78rem/1.3 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: var(--accent-d);
}
.bs-chip[hidden] { display: none; }
/* A show title runs to any length; the row must not be pushed sideways by one. */
.bs-chip-label {
  min-width: 0;
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.bs-chip-clear {
  flex-shrink: 0;
  display: grid;
  place-items: center;
  width: 1.15rem;
  height: 1.15rem;
  padding: 0;
  font: inherit;
  font-size: 0.7rem;
  line-height: 1;
  color: var(--accent-d);
  background: transparent;
  border: 0;
  border-radius: 999px;
  cursor: pointer;
}
.bs-chip-clear:hover { background: var(--accent); color: var(--white); }
.bs-chip-clear:focus-visible { outline: 2px solid var(--accent-d); outline-offset: 1px; }

/* ⚠️ THE RANGE AND THE SORT ARE ONE GROUP AT THE RIGHT END, matching the feeds.
 * `.cs-controls .pcast-sort` pins a lone sort to the far end with an auto
 * margin, which is right in the drawers — their band's left end holds a link out
 * to the show's catalogue — and wrong here, where it left the range stranded at
 * the opposite end of the band from the control it belongs beside. The auto
 * margin moves to the group, so the pair still holds the right end and the
 * search box takes the slack. Must come AFTER the .cs-controls rule it undoes;
 * the two have equal specificity. */
.bs-knobs {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  flex-shrink: 0;
  margin-left: auto;
}
.bs-knobs .pcast-sort { margin-left: 0; }

/* The search box, on a full row of its own.
 *
 * ⚠️ `flex-basis: 100%` RATHER THAN A WIDTH THAT WRAPS WHEN IT MUST. Four range
   segments, a sort pill, a chip and a search box do not share one line at any
   phone width, and relying on the wrap put the break at the mercy of how long the
   picked show's name happened to be — the band reflowed as the reader used it.
   A declared row is stable, and it gives the query the width it deserves: this is
   the control someone types a sentence fragment into, where the other three are
   one press each. */
.bs-search { flex: 1 1 100%; display: flex; }
.bs-search-input {
  width: 100%;
  padding: 0.4rem 0.7rem;
  font: inherit;
  font-size: 0.82rem;
  color: var(--text);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 999px;
}
.bs-search-input::placeholder { color: var(--muted); }
.bs-search-input:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 1px;
  border-color: var(--accent);
}

/* The 1W/1M/1Y/All segments.
 *
 * ⚠️ DEFINED HERE RATHER THAN INHERITED, because /show/<guid> does not link
 * feed-cards.css — it has never carried a range control before — while /episode
 * and /booster do. Scoped under `.cs-controls`, so on the two pages that load
 * both stylesheets this wins on specificity regardless of link order and all
 * three pages draw one control. Kept visually identical to the feeds';
 * feed-cards.css is the original. */
.cs-controls .pcast-range { display: flex; flex-shrink: 0; }
.cs-controls .pcast-range-btn {
  min-width: 2rem;
  padding: 0.5rem 0.35rem;
  font: inherit;
  font-size: 0.72rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--muted);
  background: transparent;
  border: none;
  border-radius: 6px;
  cursor: pointer;
  transition: background 0.12s, color 0.12s;
}
.cs-controls .pcast-range-btn:hover {
  color: var(--accent-d);
  background: var(--tint);
  background: color-mix(in srgb, var(--accent) 7%, transparent);
}
.cs-controls .pcast-range-btn.is-active {
  color: var(--accent-d);
  background: var(--tint);
  background: color-mix(in srgb, var(--accent) 14%, transparent);
}

/* A band mid-fetch — the FIRST gesture only, since the corpus request is
   memoised. The knobs must not look live while a second press would queue a
   rebuild against a corpus that has not arrived.

   ⚠️ THE KNOBS, NOT THE BAND. `pointer-events: none` over the whole band would
   take the search box out from under the reader's cursor at the exact moment
   they are typing into it, which is what triggers the fetch in the first place.
   The input stays live and its keystrokes queue behind the same promise. */
.bs-controls[aria-busy='true'] .bs-knobs { opacity: 0.55; pointer-events: none; }
/* The drawers' bands hold no search box, so there the whole band is the knobs. */
.cs-controls[aria-busy='true']:not(.bs-controls) { opacity: 0.55; pointer-events: none; }

/* "Load N more", INSIDE the shell — the same call `.ce-scroll` makes on the
   episode drawers, and for the same reason: a button below a panel reads as
   belonging to the page rather than to the list. Deliberately the same pill as
   the feeds' .pcast-showmore rather than a reference to it, for the same reason
   as the range buttons above: /show does not link the stylesheet that defines
   it. */
.bs-more { display: flex; justify-content: center; padding: 0 0.7rem 0.9rem; }
/* The slot ships empty on every page and stays empty on a list that holds
   everything already, so it must cost no space until something is put in it. */
.bs-more:empty { display: none; }
.bs-more-group { display: flex; flex-direction: column; align-items: center; gap: 0.4rem; }
.bs-more-count { font-size: 0.75rem; color: var(--muted); }
.bs-more-btn {
  padding: 0.5rem 1.4rem;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 700;
  color: var(--accent-d);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
  transition: background 0.12s, border-color 0.12s;
}
.bs-more-btn:hover { background: var(--tint); border-color: var(--accent); }
.bs-more-btn:disabled { opacity: 0.6; cursor: default; }

/* The truncation line, at the END of the list where it is an answer rather than
   a warning. Unreachable on today's data; see the note at each call site. */
.bs-note {
  margin: 0;
  font-size: 0.75rem;
  color: var(--muted);
  text-align: center;
  max-width: 34rem;
}

.boost-list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: flex;
  flex-direction: column;
  gap: 0.7rem;
}

/* ⚠️ SCOPED TO THE SHELL, so the homepage Boosts feed is untouched. The gap
   actually in force on these rows is boosts-thread.css's
   `.ob-boost-list { gap: 0.85rem }` — that file is linked after this one, so
   `.boost-list` alone loses the tie — and it is the SAME <ul> class the
   /#members cards use. Tightening it there too would be a change to a feed
   nobody asked about; this two-class selector outruns it on specificity and
   reaches only these three sections.

   The padding is the shell's inset: the rows are bordered cards, so they must
   not sit against its edge. */
.bs-shell .ob-boost-list {
  gap: 0.45rem;
  padding: 0.7rem;
}

.boost-row {
  display: flex;
  gap: 0.75rem;
  padding: 0.85rem 1rem;
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
}

.boost-body { min-width: 0; flex: 1; }

.boost-meta {
  display: flex;
  flex-wrap: wrap;
  align-items: baseline;
  gap: 0.5rem;
  margin: 0;
  font-size: 0.85rem;
}

.boost-who { font-weight: 700; color: var(--ink); overflow-wrap: anywhere; }
/* The name is an <a> to /booster/<npub> on /show and /episode, and a plain span
   on /booster itself, where every row belongs to the person whose page it is.
   It keeps --ink rather than taking link blue: a boost list is a list of people
   who sent sats, and a column of blue names would read as a list of links. The
   underline arrives on hover, which is where the affordance belongs. */
a.boost-who { text-decoration: none; }
a.boost-who:hover { color: var(--brand-d); text-decoration: underline; }
a.boost-who:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.boost-amt { color: var(--brand-d); font-weight: 700; }
.boost-when { color: var(--muted); font-size: 0.78rem; margin-left: auto; }

.boost-msg {
  margin: 0.4rem 0 0;
  color: var(--text);
  font-size: 0.92rem;
  line-height: 1.5;
  white-space: pre-wrap;
  overflow-wrap: anywhere;
}
/* nostr: mentions and bare URLs inside a boost message, rendered server-side
   by functions/show/[guid].js. The feeds get the identical treatment from
   boosts-thread.css, but that stylesheet is scoped to .note-body and this page
   does not load it, so the rule is restated here rather than the two surfaces
   being made to share a file neither of them fully needs. Keep them matching. */
.boost-msg a { color: var(--brand-d); text-decoration: none; overflow-wrap: anywhere; }
.boost-msg a:hover { text-decoration: underline; }
.boost-msg a.nostr-mention { font-weight: 600; }

.boost-target {
  margin: 0.4rem 0 0;
  font-size: 0.78rem;
  color: var(--muted);
  overflow-wrap: anywhere;
}

/* ── Episodes ─────────────────────────────────────────────────────────── */

.ep-drawer {
  border: 1px solid var(--border);
  border-radius: 12px;
  background: var(--surface);
  overflow: hidden;
}

/* The drawer's HEADER, and it has to read as one. Collapsed, this box is the
   only thing between the stat tiles and the community wall, and a line of small
   blue text on the same white as the body gave no sign there was a lid to lift.
   Three cues carry it now, in order of what a visitor actually notices:

   1. A sunken band, so the box has a header at all rather than being a bordered
      rectangle with a caption in it. This is also what stops the open drawer
      reading as two disconnected pieces.
   2. A SHOW / HIDE word at the right end. A word is what a first-time visitor
      reads; the chevron only confirms it. It is drawn in CSS off [open] and the
      span carrying it is aria-hidden, because <details> already announces its
      own expanded state and a screen reader does not need to hear both.
   3. A chevron that ROTATES rather than a text bullet that swaps glyphs. Drawn
      from two borders, so it does not depend on how a font renders ▸ or where
      that glyph sits on the baseline.

   The label is --ink, not brand. A whole heading in link blue reads as a link to
   somewhere else, which is the wrong promise for a control that expands in
   place; the brand sits on the chevron and the hint, which are the parts that
   ARE the control. Playfair at the .show-stats-title size because these two
   summaries stand in for the <h2> their sections don't have — every band down
   the page is then the same typeface. */
.ep-drawer > summary {
  display: flex;
  align-items: center;
  gap: 0.65rem;
  cursor: pointer;
  padding: 0.8rem 1rem;
  background: var(--cream-d);
  font-family: 'Playfair Display', Georgia, serif;
  font-size: clamp(1.05rem, 2.6vw, 1.3rem);
  font-weight: 700;
  line-height: 1.25;
  color: var(--ink);
  list-style: none;
  transition: background 0.15s, color 0.15s;
}
.ep-drawer > summary::-webkit-details-marker { display: none; }

/* Two borders and a rotation. The translate on each state is optical centering:
   the visible shape is a corner, so its mass sits off the box's own center. */
.ep-drawer > summary::before {
  content: "";
  flex: none;
  width: 0.46rem;
  height: 0.46rem;
  margin-left: 0.15rem;
  border-right: 2px solid var(--brand-d);
  border-bottom: 2px solid var(--brand-d);
  transform: translateX(-1px) rotate(-45deg);
  transition: transform 0.18s, border-color 0.15s;
}
.ep-drawer[open] > summary::before { transform: translateY(-2px) rotate(45deg); }

.drawer-hint {
  flex: none;
  margin-left: auto;
  font: 700 0.68rem/1 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  letter-spacing: 0.11em;
  text-transform: uppercase;
  color: var(--brand-d);
}
.ep-drawer > summary .drawer-hint::after { content: "Show"; }
.ep-drawer[open] > summary .drawer-hint::after { content: "Hide"; }

/* Hover is how a mouse user tests whether a thing is a thing, and the summary
   had none — cursor:pointer was the only feedback, and it arrives too late. */
.ep-drawer > summary:hover { background: rgba(0, 175, 240, 0.18); color: var(--brand-dd); }
.ep-drawer > summary:hover::before { border-color: var(--brand); }
.ep-drawer > summary:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

.ep-list {
  list-style: none;
  margin: 0;
  padding: 0;
  border-top: 1px solid var(--border);
  /* A show can carry 500 episode rows; cap the drawer and scroll inside it
     rather than making the page endless. */
  max-height: 32rem;
  overflow-y: auto;
}

.ep-row {
  display: flex;
  align-items: center;
  gap: 0.75rem;
  padding: 0.7rem 1rem;
  border-bottom: 1px solid var(--border);
}
.ep-row:last-child { border-bottom: 0; }

/* Same 44px rounded square as .cs-art in the drawer below, deliberately: the
   two lists sit in matching boxes on the same page and should read as one
   pattern. Episode art is near-universal in the index; a row without it falls
   back to the show's own art, and only then to the glyph. */
.ep-art {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 8px;
  object-fit: cover;
  background: var(--cream-d);
}
.ep-art--blank {
  display: grid;
  place-items: center;
  font-size: 1.1rem;
}

.ep-main { min-width: 0; flex: 1; }

.ep-title {
  margin: 0;
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
  overflow-wrap: anywhere;
}

/* The title links to /episode/<item-guid>. Inherits the row's own weight and
   colour rather than arriving in link blue: the whole row is one record and a
   blue title inside it would read as the only meaningful part, the same
   reasoning .cs-title follows in the community drawer below. The affordance is
   the underline on hover, which is where a reader looks for it once the pointer
   is already on the word. An untitled episode has no page and stays plain. */
.ep-title-link {
  color: inherit;
  text-decoration: none;
}
.ep-title-link:hover {
  color: var(--brand-d);
  text-decoration: underline;
  text-underline-offset: 3px;
}
.ep-title-link:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 2px;
  border-radius: 3px;
}


.ep-meta {
  margin: 0.2rem 0 0;
  font-size: 0.76rem;
  color: var(--muted);
}

/* ── The catalogue (podcast drawers, 2026-09-04) ──────────────────────────
   The drawer lists the show's whole Podcast Index catalogue, merged in the
   browser onto the rows the edge rendered. An INDEXED row — one with a page
   here and figures on its meta line — wears a brand outline; a row known only
   to Podcast Index carries date, duration and a Boost button and nothing
   else. The outline is drawn INSIDE the row's box (negative offset) so it
   costs no layout: rows keep their hairline separators and their height
   whether or not they qualify, and a re-sort that interleaves the two kinds
   moves nothing but the rows. See "The Catalogue" in docs/show-pages-spec.md. */
.ep-row--indexed {
  outline: 1.5px solid var(--brand);
  outline-offset: -5px;
  border-radius: 10px;
}

/* The catalogue's one line of its own: "Loading episodes…", or why the list
   is shorter than the show. Sits under the list, in the meta's voice. */
.ep-status {
  margin: 0;
  padding: 0.55rem 1rem;
  font-size: 0.76rem;
  color: var(--muted);
  border-top: 1px solid var(--border);
}
.ep-status[hidden] { display: none; }

/* ── Other shows this community boosts ────────────────────────────────── */

/* Reuses .ep-drawer / .ep-list wholesale — same box, same summary affordance,
   same capped-and-scrolling body. Only the row differs, because these rows are
   links to other show pages where an episode row is not. */

/* (The --accent supply that used to sit here is on .show-main now — both
   drawers mount a .cs-controls, and only one of them had this class.) */

/* The control band, shared by BOTH drawers on this page — the community one and
   the episode one above it. No range control in either; see the note in the
   Function.

   They differ in one way. The community band holds nothing but a sort, so it
   ships hidden and JavaScript reveals it; the episode band also holds a link to
   the show's full catalogue, which needs no JavaScript, so it ships visible and
   only the sort is conditional.

   --cream-d, NOT --cream. It was the page background, which meant the toolbar
   band read as a gap punched through the card to the page behind it: the drawer
   looked severed at the sort row rather than open. Matching the summary's band
   makes the open drawer a header / toolbar / list stack, one component, and it
   gives the white sort pill a surface to sit on that it isn't already the same
   color as. */
.cs-controls {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  flex-wrap: wrap;
  gap: 0.5rem;
  padding: 0.5rem 0.75rem;
  border-top: 1px solid var(--border);
  background: var(--cream-d);
}
.cs-controls[hidden] { display: none; }

/* The episode drawer's band carries a second control at its left end, linking
   out to the show's full catalogue. The two are pinned to opposite ends by the
   sort's own margin (auto margins resolve before justify-content, so flex-end
   above still puts a lone sort on the right when there is no link beside it),
   and the band wraps rather than overflowing: two pills plus their labels do not
   fit one line on a narrow phone, and the sort dropping beneath is better than
   either of them being squeezed. */
.cs-controls .pcast-sort { margin-left: auto; }

/* Deliberately the same pill as .pcast-sort-btn beside it — they are two
   controls on one band, and a link styled as a link would read as a stray piece
   of body text sitting in a toolbar. The arrow is the only difference, and it
   carries the one thing the sort doesn't do: leave the site. */
.cs-allitems {
  display: inline-flex;
  align-items: center;
  gap: 0.3rem;
  flex-shrink: 0;
  padding: 0.3rem 0.7rem;
  font: 600 0.78rem/1 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: var(--text);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 999px;
  text-decoration: none;
  white-space: nowrap;
  transition: border-color 0.12s, background 0.12s, color 0.12s;
}
.cs-allitems:hover { border-color: var(--accent); background: var(--tint); color: var(--accent-d); }
.cs-allitems:focus-visible { outline: 2px solid var(--brand); outline-offset: 2px; }
.cs-allitems-arrow { color: var(--muted); font-size: 0.9em; }
.cs-allitems:hover .cs-allitems-arrow { color: var(--accent-d); }

/* The row is the link plus a boost button beside it, so the border that used
   to sit on the link moves out to the row. */
.cs-row {
  display: flex;
  align-items: center;
  border-bottom: 1px solid var(--border);
}
.cs-row:last-child { border-bottom: 0; }
.cs-row:hover { background: var(--tint); }

.cs-link {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  flex: 1;
  min-width: 0;
  padding: 0.6rem 0.5rem 0.6rem 0.6rem;
  color: inherit;
  text-decoration: none;
}
.cs-link:focus-visible { outline: 2px solid var(--brand); outline-offset: -2px; }

.cs-rank {
  flex-shrink: 0;
  min-width: 1.6rem;
  text-align: right;
  font-size: 0.78rem;
  font-weight: 700;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

.cs-art {
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  border-radius: 8px;
  object-fit: cover;
  background: var(--cream-d);
}
.cs-art--blank {
  display: grid;
  place-items: center;
  font-size: 1.1rem;
}

.cs-main { min-width: 0; flex: 1; }

.cs-title,
.cs-meta { display: block; }

/* Deliberately the same size as .ep-title in the episode drawer above — these
   are two lists of the same kind in the same box, and the page reads as one
   thing only if they match. Nothing here inherits the 17px body serif. */
.cs-title {
  font-size: 0.9rem;
  font-weight: 600;
  color: var(--ink);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.cs-link:hover .cs-title { color: var(--brand-d); }

.cs-meta {
  margin-top: 0.2rem;
  font-size: 0.76rem;
  color: var(--muted);
  font-variant-numeric: tabular-nums;
}

/* The row's boost button is .ob-boost-pill, defined once in theme.css and
   shared with the Episodes, Songs, Shows and Albums cards. Only its placement
   is local: the row is a flex line, and the class's own margin-left:auto pins
   it to the right end. */
.cs-row .ob-boost-pill { margin-right: 0.6rem; }

/* The Favorites section on /booster (docs/favorites.md). Four groups on the
   community-row vocabulary: a small heading per group, then .cs-row rows with
   no rank column. The owner's rows carry the Favorite heart on the right, on
   the same terms as a community row's boost pill: a sibling of the link, never
   a child. */
.fav-groups { display: flex; flex-direction: column; gap: 0.25rem; }
.fav-group-title {
  margin: 0.9rem 0.6rem 0.25rem;
  font: 700 0.8rem/1.3 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--ink-muted, var(--navy-l));
}
/* The drawer's dropdown menu is absolutely positioned below its pill, and
   .ep-drawer clips its overflow to keep its rounded corners; on a short or
   empty list the menu had nowhere to open into (Reed, 2026-09-09). This
   drawer overflows when open, and the lid and the foot carry the corner
   radii themselves so nothing else changes. */
[data-booster-favorites] .ep-drawer[open] { overflow: visible; }
[data-booster-favorites] .ep-drawer[open] > summary { border-radius: 11px 11px 0 0; }
[data-booster-favorites] .ep-drawer[open] > :last-child { border-radius: 0 0 11px 11px; }
[data-booster-favorites] .ep-drawer[open] .cs-controls { position: relative; z-index: 2; }
.fav-groups .cs-row .cs-rank { display: none; }
.fav-groups .cs-row .ob-fav-pill { margin-right: 0.6rem; }
.fav-groups .cs-row .cs-link--external .cs-title::after { content: ' ↗'; opacity: 0.6; font-size: 0.85em; }
.fav-guid { font-family: ui-monospace, SFMono-Regular, Menlo, monospace; font-size: 0.78em; opacity: 0.7; word-break: break-all; }

/* "Boosts by <name> →" on a booster page's show row: the way into the #boosts
   filter. See renderShows in functions/booster/[npub].js, where the label, its
   16-character cap and its name-free fallback are all argued.

   Deliberately quieter than .ob-boost-pill, which shares this right edge on the
   show page's rows. That one moves sats and is the loudest thing on its row;
   this one changes what a list below shows, and a second solid pill would read
   as a second payment button. It comes up to brand on hover, where the press is
   already intended.

   ⚠️ Ships `hidden` from the server and revealed by booster-page.js — it does
   nothing without JavaScript. `[hidden]` needs saying because the rule below
   sets `display`. */
.cs-boosts-btn {
  flex-shrink: 0;
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  margin-right: 0.6rem;
  padding: 0.3rem 0.65rem;
  font: 600 0.72rem/1 -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
  color: var(--muted);
  background: var(--surface);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  transition: color 0.12s, border-color 0.12s, background 0.12s;
}
.cs-boosts-btn[hidden] { display: none; }
.cs-boosts-btn:hover { color: var(--accent-d); border-color: var(--accent); background: var(--tint); }
.cs-boosts-btn:focus-visible { outline: 2px solid var(--brand); outline-offset: 1px; }

/* ⚠️ THE LABEL GOES BELOW 34rem, AND THE ARROW CARRIES IT. The row already holds
   a rank, 44px of artwork, a title and three figures; "Boosts by btconboard #LN…"
   is ~150px on top of that, which at 375px leaves the show title nothing. The
   title is the row's actual content, so it wins.

   The breakpoint is set from the label rather than from a device: 34rem is where
   the longest capped label still leaves the title a readable column. It was 26rem
   when the button said only "Boosts".

   The name is not lost, it moves — `title` and `aria-label` carry the full,
   uncapped "Boosts by <name> to <show>" at every width. */
@media (max-width: 34rem) {
  .cs-boosts-btn { padding: 0.3rem 0.5rem; font-size: 0; gap: 0; }
  .cs-boosts-btn span { font-size: 0.85rem; }
}

/* ── Podroll ──────────────────────────────────────────────────────────────
   The two <podcast:podroll> sections: what this show recommends, and who
   recommends it. See the Podroll block in functions/show/[guid].js.

   A DELIBERATELY DIFFERENT SHAPE from everything else on the page. The rest of
   this page is rows: a 44px thumbnail, a title, a line of figures, a border
   underneath. A podroll carries no figures at all — it is one publisher's list
   of names — so a row would leave two thirds of its width empty and read as a
   ranked list that forgot its ranking. Big square artwork with the name beneath
   is the form the artwork earns when it is the only content, and it is how Bowl
   After Bowl's own roll-call page presents theirs.

   FIVE ACROSS ON DESKTOP, TWO ON A PHONE, as fixed column counts rather than
   auto-fill. The wall above uses auto-fill because a circular avatar has a
   natural size and the count can follow it; here the tile has no natural size —
   it is as wide as a fifth of the column — so the count is the design and the
   width follows from it. */

.pr-grid {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 1.35rem 1rem;
}

.pr-tile { min-width: 0; }
.pr-tile[hidden] { display: none; }

/* The whole tile is one link — artwork and title together, per the section's
   spec. Nothing else is in it, so there is no second target to compete with. */
.pr-link {
  display: block;
  color: inherit;
  text-decoration: none;
}
.pr-link:focus-visible {
  outline: 2px solid var(--brand);
  outline-offset: 4px;
  border-radius: 12px;
}

/* aspect-ratio rather than a height: the tile's width is a fraction of the
   column, so the only way to keep it square at every viewport is to derive the
   height from whatever that resolves to. object-fit covers the art that isn't
   square to begin with, which is a real slice of it. */
.pr-art {
  display: block;
  width: 100%;
  aspect-ratio: 1 / 1;
  border-radius: 12px;
  object-fit: cover;
  background: var(--cream-d);
  border: 1px solid var(--border);
  transition: transform 0.14s, border-color 0.14s, box-shadow 0.14s;
}
.pr-art--blank {
  display: grid;
  place-items: center;
  font-size: 1.8rem;
}
.pr-link:hover .pr-art {
  transform: translateY(-3px);
  border-color: var(--brand);
  box-shadow: 0 10px 22px rgba(0, 0, 0, 0.16);
}

/* Clamped to two lines, with the full string in the link's title attribute.
   Titles run to 70 characters at the maximum against a tile ~110px wide, so
   unclamped it would set six lines under one tile and pull the whole grid row
   out of alignment. Inherits the body serif at the same size as .sup-name in
   the wall above, deliberately: two grids of centered names on one page. */
.pr-title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  margin-top: 0.55rem;
  font-size: 0.82rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--ink);
  text-align: center;
  overflow-wrap: anywhere;
}
.pr-link:hover .pr-title { color: var(--brand-d); }

/* ── Feed controls ────────────────────────────────────────────────────────
   The "Sort: X ▾" pill, built by assets/js/feed-controls.js. Those rules live
   inline in index.html, which this page does not load, so they are restated
   here — the same arrangement as .nostr-mention, which show-page.css restates
   from boosts-thread.css for the same reason. KEEP THE TWO MATCHING.
   (Only the sort half is carried: this drawer has no range control.) */

.cs-controls .pcast-sort { position: relative; flex-shrink: 0; align-self: center; }
.cs-controls .pcast-sort-btn {
  display: inline-flex;
  align-items: center;
  gap: 0.25rem;
  padding: 0.3rem 0.7rem;
  font: inherit;
  font-size: 0.78rem;
  font-weight: 600;
  color: var(--text);
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 999px;
  cursor: pointer;
  white-space: nowrap;
  transition: border-color 0.12s, background 0.12s;
}
.cs-controls .pcast-sort-btn:hover { border-color: var(--accent); background: var(--tint); }
.cs-controls .pcast-sort-tag { color: var(--muted); }
.cs-controls .pcast-sort-cur { color: var(--accent-d); font-weight: 700; }
.cs-controls .pcast-sort-caret { color: var(--muted); font-size: 0.85em; }
.cs-controls .pcast-sort-menu {
  position: absolute;
  top: calc(100% + 4px);
  right: 0;
  z-index: 6;
  min-width: 175px;
  background: var(--white);
  border: 1px solid var(--border);
  border-radius: 10px;
  box-shadow: 0 10px 30px rgba(0, 0, 0, 0.18);
  padding: 0.35rem;
}
.cs-controls .pcast-sort-menu[hidden] { display: none; }
.cs-controls .pcast-sort-item {
  display: block;
  width: 100%;
  text-align: left;
  padding: 0.45rem 0.6rem;
  font: inherit;
  font-size: 0.85rem;
  font-weight: 600;
  color: var(--text);
  background: transparent;
  border: none;
  border-radius: 6px;
  cursor: pointer;
}
.cs-controls .pcast-sort-item:hover { background: var(--tint); color: var(--accent-d); }
.cs-controls .pcast-sort-item.is-active { color: var(--accent-d); background: var(--tint); }
.cs-controls .pcast-sort-item.is-active::after { content: ' ✓'; float: right; }

/* ── Responsive ───────────────────────────────────────────────────────── */

@media (max-width: 640px) {
  .show-hero-inner {
    flex-direction: column;
    align-items: flex-start;
    gap: 1rem;
  }
  .show-art img,
  .show-art-blank { width: 110px; height: 110px; }
  .show-desc { margin-top: 1.15rem; }
  .show-desc-body { font-size: 0.9rem; }
  /* ⚠️ ONE ROW, WHATEVER THE COUNT. The tiles are `repeat(auto-fit,
     minmax(7rem, 1fr))` at full width, which on a 375px phone (335px of content)
     needs 360px for three columns and falls to two — so /show and /episode broke
     2 + 1 and /booster broke 2 + 2, and the second line read as a separate row of
     figures rather than the rest of one.

     `grid-auto-flow: column` over `grid-template-columns: none` is what makes
     this count-agnostic: three tiles become three equal columns and four become
     four, with no rule naming either number. Today the maximum is four (sats,
     boosts, shows, episodes on /booster). A fifth would want looking at rather
     than squeezing in.

     The sizes below are the whole cost, and they scale continuously rather than
     stepping: at 320px the four-tile case leaves ~65px a tile, where the binding
     constraint is the LABEL — "episodes" and "boosters" are eight characters —
     not the number above it. */
  .show-stats {
    grid-template-columns: none;
    grid-auto-flow: column;
    grid-auto-columns: 1fr;
    gap: 0.4rem;
  }
  /* Grid items default to min-width:auto, which refuses to shrink below their
     content and would push the row wider than the page. */
  .show-stat { min-width: 0; padding: 0.6rem 0.25rem; }
  .show-stat dd { font-size: clamp(0.95rem, 4.4vw, 1.25rem); }
  .show-stat dt {
    font-size: clamp(0.55rem, 2.1vw, 0.72rem);
    letter-spacing: 0.05em;
  }
  /* The reserved line tracks the tile's own padding, which shrank above. The
     chip keeps a floor of 0.55rem: below that "T#2,274" stops being readable,
     and shrinking a control until it fits is what makes it look like a
     different control. */
  .show-stat--ranked { padding-top: 1.15rem; }
  /* Carries the `dd` for the same reason the base rule does: `.show-stat dd`
     two rules up sets a clamp() at (0,1,1) and would win over a lone class. */
  .show-stat dd.show-stat-rank {
    top: 0.28rem;
    right: 0.3rem;
    padding: 0.08rem 0.3rem;
    font-size: clamp(0.55rem, 2.2vw, 0.63rem);
  }
  /* The wall's phone rules moved to supporter-wall.css with the rest of it. */
  .boost-when { margin-left: 0; }
  /* "Other Shows This Community Boosts" is a long label; keep the hint
     on the same line as the last word of it rather than letting the two
     compete for a narrow row. */
  .ep-drawer > summary { padding: 0.7rem 0.8rem; gap: 0.5rem; }
  .drawer-hint { font-size: 0.62rem; letter-spacing: 0.08em; }
  .cs-art,
  .ep-art { width: 38px; height: 38px; }
  .cs-link { gap: 0.55rem; padding-left: 0.4rem; }
  .cs-row .ob-boost-pill { margin-right: 0.4rem; }

  /* Two podroll tiles across, not three: the artwork is the content here, and a
     third column on a 375px phone leaves each tile ~100px, which is a thumbnail
     rather than a cover. The title's two-line clamp holds at this width. */
  .pr-grid { grid-template-columns: repeat(2, 1fr); gap: 1.2rem 0.9rem; }
  .pr-title { font-size: 0.86rem; }
}


/* ── Dark mode ─────────────────────────────────────────────────────
   The tokens flip in theme.css. Two roles cannot follow them both
   ways: --brand-d is the dark theme's brand TEXT step (lightened), so
   the one filled button that used it as a hover fill under white
   hovers to --brand-dd, the ramp's AA fill in both themes; and
   --brand-dd's own text roles read --brand-d instead. */
:root[data-theme="dark"] .show-main .btn-boost:hover { background: var(--brand-dd); }
:root[data-theme="dark"] .show-desc-body a:hover { color: var(--brand-d); }
:root[data-theme="dark"] .ep-drawer > summary:hover { color: var(--brand-d); }
